You are on page 1of 127

Practical Vue.

js
Daniel Schmitz and Daniel Pedrinha Georgii
This book is for sale at http://leanpub.com/vue

This version was published on 2016-07-13

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

© 2016 Daniel Schmitz and Daniel Pedrinha Georgii


Contents

1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1 Tech used . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Node installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Using npm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Some bits about RESTfull . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2. Meeting Vue.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1 jsFiddle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 Setting up jsFiddle for Vue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Hello World, vue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 Two way data-bind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.5 Creating a list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.6 Detecting changes to the Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6.1 Using track-by . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.6.2 Using $set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.6.3 Using $remove . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.4 Object loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.7 Events and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.7.1 Changing the event propagation . . . . . . . . . . . . . . . . . . . . . . . . 17
2.7.2 Keyboard modifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.8 Reactive design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.9 Creating a task list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.10 Vue’s life cycle events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.11 More about Data-bind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.11.1 Single Data-bind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.11.2 Data-bind with HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.11.3 Attribute data-bind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.11.4 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.12 Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.12.1 uppercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12.2 lowercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12.3 currency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12.4 pluralize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
CONTENTS

2.12.5 json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12.6 debounce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12.7 limitBy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12.8 filterBy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.12.9 orderBy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.13 Custom filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.13.1 “Two way” filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.14 Directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.14.1 Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.14.2 Modifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.15 Directive shortcuts (Shorthands) . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.16 Changing styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.17 Using the v-if conditional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.18 Showing or hiding a code block . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.19 v-if vs v-show . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.20 Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.20.1 Check-box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.20.2 Radio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.20.3 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.20.4 Input attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.21 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

3. Creating components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1 Vue-cli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.2 Creating the first project with vue-cli . . . . . . . . . . . . . . . . . . . . . . . . 36
3.3 Executing the project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.4 The project structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5 The packages.json file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.6 Components and .vue files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.7 Creating a new component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.8 Adding properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.8.1 camelCase vs. kebab-case . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.8.2 Validations and default values . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.9 Slots and component composing . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.9.1 Using multiple slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.10 Events and communication between components . . . . . . . . . . . . . . . . . . 48
3.10.1 Passing parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.11 Reorganizing the project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.12 Adding styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.13 Changing the Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.14 Changing the Footer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.15 Changing the App Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
CONTENTS

4. Vue Router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.1 Installing the Vue Router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.2 Setting up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.3 Setting up the router.map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.4 Setting up the router-view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.5 Creating new components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.6 Creating a menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.6.1 Passing parameters on the link . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.7 Active class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.8 Filtering routes by login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

5. Vue Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.1 Testing the Ajax access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.2 Request methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.3 Working with resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

6. Creating a blog with Vue, Express and MongoDB . . . . . . . . . . . . . . . . . . . . . 75


6.1 Creating the RESTful server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.2 The MongoDB database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.3 Creating the project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.4 Project structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.5 Setting up the MongoDB models. . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.6 Setting up the Express server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.7 Testing the server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.8 Testing the API without Vue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

7. Implementing the Blog with Vue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93


7.1 Setting up the packages.json file . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.2 Installing Vue and Materialize package . . . . . . . . . . . . . . . . . . . . . . . . 93
7.3 Setting up the router and resources . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.4 Setting up the main app interface . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
7.5 Getting Posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
7.6 Setting up the Vue Validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
7.7 Logging-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
7.8 Authentication Token . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.9 Creating Post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.10 Log-out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.11 Refactoring the home page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

8. Mixins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
8.1 Creating mixins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
8.2 Conflicting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

9. Plug-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
CONTENTS

9.1 Creating a plug-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

10. Next Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121


10.1 Vuex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
10.2 Vue.js in Twitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
10.3 Vue Awesome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
CONTENTS 1

About piracy
This work is not free and should not be published without authorization, specially in sites like scrib.
Please contribute to the authors so they can invest in more quality content.
If you got this ebook for free, you can read it, but if you like it we ask you to buy it and help us to
publish more books.
Thanks!!
CONTENTS 2

New versions
A book about programing should always be updated, granting at least one year of support. You will
get an e-mail every time this book is updated so you can download the updated version for free.
CONTENTS 3

Support
For support, open an issue on github:
https://github.com/danielschmitz/vue-codes/issues
You can also suggest new chapters for this ebook.
Source code
All examples of this work are on github:
https://github.com/danielschmitz/vue-codes
1. Introduction
Welcome to the Vue world! A framework based on reactive components, used specially for web
interfaces creation. Vue.js was created to be simple, reactive, based on components and compact.
Here we will focus on learning with practical examples, in which you will learn the basic concepts
of the framework and start a more complex application development.

1.1 Tech used


We will be using the following technologies:

Node
If you are a Java-script developer, you already know Node. If not, Node can be explained as a
way to execute Java-script on the back-end (server side). Thanks to Node, Java-script became
widely used by developers. Or would it be thanks to Java-script that Node exists? We will
leave you to decide.
npm
The node package manager is Node’s package manager. With it we can install java-script/css
libraries without the need to download files and install ourselves. With npm we can have a
base application ready in seconds. We will use it a lot here. If you are not used to it, don’t
worry, with the examples here you will get a pretty good base on this technology.
Text editor
You can use any text editor to write your Vue code. It’s recommended to use a light editor
with support to Vue, like:

• Sublime Text 3
• Visual Studio Code
• Atom

All those have plug-ins to support Vue. Here, we will use Visual Studio Code.

Web server
In our most complex examples will be necessary to communicate with a server to execute some
database operations. Some operations can’t be executed on the client side Java-script. Here we
will use Node with Express server to create a simple structured blog. Another example will be
approached with Apache, PHP and MySQL, in which we will create a simple social network.

4
Introduction 5

Browserify
This utility is a “module bundler” capable of grouping java-script files in one, allowing us to
split the applications into small separate packages that will be grouped only when necessary.
Material Design e materialize-css
Material Design is a layout concept created by Google, used in his apps like Gmail, Imbox, Plus
and so on. A design pattern that can be used to create applications. And since web systems
use style sheets (CSS), we will use materialize-css, that uses the material design concept.
Postman
To be able to test the REST requests, we will use Postman, a plug-in for Google Chrome that
make requests to the server. Postman will simulate requests that any client would do, allowing
a back-end developer to work without access to the front-end.

1.2 Node installation


Node and npm are two technologies that you should know. If you still didn’t have the opportunity
to use them, this is the moment. In this ebook we won’t install java-script frameworks without npm.
To install node/npm in Linux, type the following command:

sudo apt-get install git node npm


sudo ln -s /usr/bin/nodejs /usr/bin/node

To install Node on Windows, access the official website¹ and download the stable version. Make
sure to select “npm package manager” to install npm too.

1.3 Using npm


With npm we will install almost all necessary tools for the development. To understand better how
npm works, here are some basic commands to use on the command line (Linux and Windows).

npm init
This command starts npm on the current directory. It means the package.json file will be
created with information about the project as its name, project version, property among others.
It will also store frameworks and libraries added to the project.
npm install or npm i
Installs a library registered on the npm base, that can be accessed here². The npm i command
has the same effect. When a library is added, a directory called node_modules is created and
usually the library will be stored in node_modules/libraryname. For example, to install Vue,
execute the command npm i vue and notice that the directory node_modules/vue is created.
¹https://nodejs.org/en/
²https://www.npmjs.com/
Introduction 6

npm i –save or npm i -S


--save or -S tells npm to add the package, to be installed, to the package.json configuration
file too. It will be referred on the dependencies item.

npm i -saveDev or npm i -D


Has a similar behavior to the above item, but the installed package will be referred on the
devDependencies item. Use -D to packages that won’t be part of the project, but will be
necessary to the development, as unit tests, automation tasks, virtual servers and so on.

npm i -g
Installs the package globally to the system, being available to any project. For example, the
live-server is a small web server that emulates the present directory as a web directory
with an access address like http://localhost:8080. As the live-server is widely used in
java-script projects, it’s usually installed with the command npm i -g live-server to use it
in any project.

1.4 Some bits about RESTfull


With the evolution of web systems, the so called web-services are being slowly replaced by RESTful,
which is almost the same thing, but with an easier concept. We won’t focus on the concepts but
on practical examples, so we will use the SLim Framework to create a REST API to make HTTP
requests and get results in an easier format than XML, the JSON.
The following image shows why RESTful. With it (and Slim) we can provide a data service to any
kind of application (web, desktop or mobile).
Introduction 7

In this image we have the full cycle of a RESTful application. In ‘1’ we have a client making a HTTP
request to the server. This works the same way as a ‘normal’ HTTP request where a website requests
information from a host.
When the server gets the request, it identifies which API to call and execute, and respond to the
client in a known format, like JSON.
This way the client and the server don’t need to know each other, but can exchange information.
This way, it doesn’t matter if the client is a web browser or a mobile application, or if the server is
PHP or Java, the communication is the same.
2. Meeting Vue.js
In this chapter we will learn some basic concepts about Vue. We won’t install it yet, so it’s better to
use an on-line editor like jsFiddle.

2.1 jsFiddle
jsFiddle is an on-line html/java-script/css editor used a lot by students, quick problem resolutions
and small tests. It’s better to use it for now than create a project just to learn the following concepts.
Access jsfiddle.net¹ in your browser and you should see the following screen:

If you want, you can create an account to save the created codes for future consulting. In jsFiddle
we have 4 areas: html, java-script, css and result. When we click on the Run button, the code will be
compiled and the result will be presented.
¹jsfiddle.net

8
Meeting Vue.js 9

2.2 Setting up jsFiddle for Vue


To use jsFiddle with Vue, click on the java-script settings icon:

On Frameworks & Extensions selection box, find Vue and pick the version Vue 1.0.12, as on the
following screen:
Meeting Vue.js 10

With jsFiddle set up, we can start with Vue.

2.3 Hello World, vue


To write a Hello World with Vue we use the concept of data-bind. It means that a Vue variable will
be bound to one or more html variables.
Add the following html code:

<div id="app">
{{msg}}
</div>

Notice two things. First we created a div with id="app". And we used {{ and }} to add a variable
called msg. This variable will be filled by Vue.
Now to the Java-script. To work with Vue, all we need is to create a Vue object and pass a few
parameters:
Meeting Vue.js 11

new Vue({
el: '#app',
data: {
msg: 'Hello World'
}
})

The new Vue() object parameters are in JSON format. The el parameter declares the html element
to be referred on the html document. In this case with #app we are pointing to the app div.
The data property is a special Vue parameter where all the variables from the Vue object are stored.
Those variables can be used by the Vue object itself or for data-binding. In this case, the data.msg
variable will be bound to the {{msg}} in the html code
Now click Run button to see a result like the following:

2.4 Two way data-bind


The “two-way” data-binding allows Vue to observe a variable and update its value when changed
on the html. On the following example we create an example to change the msg variable value.
Meeting Vue.js 12

<div class="app">
{{msg}}
<input v-model="msg"/>
</div>

Notice that the input element uses a v-model property from Vue. It allows Vue to observe this field
and update the msg variable when the input value changes, as on the following example:

2.5 Creating a list


There are many Vue commands that we will learn in this book, one of them is the v-for that loops
an element. Create a new html file with the following code:
Meeting Vue.js 13

<div id="app">
<ul>
<li v-for="fruit in fruits">
{{fruit.name}}
</li>
</ul>
</div>

The v-for property on the <li> element will make it repeat according to the elements of the fruits
array from the following Vue object:

new Vue({
el:'#app',
data:{
fruits:[
{name:'apple'},
{name:'banana'},
{name:'orange'}
]
}
})

The v-for loop has an special property called $index to return the element index.
Meeting Vue.js 14

Since Vue v1.0.17 it’s possible to use item of items instead of item in items to better suit the
java-script iterations.

2.6 Detecting changes to the Array


Vue can observe changes to the Arrays reflecting them to the html. The following mutation methods
are observed by Vue:

push()
Adds an item to the Array

pop()
Removes the last element of the Array

shift()
Removes the first element of the Array

unshift()
Adds new items to the beginning of the Array

splice()
Adds items to the Array at a specific index

sort()
Sorts the Array

reverse()
Reverts the Array items

There are methods that Vue can’t observe, called non-mutating methods like filter(), concat() e
slice(). Those methods don’t change the original array, but instead return a new Array.

If we use filter, a new array will be returned according to the filter expression used. You can use it
to replace the array with the new filtered one. And don’t worry, Vue won’t recreate the entire DOM
(Document Object Model). It will reuse as much as possible of your DOM elements.
Meeting Vue.js 15

2.6.1 Using track-by


Imagine that we have a table with many records in a web page from an Ajax request to the server
and also a filter to make a new Ajax request that will return a new array of items to update the table.
Notice that for each result a new list is generated and updated on the table, which can become a
heavy operation to the DOM, sometimes replacing all the items, in case the v-for can’t recognize
that some of the items are the same as before.
It’s possible to help Vue with the track-id property. This property tells Vue which list property
should be mapped so it can keep a relation between the old and new values after each request.
Imagine a list of objects with the _uid property as following:

{
items: [
{ _uid: '88f869d', ... },
{ _uid: '7496c10', ... }
]
}

In this case we can tell v-for to use this property as a list base:

<div v-for="item in items" track-by="_uid">

</div>

This way, when Vue gets new results from the server, it can track the ones with the same _uid and
keep their DOM.

2.6.2 Using $set


Because of a Java-script limitation, Vue can’t notice changes to an array item on the following way:

this.fruits[0] = {}

The previous code will change the value of the first item in the array, but Vue won’t notice it and
update the DOM. You can verify this behavior here².
To avoid this problem it’s recommended to use a special method from Vue called $set:

²https://jsfiddle.net/danielschmitz/fpzc8x2b/
Meeting Vue.js 16

this.fruits.$set(0,{name:'foo'});

This way Vue will update the HTML list. Click here³ to observe the result.

2.6.3 Using $remove


Besides $set there’s also the $remove method to remove an item from the array and update the
HTML.

2.6.4 Object loops


It’s possible to loop between the properties of an object, where $key is the property name and value
its value:

<ul id="app" >


<li v-for="value in object">
{{ $key }} : {{ value }}
</li>
</ul>

2.7 Events and methods


We can capture many kinds of events and work with them. In the next example we have a button
that, when clicked, will change a Vue property.

<div id="app">
{{msg}}
<button v-on:click="onClick">Click me!</button>
</div>

On the button element we capture the click event with vue. It will execute the onClick method
declared on the Vue object as following:

³https://jsfiddle.net/danielschmitz/fpzc8x2b/3/
Meeting Vue.js 17

new Vue({
el:'#app',
data:{
msg:"Hello World!"
},
methods: {
onClick: function(){
this.msg="Winter is coming";
}
}
});

The Vue object has a new property called methods that groups all the referred methods on the HTML.
In this case, the onClick method changes the msg variable value.

2.7.1 Changing the event propagation


When we click on the previous button, the event will trigger the v-on:click method but will also
propagate to until the browser captures it. This is the natural event behavior.
But some times it’s necessary to capture the click event but without propagating. In this situation
we call the preventDefault or stopPropagation methods as on the following example:
Meeting Vue.js 18

<button v-on:click="say('hello!', $event)">


Submit</button>

and:

methods: {
say: function (msg, event) {
event.preventDefault()
alert(msg)
}
}

Vue allows us to cancel the event propagation on the HTML code, without the need to code it in
java-script:

<!-- event propagation will be canceled -->


<a v-on:click.stop="doThis"></a>

<!-- submit event won't reload the page -->


<form v-on:submit.prevent="onSubmit"></form>

<!-- modifiers can be concatenated -->


<a v-on:click.stop.prevent="doThat">

2.7.2 Keyboard modifiers


It’s possible to use the modifier v-on:keyup.13 to capture the “keyup 13” event from the keyboard
(Enter key) or as follows:

<input v-on:keyup.enter="submit">
or
<input @keyup.enter="submit">

Some keys have names:

• enter
• tab
• delete
• esc
• space
• up
• down
• left
• right
Meeting Vue.js 19

2.8 Reactive design


One of the main concepts of Vue is the reactive design, where the page elements are changed
accordingly to the Vue objects state. In other words, it’s not necessary to go through the element’s
DOM to change their values.

2.9 Creating a task list


With the three concepts learned so far we can create a small task list using:

• Storing variables on the Vue object and using them on the HTML.
• Changing variable values with two-way data binding.
• Capturing events and calling Vue methods.

Let’s create an input field for the task name, buttons to add and remove tasks and show a list of
created tasks.

<div id="app" class="container">

<div class="row">
<div class="col-xs-8">
<input type="text" class="form-control" placeholder="Add a task" v-model="inpu\
tTask">
</div>
<div class="col-xs-4">
<button class="btn btn-success" v-on:click="addTask">
Add
</button>
</div>
</div>

<br/>

<div class="row">
<div class="col-xs-10">
<table class="table">
<thead>
<tr>
<th>Task Name</th>
<th></th>
</tr>
</thead>
Meeting Vue.js 20

<tbody>
<tr v-for="task in tasks">
<td class="col-xs-11">
{{task.name}}
</td>
<td class="col-xs-1">
<button class="btn btn-danger" v-on:click="removeTask(task.id)\
">
x</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

Line break on the source code


Careful with the line break when copying codes from this ebook.

When copying code from this ebook, a line break due to lack of room on the screen adds a backslash
that should be removed.

Notice the use of classes on the page elements. For instance, the first <div> has the container class
and the input element has the form-control class. Those classes are responsible for the page styles
and need some css framework to work. Here we will use Bootstrap, that can be added to jsFiddle as
following:
Meeting Vue.js 21

The address to the added file is:


https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css
Back to the HTML, we created a text box that has as v-model file, the inputTask value. Then we
added a button to call the addTask method. The element list is created by the table element and we
used v-for to create a loop on the tasks array.
The loop fills the table lines where we pass the name property and used the id to remove the task.
Notice that the button to remove a task passes the task id to the method removeTask(id).
Let’s create the Vue object with the inputTask and tasks variables and addTask and removeTask
methods.

new Vue({
el:'#app',
data:{
tasks:[
{id:1,name:"Learn Vue"},
{id:2,name:"Learn Npm"},
{id:3,name:"Learn Sass"}
],
inputTask:""
},
methods:{
addTask(){
if (this.inputTask.trim()!=""){
this.tasks.push(
{name:this.inputTask,
id:this.tasks.length+1}
)
this.inputTask="";
}
},
removeTask(id){
Meeting Vue.js 22

for(var i = this.tasks.length; i--;) {


if(this.tasks[i].id === id) {
this.tasks.splice(i, 1);
}
}
}
}
})

The Vue code is easy to understand. First we created the two variables. tasks is an array of objects
that will be the base list for the table. inputTask uses two-way data-binding with the form text box
for the task text.
The addTask() method will add a new task to the task list this.tasks. Here, just to make it easier,
we used the task list length + 1 for the new item id, but this should not be used in a real application.
The removeTask(id) method expects a parameter to remove the right item from the task list.
The result can be observed on the following image. Notice that it is using the Bootstrap style sheets.

You can access this code in jsFiddle [here][https://jsfiddle.net/danielschmitz/vcpmk4rh/].

2.10 Vue’s life cycle events


When a Vue object is instantiated it has a full life cycle described on the following image:
Meeting Vue.js 23

In red, we have the events dispatched during the life cycle. The events that can be captured are:
created, beforeCompile, compiled, ready, beforeDestroy and destroy.
To execute Ajax requests, we usually use the ready event. We will come back to this later while
talking about vue-resource.

2.11 More about Data-bind


There are a few more details about data-bind.

2.11.1 Single Data-bind


If you need to apply the data-bind only the first time Vue is initiated, you can use the “*” prefix as
following:
Meeting Vue.js 24

{{*msg}}

This will load the variable value to the HTML element, but won’t keep it bound. In other words,
changes to the variable after the page loads won’t be reflected to the HTML element.

2.11.2 Data-bind with HTML


Using {{ and }} doesn’t accept HTML tags due to a security matter. It means that if your msg variable
has the value <b>Hello World</b>, the HTML element with {{msg}} will show &lt;b&gt;Hello
World&lt;/b&gt;.

To use HTML formating with data-bind it’s necessary to use {{{ and }}}. This way a value like
<b>Hello World</b> will be shown in bold format.

2.11.3 Attribute data-bind


It’s possible to use data-bind for attributes like: <div id="item-{{ id }}"></div>

2.11.4 Expressions
It’s possible to use expressions inside the data-bind as follows:

{{ number + 1 }}
If number is a variable with a value in the Vue object, it will add 1 to it’s value before displaying.

{{ ok ? 'YES' : 'NO' }}
If ok is a boolean variable it will display YES, if true and NO if false.

{{ message.split('').reverse().join('') }}
Here, the content of the message variable will be split in an array of characters split(''). The
reverse() method will revert the indexes of the array and the join method will concatenate
the characters back together. The result will be a string with the characters inverted.

But be careful, the data-bind doesn’t accept sentences, only expressions. Things like {{ var a = 1}}
or {{ if (ok) {return message} }} won’t work.

2.12 Filters
Filters are used, most of the times, to format values from the data-bind. The next example will
capitalize the first character of the msg string.
Meeting Vue.js 25

<div>
{{ msg | capitalize }}
</div>

The filter must be after the pipe | and it’s possible to concatenate more than one filter in one expres-
sion like: <li v-for="fruit in fruits | filterBy filterValue in 'name' | limitBy 3">.
Capitalize is just one of the available filters, here are a few more:

2.12.1 uppercase
Converts all characters to upper case.

2.12.2 lowercase
Converts all characters to lower case.

2.12.3 currency
Converts a value to a currency including the desired currency like:

{{ total | currency '$' }}

2.12.4 pluralize
Converts an item to its plural form according to the filter value. On the last example, if value is 1
the result will be “1 item”. If value is 2 or more, the result will be, for example, “2 items”.

<div>
{{ value | pluralize 'item' }}
</div>

2.12.5 json
Converts an object to JSON format returning a string.

2.12.6 debounce
Limited to directives, it adds a delay in milliseconds to the event in question. For example <input
@keyup="onKeyup | debounce 500">.

2.12.7 limitBy
Limited to directives, it’s used to limit the values of a v-for, usually being used for pagination. The
following example will show only the first 10 items:
Meeting Vue.js 26

<div v-for="item in items | limitBy 10"></div>

And the next one, shows the items between 5 and 15:

<div v-for="item in items | limitBy 10 5"></div>

2.12.8 filterBy
Limited to directives, it does a filter loop, specially for v-for. See the examples:

<div v-for="item in items | filterBy 'hello'">

In the previous example, only the values that contain hello will be shown. But usually the items
array is an array of objects, not strings, so in order to filter a property of an item we can do on the
following way:

<div v-for="user in users | filterBy 'Jack' in 'name'">

On the next example, we create a fruit list and use filters to: 1) capitalize the items and 2) filter
accordingly to the text typed on the text box:

<div id="app">
<input v-model="filterValue"/>
<ul>
<li v-for="fruit in fruits | filterBy filterValue in 'name'">
{{fruit.name | capitalize}}
</li>
</ul>
</div>

and
Meeting Vue.js 27

new Vue({
el:'#app',
data:{
fruits:[
{name:'apple'},
{name:'banana'},
{name:'orange'},
{name:'lemon'},
{name:'grape'},
{name:'rapberry '},
{name:'coconut '},
{name:'pear '}
],
filterValue:''
}
})

This example can be accessed in jsFiddle here⁴.


If you need to filter more than one property, its possible to do it like the following example:

<li v-for="user in users | filterBy searchText in 'name' 'phone'"></li>

2.12.9 orderBy
Limited to directives, is used to order items in an array.

<ul>
<li v-for="user in users | orderBy 'name'">
{{ user.name }}
</li>
</ul>

To order in a decreasing order:

<ul>
<li v-for="user in users | orderBy 'name' -1">
{{ user.name }}
</li>
</ul>

It’s possible to order based on two or more values:


⁴https://jsfiddle.net/danielschmitz/dy765x76/
Meeting Vue.js 28

<ul>
<li v-for="user in users | orderBy 'lastName'
'firstName'">
{{ user.lastName }}, {{ user.firstName }}
</li>
</ul>

2.13 Custom filters


It’s possible to create custom filters using the Vue.filter command. The next example creates a
filter that will invert the string characters position.

Vue.filter('reverse', function (value) {


return value.split('').reverse().join('')
})

With the filter created, it’s can be used like this:

<span v-text="message | reverse"></span>

2.13.1 “Two way” filters


It’s possible to create custom filters when showing values on the screen and when updating the
v-model. One example would be the currency formating, that when shown on the screen has the
currency character(s) and on the model has a float value. Check out the next example:

Vue.filter('currencyDisplay', {
read: function(val) {
return '$'+val.toFixed(2)
},
write: function(val, oldVal) {
var number = +val.replace(/[^\d.]/g, '')
return isNaN(number) ? 0 : parseFloat(number.toFixed(2))
}
})

2.14 Directives
Vue directives are special properties of the HTML elements. Usually they start with v- and have
many functions. In the following example we have the directive v-if:
Meeting Vue.js 29

<p v-if="greeting">Hello!</p>

The <p> element will be visible to the user only if the greeting property is true.
For a full list of directives, you can always consult the api⁵. We will show the most used ones one
the next chapters.

2.14.1 Arguments
Some directives have arguments that should be used after “:”. The next example uses the v-bind
directive to update the href attribute of the HTML element:

<a v-bind:href="url"></a>

It's the same as:

<a href="{{url}}"></a>

Another directive example is the v-on used to listen to events. As examples, the click event can be
listened with v-on:click and the Enter key press with v-on:keyup.enter.

2.14.2 Modifiers
A modifier is an element that sets up the directive argument. The previous example v-on:keyup.enter
has the “enter” modifier.

2.15 Directive shortcuts (Shorthands)


There are some shorthands widely used in Vue that shortens the HTML code. For example, instead
of:

<input v-bind:type="form.type"
v-bind:placeholder="form.placeholder"
v-bind:size="form.size">

We can use the shorthand:

⁵http://vuejs.org/api
Meeting Vue.js 30

<input :type="form.type"
:placeholder="form.placeholder"
:size="form.size">

We removed the v-bind: and used just :. Every time you see just the : in Vue, remember the v-bind.
Another very common shorthand is used with events, where v-on: is replaced by @.

<!-- full syntax -->


<a v-on:click="doSomething"></a>

<!-- shorthand -->


<a @click="doSomething"></a>

2.16 Changing styles


A big advantage of reactive design is the ability to change HTML styles according to a variable state
or specific situation.
In the task-list example, the addTask method had the following java-script code:

addTask(){
if (this.inputTask.trim()!=""){
//...............
}
},

With reactive design it’s possible to change the button for this task to:

<button class="btn btn-success"


:class="{'disabled':inputTask.trim()==''}"
@click="addTask"
>

Try this here⁶


See that we added the shorthand :class including the disabled class from bootstrap, in a way that
the button will be disabled if the text box is empty.
⁶https://jsfiddle.net/danielschmitz/0wbqvw3v/
Meeting Vue.js 31

2.17 Using the v-if conditional


This conditional will show the HTML element according to a condition. For example:

<h1 v-if="showHelloWorld">Hello World</h1>

It’s possible to add v-else right after the v-if:

<h1 v-if="name==''">Hello World</h1>


<h1 v-else>Hello {{name}}</h1>

The v-else conditional must be right after the v-if to work.


The v-if conditional will add or remove the HTML element from the DOM. If you need to just hide
the element (with display:none), you can use v-show.

2.18 Showing or hiding a code block


If you need to show a code block, you can use v-if in a HTML element that contains the code block.
If there’s not HTML parent element for the code, you can use the <template> element:
Meeting Vue.js 32

<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>

2.19 v-if vs v-show


The main difference between v-if and v-show is in the HTML DOM manipulation. With v-if
elements are added or removed from the DOM as long as the data-binds and events.
In the other hand, v-show uses styles to hide the element from the page, but not from the DOM,
which has a lower processing cost, but may be not recommended in some situations.

2.20 Forms
Forms a widely necessary in web systems.
To bind a form field to a Vue variable we use the v-model

<span>Message is: {{ message }}</span>


<br>
<input type="text" v-model="message">

2.20.1 Check-box
A check-box must be bound to a v-model boolean variable. If the same v-model is bound to more
than one check-box, an array with the selected items is created.
Example:

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">


<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames | json }}</span>
Meeting Vue.js 33

new Vue({
el: '...',
data: {
checkedNames: []
}
})

Result:

2.20.2 Radio
Radio buttons accept only one value. To create them, all you need is the v-model.

2.20.3 Select
Select fields can be bound to a v-model in a way that the selected value will change the variable
value. If the multiple option is being used, multiple values can be selected.
To create dynamic options, use the v-for on the <option> element of the select:

<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>

2.20.4 Input attributes


There are three attributes that can be used with the input element, to add extra features:

lazy Updates the model after the change event from the input field, that happens usually when the
field looses focus.

number
Formats the input field to accept only numbers.
Meeting Vue.js 34

debounce
The debounce adds a delay after the user types something, before the value is sync to the
model. This is useful when we are creating a search field and we don’t want to do the search
immediately, but instead wait for the user stop typing. The code will be executed only after
the debounce, which is calculated in milliseconds.

2.21 Conclusion
We talked about almost all Vue features and left one of the most important, Components, to the next
chapter since it requires a special attention.
3. Creating components
One of the best features of Vue is the component creation. With it we think of a web system as a
group of components interacting with each other.
When creating more complex applications we use components to separate some parts and make it
more dynamic.
For that we use some techniques addressed in this chapter.

3.1 Vue-cli
It’s a node client that creates the backbone of a complete Vue application. It’s fundamental to create
the application and understand how the components work in Vue.
To install vue-cli use the following command line:

npm install -g vue-cli

In Linux, don’t forget sudo.

After installing vue-cli, type vue and make sure you get the following result:

35
Creating components 36

3.2 Creating the first project with vue-cli


To create the first vue-cli project, type the following command:

vue init browserify-simple my-vue-app

The installer will ask for some settings. You can leave the default options for now.
Then, access the “my-vue-app” directory and type to install some necessary libraries:

npm install

3.3 Executing the project


To run the project, type:

npm run dev

This will execute two distinct tasks. First it will compile the Vue application using Browser-
ify, which is a dependency manager. Then it starts the web server, usually on the address
http://localhost:8080. Check the command return to check the right port.

With the right address, you can access your app on your browser and you should see the message
“Hello Vue!”.

You can leave the npm run dev command running. This way every time you change the code, npm
will recompile and update the browser.

3.4 The project structure


Let’s talk about the created files.

.babelrc
Contains the Java-script compilation settings for es2015.

.gitignore
Git settings file for the files to be ignored. This file is useful in case you are using git version
control.
Creating components 37

index.html
Contains the basic HTML backbone of the app. There you can see the <app></app> tag, where
all the Vue app should be rendered. There’s also the dist/build.js file, that is a minified and
compacted java-script file, with all the application. This file is regenerated after every change
to the project.

node_modules
This directory contains all the libraries installed by npm. The npm install command reads
the package.json file to download the necessary files and install in this directory.

src/main.js
It’s the java-script file that starts the application. It contains the import command that will be
read by browserify and the Vue instance that we learned how to create on the last chapter.
The components property is used to tell Vue to start the component called App.

src/App.vue
Here we have news about Vue. Its way to use components based on template, script and
style. Notice that its structure was created to behave as a component, and not an instance of
Vue anymore. The focus on this chapter will be the creation of those components.

package.json
Contains all the project settings, with its name, authority, scripts that can be executed and
dependent libraries.

3.5 The packages.json file


Let’s open the package.json file and check the “scripts” item, that should be as the following:

"scripts": {
"watchify": "watchify -vd -p browserify-hmr -e src/main.js -o dist/build.js",
"serve": "http-server -c 1 -a localhost",
"dev": "npm-run-all --parallel watchify serve",
"build": "cross-env NODE_ENV=production browserify src/main.js | uglifyjs -c warnings=\
false -m > dist/build.js"
}

Notice that there are 4 scripts ready to be executed. When we execute nm run dev we are executing
npm-run-all --parallel watchify serve. In other words, executing watchify in parallel with
serve scripts.
The watchify scripts presents us with the dependency manager browserify, that is not the focus
of this work, but we should know of its existence. It will take all the dependency libraries in
src/main.js and put every thing together in one file dist/build.js.
Creating components 38

3.6 Components and .vue files


Every Vue component file has the .vue extension and has basically three code blocks:

template
It’s the HTML template of the component. It has the same features in the previous chapter.

script
Contains the java-script code for the template and component.

style
Adds css styles to the component.

Here is the src\App.vue file:

<template>
<div id="app">
<h1>{{ msg }}</h1>
</div>
</template>

<script>
export default {
data () {
return {
msg: 'Hello Vue!'
}
}
}
</script>

<style>
body {
font-family: Helvetica, sans-serif;
}
</style>

First we use the <template> tag to create the App’s HTML, where we have a div and an h1 element.
On the <script> element we have the java-script code for the template, at first with just the data
property. The way to declare the data property is a little different than before, due to the way
browserify treats the .vue components.
Instead of:
Creating components 39

data:{
msg:"Hello World"
}

We have:

data () {
return {
msg: 'Hello Vue!'
}
}

On the <style> we can add css styles for the template.

In case your text editor/IDE isn’t formating the .vue files syntax, install the appropriate
plug-in. Usually it has Vue or Vue Syntax in its name.

3.7 Creating a new component


To create a new component all you need is to create a .vue file and define template, script and style.
Create the Menu.vue file and add the following code:

src/Menu.vue

<template>
Menu
</template>
<script>
export default{

}
</script>
<style>

</style>

This is still a simple component, with only a “Menu” text. To include this component to the app, go
back to the App.vue and add the component to the template like this:
Creating components 40

src/App.vue

<template>
<div id="app">
<menu></menu>
<h1>{{ msg }}</h1>
</div>
</template>

Notice that the Menu.vue component has the <menu> tag. This is a Vue convention. After adding
the component and with npm run dev running, reload the page on the browser and notice that the
“Menu” text isn’t still on the page.
This is because it’s necessary to execute two extra steps to load a component. First, it’s necessary to
tel Browserify to load the component and add it to the build.js file. This is done with the import.
The same way that main.js imports the App.vue component, the App.vue component must import
the Menu.vue:

src/App.vue

<template>
<div id="app">
<menu></menu>
<h1>{{ msg }}</h1>
</div>
</template>

<script>
import Menu from './Menu.vue'

export default {
data () {
return {
msg: 'Hello Vue!'
}
}
}
</script>

<style>
body {
font-family: Helvetica, sans-serif;
}
</style>
Creating components 41

By including import Menu from './Menu.vue' we are referencing the Menu component and
allowing Browserify to add it to build.js.
The second thing is to set up the App.vue component telling it to use the Menu component. In Vue,
we need to add this info in the components property:

src/App.vue

<template>
<div id="app">
<menu></menu>
<h1>{{ msg }}</h1>
</div>
</template>

<script>
import Menu from './Menu.vue'

export default {
components:{
Menu
},
data () {
return {
msg: 'Hello Vue!'
}
}
}
</script>

<style>
body {
font-family: Helvetica, sans-serif;
}
</style>

Now we can see the <menu> result on the page:


Creating components 42

3.8 Adding properties


Suppose that the <menu> component has a property called title. To set up properties on a
component, we use the props property on the component script block:

src/Menu.vue

<template>
<h4>{{title}}</h4>
</template>
<script>
export default{
props: ['title']
}
</script>

This way, the title property can be set from the App component:
Creating components 43

src/App.vue
<template>
<div id="app">
<menu title="My App"></menu>
<h1>{{ msg }}</h1>
</div>
</template>
<script>
...

Here is the result:

3.8.1 camelCase vs. kebab-case


The way that the properties are organized follows the kebab-case form. This means that a property
called myMessage should be used on the template as my-message.

3.8.2 Validations and default values


It’s possible to add validations to component properties as the following example:
Creating components 44

props: {
// number type
propA: Number,

// String or number (1.0.21+)


propM: [String, Number],

// A required property
propB: {
type: String,
required: true
},

// a number with a default value


propC: {
type: Number,
default: 100
},

// A custom validation
propF: {
validator: function (value) {
return value > 10
}
},

// Returns the JSON of the property


propH: {
coerce: function (val) {
return JSON.parse(val) // cast the value to Object
}
}

The property types can be: String, Number, Boolean, Function, Object e Array.

3.9 Slots and component composing


Vue components can be made of other components, texts, HTML code and so on. We use the
<slot></slot> tag to create the components composition. Imagine a component with the following
configuration:
Creating components 45

<menu title="My App">


<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</menu>

The inner content of the menu is its composition, and can be set up on the following way:

src/Menu.vue

<template>
<h4>{{title}}</h4>
<div>
<slot></slot>
</div>
</template>
<script>
export default{
props: ['title']
}
</script>

Notice that when we use the <slot></slot> tag, all the content from the component will be added
there. In this case, the content defined by the <ul>...</ul> tags.
Creating components 46

3.9.1 Using multiple slots


It’s possible to create multiple slots to add content to the template. For that, use the name property
in the slot and the slot property in the component. On the next example we use a footer slot to
the Menu component:

src/Menu.vue
<template>
<h4>{{title}}</h4>
<div>
<slot></slot>
</div>
<hr/>
<slot name="footer"></slot>
</template>
<script>
export default{
props: ['title']
}
</script>
Creating components 47

After setting up the Menu component, let’s use the new slot on the App component.

src/App.vue

<template>
<div id="app">
<menu title="My App">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<div slot="footer">This is a footer slot menu</div>
</menu>
<h1>{{ msg }}</h1>
</div>
</template>

Notice that we added a div to the Menu component and set it with the slot name, in this case <div
slot="footer">. The result should be like this:
Creating components 48

3.10 Events and communication between components


We saw two ways that a component can communicate with another. We can use the props attribute
and the slots that allow components to be composed by other components.
Now we will see how a component can send messages to another component, in an independent
way, with events. Each Vue components can work with four custom events types:

• Execute events with the $on() method.


• Dispatch events with the $emmit() method.
• Dispatch events that will propagate “up” with the $dispatch() method.
• Dispatch events that will propagate “down” with the $broadcast method.

Just like in the DOM, Vue events stop as soon as they find their callbacks, except when
it returns “true”.

Let’s suppose that when we click in a menu button we should notify the App that a menu item was
clicked. First we need to set up the App to listen to the Menu events:

src/App.vue

<template>
<div id="app">
<menu title="My App" v-on:button-click="onButtonClick">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<div slot="footer">This is a footer slot menu</div>
</menu>
<h1>{{ msg }}</h1>
</div>
</template>

<script>
import Menu from './Menu.vue'

export default {
components:{
Menu
},
data () {
return {
Creating components 49

msg: 'Hello Vue!'


}
},
methods:{
onButtonClick:function(e){
alert("button clicked");
}
}
}
</script>

<style>
body {
font-family: Helvetica, sans-serif;
}
</style>

In the App’s <menu> tag we have the button-click event:

v-on:button-click="onButtonClick"

That will call the onButtonClick method defined by:

methods:{
onButtonClick:function(){
alert("button clicked");
}
}

With the listener set, we can dispatch it inside the Menu:

src/Menu.vue

<template>
<h4>{{title}}</h4>
<button @click="onButtonClick">click me</button>
<div>
<slot></slot>
</div>
<hr/>
<slot name="footer"></slot>
</template>
<script>
Creating components 50

export default{
props: ['title'],
methods:{
onButtonClick : function(){
this.$dispatch('button-click');
}
}
}
</script>

We created a button that calls the onButtonClick method to dispatch the button-click event.

3.10.1 Passing parameters


Passing parameters can be done adding a second parameter to the event dispatch:

src/Menu.vue

methods:{
onButtonClick : function(){
this.$dispatch('button-click',"dispatch event from menu");
}
}

And on the App component, we can catch the parameter like this:

src/App.vue

methods:{
onButtonClick:function(message){
alert(message);
}
}

3.11 Reorganizing the project


Now that we saw some component concepts, we can reorganize the project and create some basic
features to give the app a better design.
First let’s split the application in Header, Content and Footer. We want that the App.vue component
has the next structure:
Creating components 51

src/App.vue

<template>
<div id="app">
<app-header></app-header>
<app-content></app-content>
<app-footer></app-footer>
</div>
</template>

<script>
import AppHeader from './layout/AppHeader.vue'
import AppContent from './layout/AppContent.vue'
import AppFooter from './layout/AppFooter.vue'

export default {
components:{
AppHeader,AppContent,AppFooter
}
}
</script>

Notice that we created three components: AppHeader, AppContent and AppFooter. These compo-
nents must be created on the /layout/ directory to better separate the application peaces. Here,
we will put the components that are part of the layout of the application in the layout directory.
Components that are part of forms, can be put in a directory called /form/, and components
related to reports can be put in the /report/ directory. It’s also possible to separate the components
according to the business logic of the application. For example, components related to the layout,
form and report from a “Products” table, can be put in a directory called src/app/product.
The AppHeader, AppContent and AppFooter components, at first don’t have any information but a
short text:

src/layout/AppHeader.vue

<template>
Header
</template>
<script>
export default{

}
</script>
Creating components 52

src/layout/AppContent.vue

<template>
Content
</template>
<script>
export default{

}
</script>

src/layout/AppFooter.vue

<template>
Footer
</template>
<script>
export default{

}
</script>

After refactoring the App.vue file and create the AppHeader, AppContent and AppFooter files, we
have a simple web page that looks like this:
Creating components 53

3.12 Adding styles


Now let’s add some style to our app. There are many free and paid CSS frameworks available
on the market. Some of the most known are bootstrap, semantic-ui, Foundation, UI-Kit, Pure and
Materialize. There are no better or worse ones, you can use the framework that you likes the most.
Here, we will use Materialize CSS that follows the Material Design conventions created by Google.
To add it to the project, install it with the command:

npm i -S materialize-css

Then add it to the index.html file:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>my-vue-app</title>

<!--Materialize Styles-->
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="node_modules/materialize-css/dist/css/mat\
erialize.min.css" media="screen,projection"/>

</head>
<body>
<app></app>
<!--Materialize Javascript-->
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/materialize-css/dist/js/materialize.min.js"></script>
<script src="dist/build.js"></script>
</body>
</html>

Mind the line-break on the previous HTML code, represented by backslash.

To install Materialize, it’s necessary to add two CSS styles. The Google icon and the materialize .css
file in the node_modules directory. In the <body> tag we must add the Materialize java-script files,
including the jQuery. They must be between the <app> tag and the build.js lines.
Creating components 54

As soon as Materialize is installed, notice that the font for the AppHeader, AppContent and
AppFooter texts changed, as on the next image:

To test if every thing is right, we can create the toast function of Materialize to show a notification
on the screen. Let’s add this notification to the created event of the App.vue:

src/App.vue
<template>
<div id="app">
<app-header></app-header>
<app-content></app-content>
<app-footer></app-footer>
</div>
</template>

<script>
import AppHeader from './layout/AppHeader.vue'
import AppContent from './layout/AppContent.vue'
import AppFooter from './layout/AppFooter.vue'

export default {
components:{
AppHeader,AppContent,AppFooter
},
created:function(){
Materialize.toast('Materialize Running', 1000)
}
}
</script>
Creating components 55

After reloading the page, a notification message should pop-up as on the next image:

3.13 Changing the Header


Let’s change the AppHeader to show a header on the Materialize styles. From the examples on the
official site, we can copy the following code:

<nav>
<div class="nav-wrapper">
<a href="#" class="brand-logo">Logo</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a href="sass.html">Sass</a></li>
<li><a href="badges.html">Components</a></li>
<li><a href="collapsible.html">JavaScript</a></li>
</ul>
</div>
</nav>

And add to the AppHeader template:


Creating components 56

src/layout/AppHeader.vue

<template>
<nav>
<div class="nav-wrapper">
<a href="#" class="brand-logo">Logo</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a href="sass.html">Sass</a></li>
<li><a href="badges.html">Components</a></li>
<li><a href="collapsible.html">JavaScript</a></li>
</ul>
</div>
</nav>
</template>
<script>
export default{

}
</script>

Here’s the result:


Creating components 57

3.14 Changing the Footer


The same thing can be done to the page footer, copying a style from the Materialize page and adding
to the AppFooter:

src/layout/AppFooter.vue

<template>
<footer class="page-footer">
<div class="footer-copyright">
<div class="container">
© 2014 Copyright Text
<a class="grey-text text-lighten-4 right" href="#!">More Links</a>
</div>
</div>
</footer>
</template>
<script>
export default{

}
</script>
<style>
footer {
position: fixed;
bottom: 0;
width: 100%;
}
</style>

Here we used a style to fix the footer to the bottom of the browser page:
Creating components 58

3.15 Changing the App Content


The last part to fill is related to the app’s content, where most of the screens will be created. This
means we need to manage the App’s screens and the behaviour between them.
For that, there’s a library called vue-router that we will talk about on the next chapter.
4. Vue Router
Cotinuing with the ‘my-vue-app’ project created on the previous chapter, we need to use the vue-
router, which provides a way to AppContent to manage the system screens.

4.1 Installing the Vue Router


To install the vue-router library we use npm:

npm i -S vue-router

4.2 Setting up
With the vue-router installed it’s necessary to set it up. For that, we need to change the main.js file:

src/main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
const router = new VueRouter()
router.map({
'/': {
component: ?????????????
}
});
router.start(App, 'App')

To use the vue-router we need to import it via import VueRouter from 'vue-router' and set up
Vue to use it via Vue.use(VueRouter). The router configuration is made on the router.map, that,
for now, doesn’t have a start component. We start the application with router.start to load the
App component.
There are also two things for it to work. First we need to create a component to be the root of the
application “/”. And second, we need to set up in which layout the components managed by the
router will be loaded.
To simplfy we will use the “HelloWorldRouter” component in the src/components directory with a
short text:

59
Vue Router 60

<template>
Hello World Router
</template>
<script>
export default{

}
</script>

4.3 Setting up the router.map


Now we can go back to main.js and add the relationship between the components and the router:

main.js

import Vue from 'vue'


import App from './App.vue'
import VueRouter from 'vue-router'

import HelloWorldRouter from './components/HelloWorldRouter.vue'

Vue.use(VueRouter)
const router = new VueRouter()
router.map({
'/': {
component: HelloWorldRouter
}
});
router.start(App, 'App')

4.4 Setting up the router-view


After that we need to set up where the component will be loaded.
Let’s change AppContent to allow components to be loaded in it.
Vue Router 61

src/layout/AppContent.vue

<template>
<div class="container">
<router-view></router-view>
</div>
</template>
<script>
export default{

}
</script>

Now that AppContent has a div with the container class, and the <router-view></router-view>
element, the components from the Vue router can be loaded.
Refreshing the app on the browser we have this:

4.5 Creating new components


As examples we will create two more components:
Vue Router 62

src/components/Card.vue
<template>
<div class="row">
<div class="col s12 m6">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">Card Title</span>
<p>I am a very simple card. I am good at containing small bits of informatio\
n.
I am convenient because I require little markup to be used effectively.</p>
</div>
<div class="card-action">
<a href="#">This is a link</a>
<a href="#">This is a link</a>
</div>
</div>
</div>
</div>
</template>
<script>
export default{

}
</script>

This component is from Materialize and uses CSS to draw an object that looks like a card.
The other component will have some buttons:

src/components/Buttons.vue
<template>
<a class="waves-effect waves-light btn">button</a>
<a class="waves-effect waves-light btn"><i class="material-icons left">cloud</i>button</\
a>
<a class="waves-effect waves-light btn"><i class="material-icons right">cloud</i>button<\
/a>
</template>
<script>
export default{
}
</script>

Again, we are just copying buttons from Materialize to use as example for the Vue Router.
With both components created we can set up the router mapping on the main.js file:
Vue Router 63

src/main.js

import Vue from 'vue'


import App from './App.vue'
import VueRouter from 'vue-router'

import HelloWorldRouter from './components/HelloWorldRouter.vue'


import Card from './components/Card.vue'
import Buttons from './components/Buttons.vue'

Vue.use(VueRouter)
const router = new VueRouter()
router.map({
'/': {
component: HelloWorldRouter
},
'/card': {
component: Card
},
'/buttons': {
component: Buttons
}
});
router.start(App, 'App')

We set the router to load the Card component for the address “/card” and Buttons for “/buttons”.
If you type the address http://127.0.0.1:8080/#!/buttons on your browser, you will see the
buttons, but let’s create a menu with those items.

4.6 Creating a menu


Now that we have three components managed by the router, we can create a menu with the link to
those screens. Back to LayoutHeader, let’s change the horizontal menu with this code:
Vue Router 64

src/layout/AppHeader.vue

<template>
<nav>
<div class="nav-wrapper">
<a href="#" class="brand-logo">Logo</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a v-link="{ path: '/' }">Home</a></li>
<li><a v-link="{ path: '/card' }">Card</a></li>
<li><a v-link="{ path: '/buttons' }">Buttons</a></li>
</ul>
</div>
</nav>
</template>
<script>
export default{

}
</script>

Open it on your browser and now you can click on the menu items on the top and load the right
components. This way, it’s possible to use the router to browse between the app’s components.
Vue Router 65

4.6.1 Passing parameters on the link


The next example shows how to pass parameters to the router on the link:

<a v-link="{ path: '/user/edit', params: { userId: 123 }}">Edit User</a>

This means the router will generate the link /#/user/edit?userId=123.

4.7 Active class


When we select a menu item, we can change the item’s class to indicate the user that the item is
selected. This is done in three steps: 1) Choose the class name to give the item the “selected” look. 2)
Tell the router which class is the one for the selected item. 3) Add the v-link-active to the HTML
element of the selected item.
Since we are using Materialize, the class for the job is active. We need to set it in the main.js file:

src/main.js

...
const router = new VueRouter({
linkActiveClass: 'active'
})
...

The menu items are set this way:

<template>
<nav>
<div class="nav-wrapper">
<a href="#" class="brand-logo">Logo</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li v-link-active><a v-link="{ path: '/' }">Home</a></li>
<li v-link-active><a v-link="{ path: '/card' }">Card</a></li>
<li v-link-active><a v-link="{ path: '/buttons' }">Buttons</a></li>
</ul>
</div>
</nav>
</template>
<script>
export default{

}
</script>
Vue Router 66

Notice that the v-link-active from Vue was added to the <li> element of the menu items list. Here
is the result:

4.8 Filtering routes by login


One of the router features is to redirect the routing according to parameters. Suppost that the /card
route requires the user to be logged in. For that, we can add a variable to the route mapping:

'/foo': {
component: HelloWorldRouter
},
'/card': {
component: Card,
auth: true
},
'/buttons': {
component: Buttons
}
});

Notice that the /card entry has the auth: true parameter. With that set, we need to use the
router.beforeEach method to treat the routes when the auth parameter is true. Since we still
don’t have a log-in system, let’s simulate:
Vue Router 67

router.beforeEach(function (transition) {
//SIMULA���:
let authenticated = false;
console.log(transition);
if (transition.to.auth && !authenticated) {
transition.redirect('/login')
} else {
transition.next()
}
})

Here, if auth == true and authenticated == false the router will change the route to /login (not
yet implemented).
5. Vue Resource
The Vue Resource plug-ini will help to access the web server via Ajax requests using XMLHttpRe-
quest ou JSONP.
We need npm again to add Vue Resource to the project:

npm i -S vue-resource

After installing it, we can set it up on the main.js file:

import Vue from 'vue'


import App from './App.vue'

import VueRouter from 'vue-router'


import VueResource from 'vue-resource'

import HelloWorldRouter from './components/HelloWorldRouter.vue'


import Card from './components/Card.vue'
import Buttons from './components/Buttons.vue'

Vue.use(VueResource)
Vue.use(VueRouter)
...
...

After starting the Vue Resource we can set up the root directory for the server (if needed) and the
authentication key:

Vue.http.options.root = '/root';
Vue.http.headers.common['Authorization'] = 'Basic YXBpOnBhc3N3b3Jk';

In our my-vue-app project, it won’t be necessary.

5.1 Testing the Ajax access


To simulate an Ajax request, create a file called users.json on the root of the project with the
following code

68
Vue Resource 69

[
{
"name":"User1",
"email":"user1@gmail.com",
"country":"USA"
},
{
"name":"User2",
"email":"user2@gmail.com",
"country":"Mexico"
},
{
"name":"User3",
"email":"user3@gmail.com",
"country":"France"
},
{
"name":"User4",
"email":"user4@gmail.com",
"country":"Brazil"
}
]

With the file on the root directory it’s possible to make an Ajax request to the “/users.json” URL.
Let’s do it on the Buttons component created before.
Change the Buttons.vue file with the following code:

src/components/Buttons.vue

<template>

<a @click="callUsers"
class="waves-effect waves-light btn">Call Users</a>

<a @click="countUsers"
class="waves-effect waves-light btn">
<i class="material-icons left">cloud</i>Count Users
</a>

<a class="waves-effect waves-light btn">


<i class="material-icons right">cloud</i>button
</a>

<hr/>
<pre>
Vue Resource 70

{{ users | json }}
</pre>
</template>
<script>
export default{
data() {
return{
users: null
}
},
methods: {
callUsers: function(){
this.$http({url: '/users.json', method: 'GET'})
.then(function (response) {
this.users = response.data
}, function (response) {
Materialize.toast('Erro!', 1000)
});
},
countUsers: function(){
Materialize.toast(this.users.length, 1000)
}
}
}
</script>

On the firsst component button, we added the callUsers method to use Vue Resource to make the
Ajax call:

this.$http({url: '/users.json', method: 'GET'})


.then(function (response) {
this.users = response.data
}, function (response) {
Materialize.toast('Error: ' + response.statusText, 3000)
});

This call has the URL and the GET method. On the response .then there are two parameters, being
the first one executed in case the request succeeds and the second one in case of error. If the request
succeeds we set the users variable, created on the Vue component, to response.data, that contains
an array of users.
On the template, we also create the following output:
Vue Resource 71

{{ users | json }}

This will print the this.users variable data on the screen, which at first is null, but after the Ajax
response becomes an array of objects similar to the next image:

The second button will print on the screen with Materialize.toast the ammount of registries in
the this.users variable. In this case, 4.

5.2 Request methods


On the previous example we learned how to make a GET request via Vue Response. The available
methods to make Ajax calls to the server are:

• get(url, [data], [options])


Vue Resource 72

• post(url, [data], [options])


• put(url, [data], [options])
• patch(url, [data], [options])
• delete(url, [data], [options])
• jsonp(url, [data], [options])

The options that can be passed to the server are:

Parameter Type Description


url string URL of the request
method string HTTP method (GET, POST, …)
data Object, string Data that can be sent to the server
params Object An object with the parameters that can be sent
in a GET request
headers Object HTTP header of the request
xhr Object An object with parameters XHR¹
upload Object An object that contains parameters XHR.upload²
jsonp string Callback function for a JSONP request
timeout number Request timeout (0 means no timeout)
beforeSend function Callback function that can change the HTTP
header before the request
emulateHTTP boolean SEnds the PUT, PATCH and DELETE requests as
POST requests and add the
X-HTTP-Method-Override HTTP header
emulateJSON boolean Sends the request data with the
application/x-www-form-urlencoded type

5.3 Working with resources


It’s possible to create an object of type this.$resource to provide a different way to access the
server, usually based on some specified API. To test the resource, let’s create more buttons and
analyse the HTTP requests that Vue makes on the server. At first, the HTTP calls will result in error,
but all we want is to observe how Vue works with resources.
First we create a resource in the Buttons.vue component:

¹https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
²https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/upload
Vue Resource 73

export default{
data() {
return{
users: null,
resourceUser : this.$resource('user{/id}')
}
},

Then we create a button to call each resource type. The first one is resource.get:

<template>
...
<a class="btn" @click="resourceGet">resource.get</a>
...
</template>
<script>
methods:{
...
resourceGet:function(){
this.resourceUser.get({id:1}).then(function (response) {
console.log(response)
});
}
...
</script>

Since we set up the resource with the user{/id} path, the resource.get will perfor the followin HTTP
call:
Vue Resource 74

Some other resource calls:

• save (POST)
• query (GET)
• update (PUT)
• remove (DELETE)
• delete (DELETE)

It’s also possible to create custom resourcers, as on the next example where we create the “/foo” call:

resourceUser : this.$resource('user{/id}',null,{'foo':{method:'GET',url:"/user/foo"}}

In this example, it’s possible to make the following call:

this.resourceUser.foo({id:1}).then(function (response) {
console.log(response)
});
6. Creating a blog with Vue, Express
and MongoDB
After learning the most relevant concepts of Vue, let’s create a final example of how to integrate it
to an API. The goal of this app is to create a simple blog with Posts and Users with authentication
necessary to create a Post.

6.1 Creating the RESTful server


We will use a solution 100% Node.js, with the following technologies:

• express: It’s the web server responsible to receive the web requests from the browser and
reply accordingly. We won’t use the live-server, but *express has the autoloading feature via
the nodemon library.
• body-parser: It’s a library to receive the JSON data of a POST request.
• mongoose: It’s a database adapter for MongoDB, that is a NoSql database with features almost
as good as a relational database.
• jsonwebtoken: It’s a node livrary used for authentication via web token. We will use it for
the user log-in.

All those can be installed with npm as we will see next

6.2 The MongoDB database


The MongoDB is oriented to self-contained documents (NoSql), which differs from relational
databases. In short, the data is stored in JSON format. You can install MongoDB in your computer
and use it for the project, but here we will use the https://mongolab.com/¹ service that offers a free
account for public databases.
Sign up and login to their website and on the administration screen click on the Create new button:
¹https://mongolab.com/

75
Creating a blog with Vue, Express and MongoDB 76

On the next screen pick the Single-node tab and the Sandbox plan, which is free, as on the next
image:

Give a name for the database, it can be as simple as blog and click on the Create new MongoDB
deployment button. On the next scree, with the database created, access it and check if the message
“A database user is required….” appears:
Creating a blog with Vue, Express and MongoDB 77

Click on the link and add a user name and password to access the database:
Creating a blog with Vue, Express and MongoDB 78

After that, we will use the conexion URI as indicated on your administration screen:

6.3 Creating the project


Let’s use vue-cli to create the initial project with the following command:

vue init browserify-simple blog

If the vue command is not available on the system, execute npm i -g vue-cli, as explained
before.

The blog directory will be created with the basic Vue structure. Access the directory and type:

npm i

This will install all the initial Vue packages.


To install the basic packages for the express server, execute the next command:
Creating a blog with Vue, Express and MongoDB 79

npm i -D express body-parser jsonwebtoken mongoose

6.4 Project structure


The project structure is focused on the following architecture:

blog/
|- node_modules - Node modules that support the app.
|- src - Java-script code directory for the Vue client.
|- model - Model files for the MongoDB.
|- server.js - Express web server file.
|- dist - Contains the compiled Java-script Vue file.
|- index.html - Entry-point of the app.

The src folder contains all the Vue application, taken that when we execute Browserify via npm,
the build.js file will be created on the dist directory.

6.5 Setting up the MongoDB models.


The server.js file contains every thing that a web application needs. We will explain step-by-step
what each command means. But first, let’s create the files that represent the MongoDB model:

/model/user.js

var mongoose = require('mongoose');


var Schema = mongoose.Schema;

var userSchema = new Schema({


name: String,
login: String,
password: String
});

module.exports = mongoose.model('User', userSchema);

The User model is created with the help of the mongoose library. The User model is like a table with
the fields name, login and password.
The Posts model is shown next:
Creating a blog with Vue, Express and MongoDB 80

/model/post.js

var mongoose = require('mongoose');


var Schema = mongoose.Schema;

var postSchema = new Schema({


title: String,
author: String,
body: String,
user: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
date: { type: Date, default: Date.now }
});

module.exports = mongoose.model('Post', postSchema);

The Post model uses almost the same concepts of User, except by the relationship between Post
and User, where Post has a reference to the User model.

6.6 Setting up the Express server


Create the server.js to add the necessary code for the api. We will explain step-by-step how to set
it up:

server.js

1 var express = require('express');


2 var app = express();
3 var bodyParser = require('body-parser');
4 var jwt = require('jsonwebtoken');

At first we reference the necessary libraries to use and the app variable that will contain the instance
of the express server.

server.js

5 //secret key (use any big text)


6 var secretKey = "MySuperSecretKey";

We created a variable called secretKey that will be used with the jsonwebtoken module to generate
an access token for the logged-in user. In a production server you will need to change the value of
the secretKey to another string.
Creating a blog with Vue, Express and MongoDB 81

server.js

7 //Database in the cloud


8 var mongoose = require('mongoose');
9 mongoose.connect('mongodb://USER:PASSWOD@___URL___/blog', function (err) {
10 if (err) { console.error("error! " + err) }
11 });

Here we are importing the mongoose library and connecting to the mongolab. Don’t forget to change
the USER:PASSWOD@___URL___ with the values you created before.

server.js

12 //bodyparser to read json post data


13 app.use(bodyParser.urlencoded({ extended: true }));
14 app.use(bodyParser.json());

At line 13 and 14 we set up the bodyParser with the use method of Express instance, represented
by the app variable. The bodyParser will get the data from the JSON requests and format it.

server.js

15 //Load mongodb model schema


16 var Post = require('./model/post');
17 var User = require('./model/user');

At line 16 and 17 we imported the models created previously. Those Schemas will be referenced by
the Post and User variables.

server.js

18 var router = express.Router();

At line 18 we create the router preparing the Express server to behave as an API. A router is
responsible to get the requests and execute the right code depending on the request. Usually there
are four kinds of requests:

• GET: Used to get data. Can be accessed by the browser via URL.
• POST: Used to insert save data, usually from a form.
• DELETE: :Used to delete data.
• PUT: Can be used to edit data. We won’t use PUT in this project, but you can if you want.

Besides the request type we also have the url and the parameters that we will see later.
Creating a blog with Vue, Express and MongoDB 82

server.js

19 app.use('/', express.static(__dirname+'/'));

At line 19 we set up the / directory as static, which means that its content will be treated as files
that, once requested, will be delivered to the requester.
This concept is similar to the “webroot” directory in other web servers.
We also used the __dirname variable that returns the full path to the server.js file. This is necessary
for a future deploy in a production server.

server.js

23 //middleware: run in all requests


24 router.use(function (req, res, next) {
25 console.warn(req.method + " " + req.url +
26 " with " + JSON.stringify(req.body));
27 next();
28 });

At line 24 we created the “middleware”. A peace of code that will be executed in every request that
Express receives. At line 25 we used the console.warn method to send a notification to the console
showing the method type, the URL and the JSON parameters. This information should be used only
in development environment and should be commented in production servers. The result from a the
line 24 should be similar to this:

POST /login with {"login":"foo","password":"bar"}

The JSON.stringify method gets a JSON object and returns its representation in text format.
At line 27 we used the next() method to continue the request flux.

server.js

29 //middleware: auth
30 var auth = function (req, res, next) {
31 var token = req.body.token || req.query.token
32 || req.headers['x-access-token'];
33 if (token) {
34 jwt.verify(token, secretKey, function (err, decoded) {
35 if (err) {
36 return res.status(403).send({
37 success: false,
38 message: 'Access denied'
Creating a blog with Vue, Express and MongoDB 83

39 });
40 } else {
41 req.decoded = decoded;
42 next();
43 }
44 });
45 }
46 else {
47 return res.status(403).send({
48 success: false,
49 message: 'Access denied'
50 });
51 }
52 }

At line 30 we have another middleware called auth to check if the token provided by the request is
valid. When the user log-in to the website it will receive a token that will be used in every request.
At line 31 we created the token variable gets the client token. In our case, every time we need to
pass the token to the server we will use the HTTP header x-access-token variable.
At line 34 we used the jwt.verify method to check the client token. Notice that the secretKey is
used there and that there’s a callback on the third parameter.
At line 35 we check if the callback threw any error. If yes, the passed token is invalid and in the line
36 re return the error with the 403 error code that means “Access denied” (HTTP error, just like 404
means not found).
At line 40 the token is valid, the decoded object is stored in the req.decoded variable to be used later
the the next method will make sure the request proceeds.
The line 46 is executed if the client didn’t provide a token, returning also the 403 error.

server.js

53 //simple GET / test


54 router.get('/', function (req, res) {
55 res.json({ message: 'hello world!' });
56 });

At line 54 we have an example of how the Express router works. With the router.get method we
set up the “/” URL to, when called, execute the callback on the second parameter. This callback sets
up the router reply with the res.json method returning the { message: 'hello world!' } JSON
object.
Creating a blog with Vue, Express and MongoDB 84

server.js

56 router.route('/users')
57 .get(auth, function (req, res) {
58 User.find(function (err, users) {
59 if (err)
60 res.send(err);
61 res.json(users);
62 });
63 })
64 .post(function (req, res) {
65 var user = new User();
66 user.name = req.body.name;
67 user.login = req.body.login;
68 user.password = req.body.password;
69
70 user.save(function (err) {
71 if (err)
72 res.send(err);
73 res.json(user);
74 })
75 });

At line 56 we began the user routing that will be accessed via the “/users” URL. At line 57 we set
yo a GET request to this URL adding the auth middleware created before. This means that before
executing the GET /users method callback the token will be validated. If valid, the callback will be
executed and at line 58 we use the User schema to find all the users in the database and at line 61
we return it as an array.
At line 64 we set up the POST /users method to store a user. Notice that in this method we didn’t
use the auth middleware, which means that, to execute it, it’s not necessary to be authenticated. At
line 65 we create a variable with the User schema to save the registry. The data passed by the client
to the Express server are accessed by the req.body variable that is set thanks to the body-parser.
The user.save method saves the registry to the database and the res.json method returns the user
to the client.
Creating a blog with Vue, Express and MongoDB 85

server.js

76 router.route('/login').post(function (req, res) {


77 if (req.body.isNew) {
78 User.findOne({ login: req.body.login }, 'name')
79 .exec(function (err, user) {
80 if (err) res.send(err);
81 if (user != null) {
82 res.status(400).send('Login Exists');
83 }
84 else {
85 var newUser = new User();
86 newUser.name = req.body.name;
87 newUser.login = req.body.login;
88 newUser.password = req.body.password;
89 newUser.save(function (err) {
90 if (err) res.send(err);
91 var token = jwt.sign(newUser, secretKey, {
92 expiresIn: "1 day"
93 });
94 res.json({ user: newUser, token: token });
95 });
96 }
97 });
98 } else {
99 User.findOne({ login: req.body.login,
100 password: req.body.password }, 'name')
101 .exec(function (err, user) {
102 if (err) res.send(err);
103 if (user != null) {
104 var token = jwt.sign(user, secretKey, {
105 expiresIn: "1 day"
106 });
107 res.json({ user: user, token: token });
108 }else{
109 res.status(400).send('Login/Senha incorretos');
110 }
111 });
112 }
113 });

At line 76 we have the Log-in code via the /login URL. When the client makes a POST /login
request we check if the isNew property is true to know if the user is logging-in or creating a new
entry.
Creating a blog with Vue, Express and MongoDB 86

At line 78 we use the findOne method with the {login:req.body.login} filter to check if the user
exists. The second parameter of this method are the fields that should be returned in case a user is
found. The .exec method will execute the findOne and call the callback too return an error, since
it’s not possible to create two entries for the same user.
If req.body.isNew is false, the code from line 99 is executed to look at the database for the login
and password. If a user matches with the client data, the jwt.sign method at line 103 will create the
authentication token for the user and return it at line 106. If the user is not found on the database at
line 108 an error is returned.

server.js
114 router.route('/posts/:post_id?')
115 .get(function (req, res) {
116 Post
117 .find()
118 .sort([['date', 'descending']])
119 .populate('user', 'name')
120 .exec(function (err, posts) {
121 if (err)
122 res.send(err);
123 res.json(posts);
124 });
125 })
126 .post(auth, function (req, res) {
127 var post = new Post();
128 post.title = req.body.title;
129 post.text = req.body.text;
130 post.user = req.body.user._id;
131 if (post.title==null)
132 res.status(400).send('Title can't be null');
133 post.save(function (err) {
134 if (err)
135 res.send(err);
136 res.json(post);
137 });
138 })
139 .delete(auth, function (req, res) {
140 Post.remove({
141 _id: req.params.post_id
142 }, function(err, post) {
143 if (err)
144 res.send(err);
145 res.json({ message: 'Successfully deleted' });
146 });
147 });
Creating a blog with Vue, Express and MongoDB 87

At line 114 we set the URL to get the posts. With :post_id we are adding a variable to the URL in
case we want an specific post. And by using the question mark ? we are setting this parameter as
optional.
At line 115 we start the GET /posts method to get all the posts on the database. At line 118 we use
the sort method to sorte the posts and at line 119 we used the populate method to add a reference
to the user model for the Post creator. This reference is possible because it was defined on the Post
schema.
At line 126 we create the POST /posts method to add a Post. At line 131 we validate it and use the
Post.save() to save it to the database.

At line 139 we add the DELETE /posts to remove a Post entry from the database. To do it we use the
Post.remove method passing the post id from the /posts/:post_id? URL.

server.js

148 //register router


149 app.use('/api', router);
150 //start server
151 var port = process.env.PORT || 8080;
152 app.listen(port);
153 console.log('Listen: ' + port);

Finishing the server script we point the router variable to the /api address at line 149. That way all
the api will be exposed at the /api URL. For example, to get all the Posts from the database, a GET
request must be done to the /api/posts address. At line 151 and 152 we define the port where the
Express server should be executed. And at line 153 we inform via console.log which port was used.

6.7 Testing the server


To test the web server we can execute the next command:

$ node server.js

Where we get a simple answer: “Listen: 8080”. If there’s any change to the server.js file, it won’t
be reflected to the current server execution. It must be terminated and restarted. To avoid this work,
let’s install the nodemon library to reload the server every timme the server.js file is changed.

$ npm install nodemon -g

After the instalation, execute:


Creating a blog with Vue, Express and MongoDB 88

$ nodemon server.js

[nodemon] 1.8.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node server.js`
Listen: 8080

The answer indicates that every time the server.js file is updated, the node server.js command
will be too.

6.8 Testing the API without Vue


It’s possible to test the API using a software capable of making GET/POST requests to the server.
We will use one called Postman², that can be installed as a plug-in for Google Chrome.
To test the http://127.0.0.1:8080/api/ address we set up Postman on the following way:

Notice that we get the “hello world” reply as set on the server. To create a user, we can make a POST
request to the /users URL like this:
²https://www.getpostman.com/
Creating a blog with Vue, Express and MongoDB 89

To test the log-in service, let’s try to access the /api/users URL. Since we set that this URL must
pass through the middleware the token won’t be found and an error thrown:
Creating a blog with Vue, Express and MongoDB 90

To make the log-in, we must access the /api/login URL like this:
Creating a blog with Vue, Express and MongoDB 91

Notice that by passing login and password the token is generated and returned to postman. Copy
and save the token somewhere to be able to use it on the next server calls. In Vue, this token will be
saved in a variable.
With the token it’s possible to make the GET /users call passing the token on the HTTP request
header like on the next image:
Creating a blog with Vue, Express and MongoDB 92

Notice that with the token set up, the user data is returned. Try changing a character on the token
to get the “Failed to authenticate” error.
7. Implementing the Blog with Vue
After setting up the Express server let’s get back to the Vue project.

7.1 Setting up the packages.json file


Vue-cli uses the http-server server to work as a server for our application. Since we created another
server, with express we need to change the server on the “scripts.serve” call.
Find the following line on the package.json file:

"serve": "http-server -c 1 -a localhost",

and change for:

"serve": "nodemon server.js",

Now when we execute npm run dev the express server will be ready.The Vue application will be
available at the http://localhost:8080 access and the API at http://localhost:8080/api.

7.2 Installing Vue and Materialize package


Until now we installed only the Express and MongoDB libraries. Now we need to install the Vue
ones:

npm i -S vue-router vue-resource materialize-css

7.3 Setting up the router and resources


Change the main.js file to enable the use of the vue-router and vue-resource:

93
Implementing the Blog with Vue 94

src/main.js

import Vue from 'vue'


import App from './App.vue'

import VueRouter from 'vue-router'


import VueResource from 'vue-resource'

Vue.use(VueResource)
Vue.use(VueRouter)

const router = new VueRouter({


linkActiveClass: 'active'
})

router.start(App, 'App')

Now we need to set up the routes. Instead of adding the route mapping to the main.js file, let’s create
a file called routes.js for that.

src/routes.js

const Routes = {
'/': {
component:{
template: "<b>root</b>"
}
},
'/login':{
component:{
template: "<b>login</b>"
}
},
'/logout':{
component:{
template: "<b>logout</b>"
}
},
'/addPost':{
component:{
template: "<b>addPost</b>"
}
},
'/removePost':{
component:{
Implementing the Blog with Vue 95

template: "<b>removePost</b>"
}
},
}

export default Routes;

The route file has, for now, a component with a simple template, indicating a HTML to be loaded
for the routes. We will create each component individually later.
To add the route file to the main.js we import it on the following way:

src/main.js

import Vue from 'vue'


import App from './App.vue'

import VueRouter from 'vue-router'


import VueResource from 'vue-resource'

import Routes from './routes.js'

Vue.use(VueResource)
Vue.use(VueRouter)

const router = new VueRouter({


linkActiveClass: 'active'
})

router.map(Routes)
router.start(App, 'App')

To finish the route setup let’s add <router-view></router-view> to the App component:

src/App.vue

<template>
<div id="app">
<h1>{{ msg }}</h1>
<router-view></router-view>
</div>
</template>

<script>
export default {
Implementing the Blog with Vue 96

data () {
return {
msg: 'Hello Vue!'
}
}
}
</script>

<style>
body {
font-family: Helvetica, sans-serif;
}
</style>

7.4 Setting up the main app interface


After installing Materialize with npm, we need to set it up in the index.html file. Add the css and js
file according to the next code

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>blog</title>

<!--Materialize Styles-->
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="node_modules/materialize-css/dist/css/mat\
erialize.min.css" media="screen,projection"/>
</head>
<body>
<app></app>
<!--Materialize Javascript-->
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/materialize-css/dist/js/materialize.min.js"></script>

<script src="dist/build.js"></script>
</body>
</html>

To add a header we edit the App.vue file including the following code:
Implementing the Blog with Vue 97

src/App.vue

<template>
<div id="app">
<nav>
<div class="nav-wrapper">
<a href="#" class="brand-logo">Blog</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li v-link-active><a v-link="{ path: '/' }">Home</a></li>
<li v-link-active><a v-link="{ path: '/login' }">Login</a></li>
<li v-link-active><a v-link="{ path: '/addPost' }">AddPost</a></li>
</ul>
</div>
</nav>
<br/>
<div class="container">
<router-view></router-view>
</div>
</div>
</template>

<script>
export default {
data () {
return {
msg: 'Hello Vue!'
}
}
}
</script>

<style>
body {
font-family: Helvetica, sans-serif;
}
</style>

Now, our website should look like this:


Implementing the Blog with Vue 98

7.5 Getting Posts


The Posts will be loaded in the home page. Let’s create an empty component for that:

<template>
Home
</template>
<script>
export default {

}
</script>

And set up the routes.js file to use the component:


Implementing the Blog with Vue 99

import Home from './Home.js'

const Routes = {
'/home': {
component: Home
},
'/login':{
component:{
template: "<b>login</b>"
}
}
}
....

Notice that there’s no root route “/” since the default route will be “/home”. To set up this behavior
we must add a “redirect” to the Vue router in the main.js file:

src/main.js

import Vue from 'vue'


import App from './App.vue'

import VueRouter from 'vue-router'


import VueResource from 'vue-resource'

import Routes from './routes.js'

Vue.use(VueResource)
Vue.use(VueRouter)

const router = new VueRouter({


linkActiveClass: 'active'
})

router.redirect({
'/': '/home'
})

router.map(Routes)

router.start(App, 'App')

Back to Home.vue we need to access the /api/posts link to get the existing Posts:
Implementing the Blog with Vue 100

src/Home.vue

<template>
<div v-show="showProgress" class="progress">
<div class="indeterminate"></div>
</div>
</template>
<script>
export default {
data (){
return {
posts: null,
showProgress:true
}
},
created: function(){
this.showProgress=true;
this.$http.get('/api/posts').then(function(response){
this.showProgress=false
this.posts = response.data
console.log(this.posts);
},function(error){
this.showProgress=false
Materialize.toast('Error: ' + error.statusText, 3000)
})
}
}

</script>

For now the page shows on the template a div with a progress bar, indicating that an Ajax request
is being processed. This div is controlled by the showProgress variable, initially set as false.
In the created method, executed when the component is ready, we start the the Ajax request to the
API with the this.$http.get('/api/posts') method, attributing the result to the posts variable
and shown on the browser console. Notice the use of the showProgress variable to control the
component interface.
After the posts variable is set we can create the template to show the posts on the page. With the
Materialize framework we can create a card to each Post as following:
Implementing the Blog with Vue 101

src/Home.vue

<template>
<div v-show="showProgress" class="progress">
<div class="indeterminate"></div>
</div>

<div class="row" v-for="post in posts">


<div class="col s12">
<div class="card blue lighten-5">
<div class="card-content black-text">
<span class="card-title">{{post.title}}</span>
<p>{{post.text}}</p>
</div>
<div class="card-action">
<span><i class="material-icons">perm_identity</i> {{post.user.name}}</span>
<!-- <a href="#">This is a link</a> -->
</div>
</div>
</div>
</div>

</template>
<script>
export default {
data (){
return {
posts: null,
showProgress:true
}
},
created: function(){
this.showProgress=true;
this.$http.get('/api/posts').then(function(response){
this.showProgress=false
this.posts = response.data
console.log(this.posts);
},function(error){
this.showProgress=false
Materialize.toast('Error: ' + error.statusText, 3000)
})
}
}
</script>
Implementing the Blog with Vue 102

In the <template> session we included the basic card format from here¹, including the v-for to
navigate between the Posts, showing the Posts titles, the content and the author. It should look like
this:

7.6 Setting up the Vue Validator


The Vue Validator will be used on the forms. Add the plug-in with the following command:

npm i -S vue-validator

Change the main.js file including the Vue Validator:

¹http://materializecss.com/cards.html
Implementing the Blog with Vue 103

src/main.js

import Vue from 'vue'


import App from './App.vue'

import VueRouter from 'vue-router'


import VueResource from 'vue-resource'
import VueValidator from 'vue-validator'

import Routes from './routes.js'

Vue.use(VueResource)
Vue.use(VueRouter)
Vue.use(VueValidator)

const router = new VueRouter({


linkActiveClass: 'active'
})

router.redirect({
'/': '/home'
})

router.map(Routes)

router.start(App, 'App')

7.7 Logging-in
To add a Post, the user must be logged-in. For that, first we create the Login.vue component with
the following code:

src/Login.vue

<template>

<validator name="validateForm">
<form class="col s12">

<div class="row">

<div class="input-field col s12">


<i class="material-icons prefix">account_circle</i>
<input id="login" type="text" v-model="user.login" v-validate:login="{ required:\
Implementing the Blog with Vue 104

false, minlength: 4 }" />


<label for="login">Login</label>
<div>
<span class="chip red lighten-5 right" v-if="$validateForm.login.required">Req\
uired field</span>
<span class="chip red lighten-5 right" v-if="$validateForm.login.minlength">At\
least 4 characters</span>
</div>
</div>

<div class="input-field col s12">


<i class="material-icons prefix">vpn_key</i>
<input id="password" type="password" v-model="user.password" v-validate:password\
="{ required: false, minlength: 4 }" />
<label for="password">Password</label>
<div>
<span class="chip red lighten-5 right" v-if="$validateForm.password.required">\
Required field</span>
<span class="chip red lighten-5 right" v-if="$validateForm.password.minlength"\
>At least 4 characters</span>
</div>
</div>

<div class="input-field col s12 m3">


<input type="checkbox" id="createaccount" v-model="user.isNew" />
<label for="createaccount">Create Account?</label>
</div>

<div class="input-field col s12 m9" v-show="user.isNew">


<input id="name" type="text" v-model="user.name" />
<label for="name">Your Name</label>
</div>

</div>

<div class="input-field col s12">


<button class="waves-effect waves-light btn right" @click="doLogin" v-if="$validat\
eForm.valid">Send</button>
</div>

</form>
</validator>

<div v-show="showProgress" class="progress">


<div class="indeterminate"></div>
Implementing the Blog with Vue 105

</div>

</template>
<script>
export default{
data () {
return {
user: {
name:"",
password:"",
login:"",
isNew:false
}
}
},
methods:{
doLogin:function(){

}
}
}
</script>

The log-in has three fields, the user name and password for logging-in and a name field for
registering. We used the Vue Validator to make sure they are always filled and the Send button
appears only if their values pass the validation.
The check box is used to create new users.
When the user clicks on the Send button, the following doLogin method will be executed:

src/Login.vue

....
methods:{
doLogin:function(){
this.showProgress=true;
this.$http.post('/api/login',this.user).then(function(response){
this.showProgress=false;
console.log(response);
this.$router.go("/home")
},function(error){
this.showProgress=false;
console.log(error);
Materialize.toast('Error: ' + error.data, 3000)
});
Implementing the Blog with Vue 106

}
}
....

The doLogin method will make a POST call to the /api/login URL, which will return either the
authentication token or an error message. With the authentication token it’s possible to set up that
the user is logged-in, but we still need a way to store it.

7.8 Authentication Token


When the user logs-in it’s necessary to store some information like his user name, id and auth token
to be used on the next requests. For that, let’s create an object called “Auth”.
First create the auth.js file with the following code:

src/auth.js

export default {
setLogin: function(data){
localStorage.setItem("username",data.user.name);
localStorage.setItem("userid",data.user._id);
localStorage.setItem("token",data.token);
} ,
getLogin: function(){
return {
name:localStorage.getItem("username"),
id:localStorage.getItem("userid"),
token:localStorage.getItem("token")
}
},
logout:function(){
localStorage.removeItem("username");
localStorage.removeItem("userid");
localStorage.removeItem("token");
}
}

To use this class, import it and fill the log-in information. When logging-in, this is done before
redirecting the router to /home:
Implementing the Blog with Vue 107

src/Login.vue

<script>

import Auth from "./auth.js"

export default{
data () {
return {
showProgress:false,
user: {
name:"",
password:"",
login:"",
isNew:false
}
}
},
created: function(){
console.log(Auth);
},
methods:{
doLogin:function(){
this.showProgress=true
this.$http.post('/api/login',this.user).then(function(response){

this.showProgress=false
Auth.setLogin(response.data)
this.$router.go("/home")

},function(error){
this.showProgress=false
console.log(error)
Materialize.toast('Error: ' + error.data, 3000)
});
}
}
}
</script>

This way, after the log-in we will always be able to import auth.js and use it. The log-in information
will be stored in the localStorage of the Browser.
Implementing the Blog with Vue 108

7.9 Creating Post


Let’s create the following form to create Posts:

src/AddPost.vue

<template>
<h4>Add Post</h4>
<validator name="validateForm">
<form class="col s12">

<div class="row">

<div class="input-field col s12">


<input id="login" type="text" v-model="post.title" v-validate:login="{ required:\
false, minlength: 3 }" />
<label for="login">Title</label>
<div>
<span class="chip red lighten-5 right" v-if="$validateForm.login.required">Req\
uired field</span>
<span class="chip red lighten-5 right" v-if="$validateForm.login.minlength">Mi\
nimum 4 characters</span>
</div>
</div>
<div class="input-field col s12">
<textarea id="textarea1" class="materialize-textarea" v-model="post.text"></tex\
tarea>
<label for="textarea1">Text</label>
</div>
</div>
<div class="input-field col s12">
<button class="waves-effect waves-light btn right" @click="add" v-if="$validateFor\
m.valid">Send</button>
</div>
</form>
</validator>

<div v-show="showProgress" class="progress">


<div class="indeterminate"></div>
</div>

</template>
<script>
import Auth from './Auth.js'
Implementing the Blog with Vue 109

export default{
data (){
return{
post:{
title:"",
token:"",
text:"",
user:{
_id:""
}
}
}
},
created:function(){
let login = Auth.getLogin();
if (login.token==null){
this.$router.go("/login")
}else{
this.post.user._id=login.id;
this.post.token=login.token;
}
},
methods:{
add:function(){
this.$http.post('/api/posts',this.post).then(function(response){
this.$router.go("/home");
},function(error){
//console.log(error)
Materialize.toast('Error: ' + error.data.message, 3000)
});
}
}

</script>

This component checks if the user is authenticated in the created method. If not, the router will
redirect the user to the Log-in page. The form to add a Post is similar to the log-in. Clicking on the
Send button will call the add method to execute the Ajax request to save the Post data.

To finish the Post creation we need to add the AddPost component to the routes.js file:
Implementing the Blog with Vue 110

import Home from './Home.vue'


import Login from './Login.vue'
import AddPost from './AddPost.vue'
import Logout from './Logout.vue'

const Routes = {
'/home': {
component: Home
},
'/login':{
component: Login
},
'/logout':{
component: {template:'Logout'}
},
'/addPost':{
component: AddPost
}
}

export default Routes;

7.10 Log-out
The log-out is made by calling the Logout.vue file created like that:

src/Logout.vue

<template>
Logout
</template>
<script>
import Auth from './auth.js'
export default {
data (){
return {

}
},
created:function(){
Auth.logout();
console.log(Auth.getLogin());
this.$router.go('/home');
}
Implementing the Blog with Vue 111

}
</script>

Loading the component, the created method will log the user out and redirect to the /home page.

7.11 Refactoring the home page


To finish this small application let’s re-factor the home page to allow the users to delete their Posts.
This is done with the following code:

<template>
<div v-show="showProgress" class="progress">
<div class="indeterminate"></div>
</div>
<div class="row" v-for="post in posts">
<div class="col s12">
<div class="card blue lighten-5">
<div class="card-content black-text">
<span class="card-title">{{post.title}}</span>
<p>{{post.text}}</p>
</div>
<div class="card-action">
<span><i class="material-icons">perm_identity</i> {{post.user.name}}</span>
<a href="#" @click="remove(post)" class="right blue-text" v-if="login.token!=nul\
l && login.id==post.user._id">Remove</a>
</div>
</div>
</div>
</div>

</template>
<script>
import Auth from './auth.js'
export default {
data (){
return {
posts: null,
showProgress:true,
login:Auth.getLogin()
}
},
created: function(){
//console.log(Auth.getLogin());
Implementing the Blog with Vue 112

this.showProgress=true;
this.loadPosts();
},
methods: {
remove: function(post){
post.token = Auth.getLogin().token;
this.$http.delete('/api/posts/'+post._id,post).then(function(response){
this.loadPosts();
},function(error){
//console.log(error)
Materialize.toast('Error: ' + error.data.message, 3000)
});
},loadPosts:function(){
this.$http.get('/api/posts').then(function(response){
this.showProgress=false
this.posts = response.data
console.log(this.posts);
},function(error){
this.showProgress=false
Materialize.toast('Error: ' + error.statusText, 3000)
})
}
}
}
</script>

The remove method will delete the Post if it belongs to the User. The method to load the Posts was
changed to a new method called loadPosts that can be called every time a Post is removed.
8. Mixins
In Vue the mixins are objects with properties and methods that can be “attached” to any Vue
component, making it part of the component. Mixins can be used to re-use code in your application
when needed.
Imagine that we want to follow the event sequence in every Vue component in our app. It would
be necessary to create the create, beforeComplete, compiled, ready, beforeDestroy and destroyed for
each component. But with mixin it’s possible to do it once and “attach” it to all your components.

8.1 Creating mixins


Let’s create the “vue-mixin” project:

vue init browserify-simple vue-mixin


cd vue-mixin
npm install

The mixin code for this example will listen to the created event and use console.log to show the
name of the component that was dispatched. Here’s the mixin code:

src/eventMixin.js

export const eventMixin = {


```js
created:function(){
console.log(`[${this.$options.name}] created`);
}
}

Create three components called Comp1, Comp2 and Comp3 and set up the mixin as follows:

113
Mixins 114

src/Comp1.vue

<template>Comp 1</template>
<script>
import {eventMixin} from './eventMixin'

export default{
mixins: [eventMixin]
}
</script>

src/Comp2.vue

<template>Comp 2</template>
<script>
import {eventMixin} from './eventMixin'

export default{
mixins: [eventMixin]
}
</script>

src/Comp3.vue

<template>Comp 3</template>
<script>
import {eventMixin} from './eventMixin'

export default{
mixins: [eventMixin]
}
</script>

Now add the components to the App.vue:


Mixins 115

src/App.vue

<template>
<div id="app">
<comp1></comp1>
<comp2></comp2>
<comp3></comp3>
</div>
</template>

<script>
import Comp1 from './Comp1.vue'
import Comp2 from './Comp2.vue'
import Comp3 from './Comp3.vue'
import {eventMixin} from './eventMixin'

export default {
components :{
Comp1,Comp2,Comp3
},
data () {
return {
msg: 'Hello Vue!'
}
},
mixins: [eventMixin]
}
</script>

Notice that we also added the mixin to the App.vue.


Compile and run the application with npm run dev and you should get a result similar to the next
image
Mixins 116

That way it’s possible to add extra features to components, without the need to implement heritage
or calling global methods. Notice that the mixins can be attached to any Vue property like data,
methods and filters among others.

8.2 Conflicting
We know that mixins can be attached to events, methods and properties of Vue components. When
Vue finds a conflict it merges both into the same object. For example, edit the Comp2 with the
following code:

src/Comp2.vue
<template>Comp 2</template>
<script>
import {eventMixin} from './eventMixin'

export default{
mixins: [eventMixin],
created : function() {
console.log(" event Comp 2 outside mixin ")
}
}
</script>

Notice that both mixin and the component listens to the created event. In this case both events will
be called, but the mixin hooks are always called first.
Mixins 117

The result of the previous code should be like this:

If there are conflicting keys when merging the objects, the component’s option will take
priority.
9. Plug-ins
The plug-in’s main goal is to add global functionalities to your Vue app. Think of a plug-in as
something that can be re-used in many projects. Some plug-ins examples are vue-router, vue-resource
and vuex.
In theory a plug-in can add the following functionalities:

• Add global properties and methods.


• Add directives, filters and transitions.
• Add properties to a Vue instance.
• Add an API with many methods and properties that make use of the previous three items.

9.1 Creating a plug-in


A Vue plug-in must implement the install method with its functionalities. Here’s a basic plug-in
example:

MyPlugin.install = function (Vue, options) {


// 1. add global method or property
Vue.myGlobalMethod = ...
// 2. add a global asset
Vue.directive('my-directive', {})
// 3. add an instance method
Vue.prototype.$myMethod = ...
}

To use a plug-in it’s necessary to import it and use the Vue.use command.
To better understand it, let’s create a simple plug-in called “dollar” that converts an integer into a
money format.

vue init browserify-simple plugin-dollar


cd plugin-dollar
npm init

118
Plug-ins 119

It’s not necessary to create a full project with vue-cli to create a plug-in. It’s best if the
plug-in is “pure” with only its java-script and package.json files to be able to be added to
the package manager in the future. Here’s another example vue-moment¹

After creating the project let’s create the dollar.js file in the project’s root directory:

module.exports = {
install: function (Vue, options) {

Vue.filter('dollar', function() {
var tmp = arguments[0]+'';
tmp = tmp.replace(/([0-9]{2})$/g, ",$1");
if( tmp.length > 6 )
tmp = tmp.replace(/([0-9]{3}).([0-9]{2}$)/g, ".$1,$2");
return '$ ' + tmp;
})
}
}

We used module.exports to allow the plug-in to be imported into other project’s components. The
install method is the only required method for a plug-in.
In this method we used arguments[0] to get the first filter argument, which is its value. For example,
if we use 123 | filter, the value of argument[0] will be “123”.
Then we return the tmp value with the value converted to a string in the money format.
With the plug-in ready we can install it in the main.js file of our project, where the Vue instance is
created.

src/main.js

import Vue from 'vue'


import App from './App.vue'

import dollar from '../dollar'


Vue.use(dollar)

new Vue({
el: 'body',
components: { App }
})

With the plug-in loaded it’s possible to use it anywhere in the application like in the App.vue:
¹https://github.com/brockpetrie/vue-moment
Plug-ins 120

src/main.js

<template>
<div id="app">
<h1>Plugin</h1>
<p>{{ value | dollar }}
</div>

</template>

<script>
export default {
data () {
return {
value: 123456
}
}
}
</script>

Notice that the “dollar” filter was used together with the value variable to make the following result:
10. Next Steps
10.1 Vuex
If you plan to use Vue in a real application, it’s highly recommended to use the Vuex (Vue + Flux)
concept to have full control of your variables and states of each application component.
For that we created a small e-book called Vuex Concepts¹ with the basic theory and practical
examples of Vuex applications.

10.2 Vue.js in Twitter


Follow @vuejs in Twitter to get updates about the framework news.

10.3 Vue Awesome


A curated list of awesome things related to Vue.js: https://github.com/vuejs/awesome-vue

¹http://leanpub.com/vuex

121

You might also like