Skip to content

Latest commit

 

History

History
990 lines (734 loc) · 15.7 KB

File metadata and controls

990 lines (734 loc) · 15.7 KB

VueJS - Intro


by Peter Cosemans


Copyright (c) 2017 Euricom nv. <style type="text/css"> /* .reveal p { text-align: left; } .reveal ul { display: block; } .reveal ol { display: block; } */ </style>

Quick Start

Easy does it.


Plain old javascript

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>My First Vue</title>
  </head>
  <body>
    <input type="text" id="input">
    <script>
      let data = {
        message: 'Hello World'
      }
      document.querySelector('#input').value = data.message
    </script>
  </body>
</html>

Launch your webpage by using serve or live-server

$ live-server
Serving "/Users/me/git/vue" at http://127.0.0.1:8080
GET /favicon.ico 404 2.100 ms - 24

VueJS

Add VueJS

<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>

Replace the script

<script>
    new Vue({
        el: '#root',
        data() {
            return {
                message: 'Hello World'
            }
        }
    })
</script>

Add a template binding

<div id="root">
    <input type="text" id="input" v-model="message">
    <p>The Value of the input is: {{message}}</p>
</div>

This gives us 2-way binding with the v-model directive and output binding with {{...}}.


Devtools

Open devtools and notice the following message:

Get it on the Chrome Web Store

You can inspect the Vue components and interact with them. See also the $vm0 (and others) variabled to inpect or change via the console.

Single root of truth: the view, the data, the console, the devtools.


Refactor with JS file

Let move the javascript to separate file: main.js

// eslint-disable-next-line
new Vue({
    el: '#root',
    data: {
        message: 'Hello World',
    },
})

And reference the main.js

<body>
    <div id="root">
        <input type="text" id="input" v-model="message">
        <p>The Value of the input is: {{message}}</p>
    </div>

    <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
    <script src="./main.js"></script>
</body>

Binding

Connect your model to the view


Value Binding

Simple binding (mustaches)

<p>The Value of the input is:
    <span>{{message}}</span>
</p>

v-text directive

<p>The Value of the input is:
    <span v-text="name"></span>
</p>

v-html directive

<div v-html="rawHtml"></div>

Using javascript expressions

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}

Computed properties

<span>new Date()</span>
<ul>
    <li v-for="item in tasks" v-if="item.completed">
        {{item.description}}
    </li>
</ul>

vs

<span>{{now}}</span>
<ul>
    <li v-for="item in incompleteTasks">
        {{item.description}}
    </li>
</ul>
data() {
    ...
},
computed: {
    now() {
        return new Date()
    }
    incompleteTasks() {
        return this.tasks.filter(task => !task.completed)
    }
}

Filters

Simple text formatting

{{ message | toUpperCase }}

<div v-text="date | formatDate"></div>
new Vue({
    // ...
    filters: {
        toUpperCase: function (value) {
            if (!value) return ''
            return value.toString().toUpperCase()
        }
    }
})

Can take arguments

    {{ message | filterA('arg1', arg2) }}

Can be chained

    {{ message | filter1 | filter2 }}

VueJS 2.0 doesn't has standard filters: 3th party package


List Binding

Create a list

data() {
    return {
        message: 'Hello World',
        names: ['Joe', 'Mary', 'Jane', 'Jack'],
    }
},

Use the v-for directive

<div id="root">
    ...
    <ul>
        <li v-for="name in names">{{name}}</li>
    </ul>
</div>

You can inpect the array in the devTools


Conditional Rendering

v-if and v-else directive

<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-show directive

<h1 v-show="ok">Hello!</h1>

v-show is using CSS to show/hide content, v-if removes the content from the dom.


Attributes Binding

Set button title

    <button title="hit me">Add</button>

Set button title at runtime: v-bind directive

data() {
    return {
        names: ['Joe', 'Mary', 'Jane', 'Jack'],
        buttonTitle: 'Click me to add a name',
    }
}
<button v-bind:title="title">Add</button>

Or shorthand syntax (mostly used)

<button :title="title">Add</button>

Attributes Binding

Use it for classes

<button :class="buttonClass">Add</button>
data() {
    return {
        buttonClass: 'active'
    }
}

Conditional Class binding

<style>
  .is-loading { background: red; }
</style>

<button :class="{ 'is-loading': isLoading }">
data() {
    return {
        ...
        isLoading: false,
    }
}

More: https://vuejs.org/v2/guide/class-and-style.html


Events

React to user input


Listening to events

<!-- `greet` is the name of a method defined below -->
<button v-on:click="greet($event)">ClickMe</button>

Or shorthand syntax (mostly used)

<button @click="greet($event)">ClickMe</button>

Event Handler

var example2 = new Vue({
    ...
    data() {
        return {
            name: 'Vue.js'
        },
    },
    // define methods under the `methods` object
    methods: {
        greet: function (event) {
            // `this` inside methods points to the Vue instance
            alert('Hello ' + this.name + '!')
            // `event` is the native DOM event
            if (event) {
                alert(event.target.tagName)
            }
        }
    }
})

Event modifier

It is a very common need to call event.preventDefault() or event.stopPropagation() inside event handlers.

methods: {
    greet: function (event) {
        event.preventDefault()
        ...
    }
}

Simplified

<a @click.prevent="greet($event)"></a>

Key modifiers:

<!-- only call vm.submit() when the keyCode is 13 -->
<input v-on:keyup.13="submit">

More: https://vuejs.org/v2/guide/events.html#Event-Modifiers


Exercise

Use a button to toggle (hide/show) a paragraph of text

  • Look for 3 solutions

Components

The power of VueJS


What are components

Re-usable UI components: like extending your html

// Extend Vue to get a reusable constructor
var MyComponent = Vue.extend({
    template: '<div>A custom component!<div>'
})

// Register the constructor with id: my-component
Vue.component('my-component', MyComponent)

Simplified

Vue.component('my-component', {
    template: 'A custom component!'
})

And use

<div id="root">
    <my-component></my-component>
</div>

Slots

Its like React Children & Angular transclude, its passes all the inner content (like inner html) to the template.

var MyComponent = Vue.extend({
    template: `
        <div>
            <h1>{{title}}</h1>
            <slot></slot>
        </div>
    `,
    data() {
        return {
            title: 'My Component'
        }
    }
})

And use it

    <div id="root">
        <my-component>
            <span> Add some text here </span>
        </my-component>
    </div>

Custom attributes: props

I want to set my title on the component

    <div id="root">
        <my-component title="My Component Title" sub-title="bla bla bla">
            <span> Add some text here </span>
        </my-component>
    </div>

In code

var MyComponent = Vue.extend({
    props: [
        'title',
        'sub-message'
    ],
    template: `
        <div>
            <h1>{{title}}</h1>
            <h3>{{subTitle}}</h3>
            <slot></slot>
        </div>
    `
})

More https://vuejs.org/v2/guide/components.html#Prop-Validation


Dynamic props

You can use v-bind (or use : syntax) on custom properties

<my-component :title="title" :sub-title="subTitle">
    <span> Add some text here </span>
</my-component>

Custom events

var MyComponent = Vue.extend({
    template: `
        <div>
            <h1>My Super Button</h1>
            <button @click="onClick()">Finish</button>
        </div>
    `,
    methods: {
        onClick() {
            this.$emit('handled', { id: 123 })
        }
    }
})
<div id="root">
    <my-component @handled="onHandled($event)">
    </my-component>
</div>

Lifecycle events

created() {
    // Instance is created
}
mounted() {
    // vm.$el is created (html in doc)
},
updated() {
    // Called after a data change
},
destroyed() {
    // Called after a Vue instance has been destroyed
}

More: https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram


Exercise

Create dismissible bootstrap alert

    <!-- default alert: warning -->
    <alert>
        Almost out of stock
    </alert>

    <!-- custom alert with event -->
    <alert type="alert" @closed="onClosed()">
        <strong>Alert!</strong> We have a problem.
    </alert>
  • Don't use jqeury or the bootstrap js library
  • Log a message to the console if the dialog is closed

Form Input Bindings

Get your user input


Basic Usage

<!-- text -->
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
<!-- checkbox -->
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<!-- radio -->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
<!-- select -->
<select v-model="selected">
    <option v-for="option in options" :value="option.value">
        {{ option.text }}
    </option>
</select>
<span>Selected: {{ selected }}</span>
For form validation see: https://github.com/logaretm/vee-validate

Form Submit

Form with bootstrap styling

<form @submit.prevent="addUser">
  <div class="form-group">
    <label for="firstName">First Name:</label>
    <input type="text" class="form-control"
           id="firstName" v-model="user.firstName">
  </div>

  ... other

  <button class="btn btn-defaut" @click.prevent="$router.go(-1)">Back</button>
  <button type="submit" class="btn btn-default">Submit</button>
</form>

App Events

Communicating between components


Communicating between components with direct access

    this.$root          // app root
    this.$parent        // parent component
    this.$children      // children components

Warning: Only use them when you know why you should.


Event Bus

In simple scenarios, you can use an empty Vue instance as a central event bus.

    var bus = new Vue();

In a component

    bus.$emit('myEvent', 123)              // same instance

And listen for it

    but.$on('myEvent', (value) => {
        ...
    })

Event Aggregator

class EventAggregator {
    constructor() {
        this.vue = new Vue()
    }

    fire(event, data = null) {
        this.vue.$emit(event, data)
    }

    listen(event, callback) {
        this.vue.$on(event, callback)
    }
}
export const eventAggregator = new EventAggregator()

Usage

import { eventAggregator } from 'eventAggregator'

// component A
methods: {
    onClick() {
        eventAggregator.fire('myEvent', { ... })
    }
}

// component B
created() {
    eventAggregator.listen('myEvent', (value) => {
        console.log('handle it: ', value)
    })
}

Directives

Extend the behavior of existing elements


Like the build in directives v-text, v-show but build yourself.

Vue.directive('focus', {
    // When the bound element is inserted into the DOM...
    inserted: function (el) {
        // Focus the element
        el.focus()
    }
})

And use it

<input v-focus>

More: https://vuejs.org/v2/guide/custom-directive.html#Hook-Functions


Exercise

  • We have the following translation service
    class TranslationService {
        constructor () {
            this.translations = {
                'Welcome to Vue': {
                    nl: 'Welkom in Vue',
                },
            }
            this.language = 'nl'
        }
        getTranslation (message) {
            const trans = this.translations[message][this.language]
            return trans || message
        }
    }
  • Create a translate directive based on a selected language
    <button @click="setLang('nl)">NL</button>
    <button @click="setLang('en)">EN</button>
    <h1 translate>Welcome to Vue</h1>

    output:
    <h1>Welkom in Vue</h1>
  • use vm.$forceUpdate() to re-render the view on language change

Exercise - Solution

See './exercise/vuejs' for a simple solution

See https://github.com/javisperez/vuetranslate for a solution.


VueJS vs Angular2

They are more common then you think


Templates

Angular2                  VueJS
---------------           ----------------
[attr]="value"            :attr="value" or v-bind:attr="value"
(click)="onClick()"       @click="onClick" or v-on:click="onClick"
{{name}}                  {{name}}
[innerHtml]="rawHtml"     v-html="rawHtml"
*ngFor="item of items"    v-for="item in items"
*ngIf="condition"         v-if="condition"
[hidden]="condition"      v-show="condition"
{{ message | uppercase }} {{ message | uppercase}}
{{ msg | upper:5 }}       {{ msg | upper 5}}
ngModel="name"            v-model="name"

Components

// Angular2
@Component({
    selector: 'counter',
    template: `<div>{{sample}}</div>`,
})
export class CounterComponent {
    @Input() name
    constructor() {
        this.name = 'sample'
    }
    ngOnInit() {
    }
    onCustomHandle() {
    }
}
// VueJS
Vue.component('counter', {
    props: ['name']
    template: `<div>{{sample}}</div>`,
    methods: {
        onCustomHandle() {
        }
    },
    data: {
        return {
        name: 'sample',
        }
    }
    created() {
    },
});

Events

Angular2

    <myComponent (myEvent)="onEvent($event)">
    @Ouput() myEvent = new EventEmitter()
    myEvent.emit(value)

VueJS

    <myComponent @myEvent="onEvent($event)">
    this.$emit('myEvent', value)

Resources

Learn

General

Advanced

Tools/Libraries

Book