Learning Vue - Js
Learning Vue - Js
of Contents
Introduction
1.1
Basics
1.2
1.2.1
Vue-router
1.2.2
Advanced
1.3
Vuex
1.3.1
Introduction
Author
Fabio Vedovelli
Basics
Basics
1. The simplest use case
2. Vue-router
Nothing special is going on at the moment: close to the </body> tag is a javascript include
and the instantiation of a Vue object. As the only parameter passed to the Vue() method is
an object literal containing some configuration information. In the example above the
property el attaches the Vue object to the <body> . Everything contained within body will
be available to Vue.js.
Next we need a form:
...
<body>
<form action="#">
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name">
</div>
<div>
<label for="email">E-mail</label>
<input type="email" id="email" name="email">
</div>
<div><button type="submit">Submit</button></div>
</form>
</body>
...
What is the our goal here? To keep the submit button disabled until both fields are
filled. Think for a moment: "- How would you accomplish this if working with Vanilla JS or
jQuery?" Probably you'd get a reference to both fields and read their values, right? Well, with
Vue.js it is a little bit different.
First we are going back to the Vue object instantiation to create an object with the data we
are interested in...
...
new Vue({
el: 'body',
data: {
name: '',
email: ''
}
})
...
Pay attention to the new attribute v-model included in both input fields. They contain a
direct reference to the properties contained in our data object living inside the Vue object.
This property is a directive and its purpose is to tie the data to the HTML element. By doing
this, everything typed in the field will change the data in the Vue object. The opposite is also
true and if the information is changed in the Javascript, let's say, by the return of an AJAX
call, the new value will automatically be displayed in the form field.
This handy behaviour is called two-way data binding.
We are getting there!
Now it is time to work on the button, afterall, he is our main character here. To enable the
desired behaviour we are going to use another directive: v-bind . This directive
conditionally add properties to HTML elements based on the state of our data. In this
particular case we'll make use of the property disabled :
...
<div><button type="submit" v-bind:disabled="(name != '' && email != '')">Submit</button
></div>
...
By the configuration above Vue is instructed to do the following: add the disabled attribute to
the <button> when both name and email are not empty. Thanks to the two-way data
binding, this is performed live and automatically. This is the power of Vue!.
But this is kind of dirty, don't you think so? What if we want to validate e-mail structure before
considering the form as valid for submission? Well, there's a better way to implement this
validation: computed property.
...
new Vue({
el: 'body',
data: {
name: '',
email: ''
},
computed: {
isValid: function () {
return this.name != '' && this.email != ''
}
}
})
...
...
<div><button type="submit" v-bind:disabled="!isValid">Submit</button></div>
...
Once again thanks to the reactivity nature of Vue.js every time one of the properties
mentioned in the method isValid changes the method gets executed, returning true or
false. Because this is a regular method you can perform any other validation inside it.
Below you find the complete source code. If you wanna see it in action there's a fiddle
available here: https://jsfiddle.net/vedovelli/focs85v3/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="#">
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" v-model="name">
</div>
<div>
<label for="email">E-mail</label>
<input type="email" id="email" name="email" v-model="email">
</div>
<div><button type="submit" v-bind:disabled="!isValid">Submit</button></div>
</form>
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/1.0.25/vue.min.js"></script>
<script>
new Vue({
el: 'body',
data: {
name: '',
email: ''
},
computed: {
isValid: function () {
return this.name != '' && this.email != ''
}
}
})
</script>
</body>
</html>
Vue-router
Vue-router
This article was first posted on Vue.js Brasil (http://www.vuejs-brasil.com)
The most notable characteristic of a Single Page Application is the performance. All
interface elements are in place and when your server infrastructure is well setup (the web
service that provides the data) the feeling user gets while using the application is the
application is locally installed, because of the short response time.
An essential component in a SPA is the Router which is responsible for showing/hidding one
or more interface elements depending on the URL. This is its single responsibility but it
doesn't mean it is a simple tool!
To support the article a new project is going to be created with Vue-cli, a command line tool
which allows you to bootstrap a new Vue project. See more on https://github.com/vuejs/vuecli.
After answeting to the 4 questions about the projets, just cd to the project's folder and
execute npm install . After the general installation of all project's default dependencies, it is
time to install the Vue-router (it is NOT listed as one of the project's default dependencies):
npm install --save-dev vue-router .
All files are now stored on /node_modules directory and will be available for your own
script for import
Vue-router
Open up the project in your favorite code editor and locate the file /src/main.js to see the
basic structure of a Vue object:
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: 'body',
components: { App }
})
Pay attention to App component because it is gonna be the parent of all other components
that might be created during development.
Vue-router configuration
Before we begin we need at least 2 components so we can mimic some navigation. So,
within the /src folder just create ComponentA.vue and ComponentB.vue. As their
contents just add something that visually differentiate them:
<template>
<h1>Componente A</h1>
</template>
<template>
<h1>Componente B</h1>
</template>
At this point we just import Vue-router straight from node_modules folder and make Vue
aware of it.
Vue-router
In the object passed as the only param to the method map() we assign the desired URLs to
the components to be displayed. Now we need to adapt our App.vue component so it will
display the right component and the correspondent URL is accessed:
Remove everything contained in App.vue replacing with the HTML below:
<template>
<div>
<router-view></router-view>
</div>
</template>
10
Vue-router
The special tag <router-view introduced by the Vue-router package is the placeholder for
the mapped components. You're free to add other tags close to and even encapsulate it
within a regular HTML tag.
The last step is to replace the Vue object creation for the router.start() method. This part
sometimes causes confusion because it is not clear where the Vue object is being created.
...
router.map({
'/componente-a': {
component: ComponentA
},
'/componente-b': {
component: ComponentB
},
})
router.start(App, '#container')
...
Please note that the start() method links the application's main component (the one with
the tag <router-view >) to the DOM element that needs to be observed by the Vue object.
So where is this element? Nowhere! We are going to add it to /index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>route</title>
</head>
<body>
<div id="container">
<router-view></router-view>
</div>
<script src="dist/build.js"></script>
</body>
</html>
Note the div with the id of "container". It also contain a <router-view> and this is the catch
to use a single file component (*.vue) as in the instantiator of the Vue-router. In the official
documentation you'll see you have to create a generic Vue component, but this is not at all
necessary and you can keep using the structure you're used to.
Below you find the complete main.js:
11
Vue-router
To finish this tutorial return to console and execute npm run dev and in your favorite browser
point it to http://localhost:8080/#!/component-a or http://localhost:8080/#!/component-b .
Because in a real world application the quantity of router tend to be big, it is advised to add
this router configuration on it own folder/files, making use of the module bundler to add them
together in a way that makes sense to the Vue object to consume it.
Here's the link to the official documentation for the Vue-router:
http://router.vuejs.org/en/index.html.
12
Advanced
Advanced
1. Vuex
13
Vuex
Vuex
Understanding the problem it solves
When you're building a browser based application it is not unusual to lose control over the
flow of the data it manages.
In those kinds of application data is not only what the user sends to the server but also what
controls the display of interface controls. So for instance you change a value in a component
and another one that is supposed to be visible, for any reason it is now hidden. This is what
we call side effects.
As your application grows it becomes a nightmare to deal with all the side effects that arise.
The usual way to share data across you tree of components is by using events, up and
down the tree. It is OK when you immediately capture a dispatched event but when the
event is captured higher or lower in the tree, you'll quickly get yourself wondering "where
that event comes from...".
Vuex
Official documentation here
It is the Vue implementation of Flux which in turn is the Facebook's implementation for
Single Source of Truth. The integration with Vue's reactivity system is transparent and
requires some simple configuration steps before it is ready to use.
14
Vuex
The first step is to install it using a regular npm command npm install vuex --save-dev .
Afterwards go to the file in which you've created the Vue instance, instruct Vue to use Vuex
and attach a store to it:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
new Vue({
el: 'body',
store: new Vuex.Store({})
})
Inside the Vuex.Store() method you'll have to pass an object two properties as follows:
1. The State which is a regular Javascript object containing the data you want to share
across your application;
2. The methods that change the state. They're called Mutation Methods.
As an example take the snippet of code above:
...
new Vue({
el: 'body',
store: new Vuex.Store({
state: {
user: {
name: '',
email: ''
}
},
mutations: {
SET_USER (store, obj) {
store.user = obj.user
}
}
})
})
As you might have imagined the quantity of both state properties and mutation methods will
grow larger than this, so we rely on module bundlers to setup Vuex somewhere else in our
file system and just import it here:
create the file: /src/vuex/store.js
15
Vuex
16
Vuex
<script>
export default {
props: ['some-property-here'],
vuex: {
getters: {},
actions: {}
},
data () { // << some local state
return {
whatever: ''
}
}
}
</script>
If we want to access the user property of our Vuex Store, it is just a matter of setting up a
getter...
...
vuex: {
getters: {
user: store => store.user
},
actions: {}
},
...
Vuex
...
vuex: {
getters: {
user: store => store.user
},
actions: {
setUser ({dispatch}, obj) {
dispatch('SET_USER', obj)
}
}
},
methods: {
ordinaryButtonClickHandler () {
let user = {
user: {
username: 'New username',
email: '[email protected]'
}
}
this.setUser(user) // << the action gets called after a button click
}
}
...
Pay attention to this specific part because this the most confusing part: dispatch('SET_USER',
obj) . First we receive this method dispatch() by using Destructuring Assignment, a new and
very useful feature of ES2015. Think about it as an event dispatcher with a single purpose:
invoke a mutation. You don't manually capture it anywhere as you would do with a regular
dispatched event. Thanks to Vue it goes straight to your mutations object in your Vuex
configuration and the proper method is invoked.
The name of the mutation method is the first parameter and the object the mutation will
receive is the second one. When it gets to the mutation, your store is finally changed and all
your components observing the Vuex Store will automatically be updated.
Supporting Tools
There is a powerful tool to support development with Vue.js called Vue Devtools, a Google
Chrome extension that lets you inspect your tree of components, interact with them in the
Console and interact with your Vuex Store, taking advantage of Time Travel for the data.
It shows all the invoked mutations and you can navigate through it, seeing changes in real
time.
18
Vuex
Conclusion
The official documentation states that Vuex is not suitable for all kinds of project but I
found it so simple to use that I include it in all of my projects. It feels natural.
Now that it is clear that Vuex is a centralized store for your data which makes it easier to
handle the state of your application, keep in mind you can also have a local state in all
your components. The decision to add it to Vuex Store or to keep it local is for you to
make. Do you need this piece of data anywhere else in my application? If the answer is YES
than you should add it to Vuex Store.
19