Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Vue js and Dyploma

186 views

Published on

An introduction to Vue.js. Using Vue.js to build a web application Dyploma Web UI to manage containerized applications and services in Outbrain.

Published in: Software
  • Be the first to comment

Vue js and Dyploma

  1. 1. Vue.js and Dyploma Dr. Yoram Kornatzky kornatzky@gmail.com http://yoramkornatzky.com
  2. 2. What is Vue.js? • Front End JavaScript framework • Fast • Simpler than Angular or React • Fast learning curve • Originated by Evan You • 84K GitHub Stars
  3. 3. GitHub Rising Stars 2017 Front-End Frameworks
  4. 4. NPM Downloads
  5. 5. State of JavaScript 2018 28K developers attention
  6. 6. Vue.js in a Nutshell • Single Page Applications (SPA) • Components • Separate HTML, JavaScript, and CSS • Vue Router • Vuex for state management - Redux
  7. 7. The Ecosystem • CLI to generate a project • Build - Webpack (and Parcel) • Test - Vue Test Utils - Mocha and Chai • ES6 (x,y,z) => x + y + z, async/await • ESLint
  8. 8. Single File Component - List.vue <template lang="html"> ... </template> <script lang="js"> import ... export default { ... } </script> <style scoped lang=“scss"> … </style>
  9. 9. The Template <div v-if="!isLoading && !error && list.length > 0"> <Item v-for="(item, index) in list" :key="item.id" v-bind:single="item" v-bind:header-fields="headerFields" v-bind:fields="fields" v-bind:actions-icons="actionsIcons" v-bind:actions=“actions" v-bind:name-field=“nameField" v-on:action=“doAction" v-on:selectedItem="selectedItem" > </Item> </div>
  10. 10. Rendering - Virtual DOM • v-if for conditional rendering • v-for for looping over data • v-bind for transferring props • v-on for receiving events v-on:click=“doThis"
  11. 11. Binding HTML Classes <div class=“select-all” v-bind:class="{ 'no-select-all': !hasSelectAll }" > <div class="advanced" v-bind:style=“advancedStyle"> computed: { advancedStyle() { if (!this.isVertical) { return {}; } return { height: 'auto' /* `${2 * 18.95 this.advancedFields.length}vh` */ }; }, }
  12. 12. JavaScript import _ from 'lodash'; import Spinner from 'vue-simple-spinner'; import Item from '@/components/list/Item'; import Pagination from '@/components/list/Pagination';
  13. 13. Components and Props export default { name: 'list', components: { Top, Item, Pagination, Spinner }, props: ['title', ‘placeholder’],
  14. 14. Local Data data( ) { return { inSearch: false, list: [ { id: 11, name: ‘whoami’, cpu: 2, replicas: 7 … }, … ], }; },
  15. 15. Lifecycle Methods mounted() { }, activated() { this.rangeFetched = false; this.query = this.init || this.field.default || ''; },
  16. 16. Methods methods: { doAction(data) { this.allSelected({ chosen: false }); this.$emit('operation', data); }, }
  17. 17. Computed Properties <div class="artifact-information"> <detail v-bind:detail="artifact" v-bind:title="title" v-bind:name="name" v-bind:partition="partition" v-on:reload="reload" v-bind:is-loading="isLoadingSingle" v-bind:error="singleError" > </detail> </div>
  18. 18. computed: { artifact( ) { return { ...this.single, branch: this.single.extraValues.branch, java_env: this.single.extraValues.java_env, user: this.single.extraValues.user }; }, },
  19. 19. Two-Way Data Binding <input v-model="query" @focus="reset" type="text" v-bind:placeholder="field.placeholder" v-bind:disabled="isDisabled" v-bind:class="{ 'disabled': isDisabled }" > data() { return { query: '', }; },
  20. 20. Watching Data Changes watch: { query(to, from) { if (this.query.trim().length >= this.startAt) { this.fetchItems(); } } },
  21. 21. index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>dyploma-ui</title> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
  22. 22. main.js import 'babel-polyfill'; import Vue from 'vue'; import App from './App'; import router from './router'; import store from './store'; Vue.config.productionTip = false; new Vue({ el: '#app', store, router, template: '<App/>', components: { App }, });
  23. 23. App.vue <template> <div id="app"> <navigation/> </div> </template> <script> import Navigation from '@/components/Navigation'; export default { name: 'app', components: { navigation: Navigation, }, }; </script>
  24. 24. <style lang="scss" scoped> #app { margin: 0; padding: 0; width: 100%; height: 100vh; } </style>
  25. 25. Vuex Store State Deployments Service s Blocker s Artifacts Mutations Getters Component Actions Server
  26. 26. Using the Store computed: { ...mapGetters(['listError', 'isLoadingList', 'all', 'noMoreData',]), deployments() { return this.all(this.page, this.inSearch); }, }
  27. 27. Vue Router import Vue from 'vue'; import Router from 'vue-router'; Vue.use(Router); export default new Router({ routes: [ { path: ‘/', name: ‘Root', redirect: { name: 'Deployments' }, }, { path: ‘/deployments', name: ‘Deployments', component: Deployments, }, { path: ‘/deployments/deploymentInformation/:id', name: 'Deployment Information’, component: DeploymentInformation, }, { path: ‘/deployments/edit-deployment-information', name: 'Edit Deployment Information', component: EditDeploymentInformation, }, ] });
  28. 28. beforeRouteEnter(to, from, next) { next((vm) => { vm.clearDeployments(); vm.getDeployments({ page: 1 }); }); }, Call Action on Entry
  29. 29. Dyploma A system for managing containerised applications and services on top of Kubernetes in Outbrain: 1. deployments 2. artifacts 3. services 4. blockers
  30. 30. The Backend • Java Spring backend • REST API • MySQL database • Python CLI: dyploma-cli deployment create -n ob1ktemplate -e prod -v db959fffc49 -c sadc1a - - parameter service_configuration.cpu=4.0 -cm “simple commment”
  31. 31. Dyploma Web UI
  32. 32. Dyploma Web UI • BitBucket: Delivery / delivery-ui • Cloud Platform > Core Services > Delivery Team
  33. 33. Structure of App • Generic components: do not interact with store 1. List 2. Detail 3. Form • Containers: interact with store, use generic components
  34. 34. Fields Block
  35. 35. Flat Block
  36. 36. Field Block <div class=“fields-block"> <flat-block v-for="(f, index) in fields" :key="f.key" v-bind:data="data" v-bind:field="f"> </flat-block> </div>
  37. 37. The Power of Generics Detail Form component
  38. 38. Advanced Search component
  39. 39. Generic Input Form <div v-for="(f, index) in advancedFields" :key="index" class="field"> <div class="field-label" v-if="f.key != nameField">{{ f.name }}</div> <input class="field-input" v-if="simpleField(f)" v-bind:id="f.key" v-bind:value="initialFieldValue(f)" v-on:change="onChange" v-bind:disabled="f.disabled" v-bind:class="{ 'disabled': f.disabled }" > </input> <typeahead v-bind:field="f" :start-at="3" v-on:selected="typeaheadFiltered" v-if="f.typeahead && f.key != nameField" v-bind:init="initial && initial[f.full]" v-bind:is-disabled="f.disabled" v-bind:additional="initial && f.additional && initial[f.additional]" > </typeahead> </div>
  40. 40. Container - Deployments <template lang="html"> <div class="deployments"> <List v-bind:list="deployments" v-bind:title="title" v-bind:has-advanced="hasAdvanced" v-bind:has-search="hasSearch" v-bind:placeholder="placeholder" v-bind:fields="fields" v-bind:actions-icons="actionsIcons" v-bind:actions="actions" > </List> </div> </template>
  41. 41. Using the Store import { createNamespacedHelpers } from ‘vuex'; const { mapActions, mapGetters } = createNamespacedHelpers(‘deployments’); export default { methods: { ...mapActions(['getDeployments', 'clearDeployments', 'searchDeployments', ‘deleteDeployment']), }, computed: { ...mapGetters(['listError', 'isLoadingList', 'all', 'noMoreData', 'errorsList']), deployments() { return this.all(this.page, this.inSearch); }, }
  42. 42. The Styling • SASS (SCSS) • Flexbox • Grid • Vue Material • Chrome on desktop • Desktop responsiveness

×