Sei sulla pagina 1di 344

30 Days of Vue

WHAT IS VUE?
WHAT IS VUE?
Over the next 30 days, we’re going to walk through everything
you need to know to get started with the Vue framework. From
the very beginning through topics like the Vue Instance,
Components, and even Testing.

Each day in our 30-day adventure will build upon the previous day's
materials which will give us a good introduction to various terms, concepts,
and underpinnings of the Vue framework.

This series is mainly targeted to readers that have no prior Vue knowledge
and have a little or some experience with JavaScript. Though this course
has been prepared for you to cover the material linearly from article-to-
article, you are more than welcome to hop around in the course if you feel
you’ve already grasped certain concepts.

With all that said, let’s get started. We’ll start at the very beginning by
discussing what Vue is.

WHAT IS VUE?
Vue is an open source JavaScript framework geared towards building user
interfaces, created by Evan You <https://twitter.com/youyuxi?lang=en> . If
we take a glance at the front page of the main website <https://vuejs.org/>
, we can see that Vue is said to be the progressive JavaScript framework
that is approachable, versatile, and performant. Let’s explain each of
these points:

PROGRESSIVE
Vue is recognized to be progressive since it can often be scaled down as
well as it scales up. For very simple use cases, you can use Vue like you use
jQuery - by dropping a single script tag:

<script src="https://cdn.jsdelivr.net/npm/vue"></script>

But as your needs start to grow, Vue grows with you by giving you tools
within the ecosystem to help you be more productive. Oftentimes, Vue is
labeled as an adoptable framework since each of these tools can be
incrementally added when needed.

APPROACHABLE
Vue is understood to be approachable since as long as you know standard
HTML, CSS, and JS; you’re able to start working with Vue right away to
build more complex web applications.

VERSATILE
The Vue framework is recognized to be versatile since the Vue library itself
fits neatly within an ecosystem of tools that make up the entire Vue
framework. These tools are the:

The vue-cli <https://cli.vuejs.org/> (i.e. Vue Command Line Interface)


which allows for rapid prototyping and set-up of Vue-Webpack
applications.
vue-router <https://router.vuejs.org/> which helps us introduce client
side routing into our application with relative ease.
vuex <https://vuex.vuejs.org/guide/> , the Elm-inspired Flux-like
library that helps facilitate how data is managed in an application.
vue-test-utils <https://vue-test-utils.vuejs.org/> , the testing utility
library that introduces various helpers and function to make testing
Vue components a lot easier.
Each of the above tools was created and is currently maintained by the Vue
core team. This makes integrating and using them in Vue applications a
relatively seamless experience. We’ll be discussing each of these libraries
at various points throughout this series.

PERFORMANT
Finally, Vue is seen to be performant since it takes advantage of the virtual
DOM for incredibly fast re-render times. The Vue core library is also built to
require minimal effort towards performance optimization.

OKAY… COOL… BUT HOW DO WE USE IT?


In the simplest way possible, we can get started working with Vue by
placing a script tag in an HTML file that loads the latest version of Vue from
a Content Delivery Network (CDN). We can also reference a JavaScript file
(named main.js for example) which would be the file where our Vue
JavaScript code lives:

<html>
<body>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

With the Vue library available, we’re able to create a new Vue application.
We’ll create this application by declaring the Vue Instance - which is the
heart of a Vue application - in the main.js file. The Vue instance is created
by declaring the new Vue({}) constructor:
new Vue({
// options
});

A Vue instance accepts an options object which can contain details of the
instance such as its template, data, methods, etc. Root level instances allow
us to specify the DOM element with which the instance is to be
mounted/attached to, like so:

new Vue({
el: '#app',
});

We've just used the element option, el , to dictate the HTML element with
the id of app to be the mounting point of our Vue application.

Vue is a user interface library focused on the view layer. A Vue


application needs to depend on an HTML DOM element to
control how the element behaves.

The Vue instance can also return data that needs to be handled within the
view. This data has to be dictated within a data option. In our instance,
let’s declare a greeting data property that’s given a string value of Hello
World! :
src/main.js

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
},
});

To have the greeting data value be presented in the template, we’ll first
need to declare the element that our Vue app is to be mounted on (i.e. the
element with the id of app ):

<html>
<body>
<div id="app">
<!-- where our Vue template code will live -->
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

We’ll now be able to display the value of the greeting property in our Vue
instance on the HTML template. To bind data values as the text content of
elements, we’ll use the Mustache Syntax <https://vuejs.org/v2/guide
/syntax.html#Text> :
src/index.html

<html>
<body>
<div id="app">
<h2>{{ greeting }}</h2>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

The greeting data property in the template is now directly bound to the
value in our instance. When our app loads, we’ll be presented with Hello
World!

Hello World!

Live version - https://30dofv-helloworld.surge.sh


<https://30dofv-helloworld.surge.sh>

That was easy, wasn’t it? In the next article, we'll take a deeper look at the
data property of a Vue instance and how it enables reactivity of our Vue
applications.
30 Days of Vue

THE VUE INSTANCE -


DATA
THE VUE INSTANCE - DATA
Now that we know how to instantiate a Vue application with
the Vue instance, let's take a deeper look into how the data
property of a Vue instance helps enable reactivity.

THE VUE INSTANCE


To reiterate what we’ve learned, the Vue instance is the starting point of
Vue applications and contains a data property that allows us to bind data
from our instance and on to the template. In the last article, we bound the
value of a greeting data property on to the text content of an <h2> tag.
Let’s revisit that example and introduce a few more data properties:

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
user: 'Hassan Djirdeh',
city: 'Toronto',
},
});

We've introduced user and city data properties that have values of
'Hassan Djirdeh' and 'Toronto' respectively. We can re-use the
Mustache Syntax to bind these new data properties onto the template:
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>by {{ user }} who lives in {{ city }}</p>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

In the code above, we're also introducing a local stylesheet,


styles.css , to apply some simple styling on to our
application.

With the data being bound to the template, we're now able to see the
values of these different data properties.
Hello World!
by Hassan Djirdeh who lives in Toronto

Live version - https://30dofv-simplebinding.surge.sh


<https://30dofv-simplebinding.surge.sh>

In Vue, information within the data property of an instance is known to be


reactive <https://vuejs.org/v2/guide/reactivity.html> . This means that if
we manage to modify or change the data values of a Vue instance, Vue will
recognize this change and re-render the template to show the updated data
values.

METHODS AND HANDLING EVENTS


Let’s see an example of data reactivity. To help facilitate a change in data,
we’ll provide the user with a button that will be responsible for changing
the greeting message itself (i.e. the value of the greeting data property).
We’ll place this button at the bottom of our template:
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>by {{ user }} who lives in {{ city }}</p>
<button>Change Greeting</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

As of right now, our button doesn’t do anything. We can attach a click


listener to the button to trigger a change to the instance greeting
property. To facilitate this change, we can use the instance's methods
<https://vuejs.org/v2/guide/events.html#Method-Event-Handlers>
property:

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
user: 'Hassan Djirdeh',
city: 'Toronto',
},
methods: {
// instance methods
},
});
The methods property of a Vue instance allows us to define methods
bound to that instance that behave like normal JavaScript functions (i.e. are
evaluated only when called). In these methods, we’re able to directly
change data values kept in our instance.

Instead of using methods, we could also write our intended


functionality change inline in the template. We'll be discussing
more of this in article #8 - Methods & Computed Properties.

In our example, we’ll create a changeGreeting() method that toggles the


value of the greeting data property:

src/simple-data-change-example/main.js

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
user: 'Hassan Djirdeh',
city: 'Toronto'
},
methods: {
changeGreeting() {
this.greeting = this.greeting === 'Hello World!' ?
'What is up!' :
'Hello World!';
}
}
});

We’re using a simple ternary operator <https://developer.mozilla.org/en-


US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator> to
toggle the value of the greeting property from 'Hello World!' to 'What
is up!' and vice versa.

Notice how we’re referencing the value of the greeting property with
this.greeting ? When a Vue instance is instantiated, Vue recursively
creates a series of getters and setters for each data property to make them
reactive. Within an instance, the data object can then be accessed with
this.$data . With proxying <https://developer.mozilla.org/en-US/docs
/Web/JavaScript/Reference/Global_Objects/Proxy> , Vue proxies all the
properties of the instance so this.$data.greeting is equal to simply
stating this.greeting . We’ll be talking a little more about reactivity and
data-driven Vue apps tomorrow, but for more reading on this - be sure to
check out the Options/Data section <https://vuejs.org/v2/api/#data> of
the Vue documentation.

With our method prepared, we’ll need to call the method from our template
when the user clicks the 'Change Greeting' button. To handle this
interaction, we’ll use Vue’s v-on directive <https://vuejs.org/v2/guide
/events.html> .

A Vue directive is essentially a special type of command that


can be added to HTML content. We'll be discussing, in more
detail, Vue's most commonly used native directives in articles
#4, #5, and #6.

The v-on directive is one of the many native Vue directives available to us
in the template. To be able to listen to the button click event and run the
instance changeGreeting() method, we’ll attach the v-on directive to a
click listener on the button element.
src/simple-data-change-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>by {{ user }} who lives in {{ city }}</p>
<button v-on:click="changeGreeting">
Change Greeting
</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

When we now click the button, the changeGreeting() method is called


which changes the value of the greeting data property. When the
greeting data property changes, the template is automatically re-rendered
to show the change.

Give it a try! Click the 'Change Greeting' button multiple times to witness
the greeting text change back and forth.
Hello World!
by Hassan Djirdeh who lives in Toronto

Change Greeting

Live version - https://30dofv-simpledatachange.surge.sh


<https://30dofv-simpledatachange.surge.sh>

We’ll stop here for now and spend a little time tomorrow discussing how
the things we’ve learned today shape the way we build data-driven
applications in Vue.
30 Days of Vue

THE VUE INSTANCE -


DATA DRIVEN
APPLICATIONS
THE VUE INSTANCE - DATA DRIVEN
APPLICATIONS
In yesterday’s article, we’ve come to understand how data
behaves reactively in Vue. Today, we’re going to spend a little
more time discussing this behavior since it plays an especially
important role in how we build applications in Vue.

REACTIVE DATA
Data in Vue is treated as reactive since modifying data often directly causes
the view to update. For every key-value pair we declare in the data
property of an instance, the Vue library creates getters and setters
pertaining to that property. These setters and getters work under the hood
to track the data properties and cause the template to re-render when a
change has been made.

For a more detailed explanation of data reactivity - be sure to


read through the Reactivity in Depth <https://vuejs.org
/v2/guide/reactivity.html> section of the core Vue
documentation.

For those who may be coming from a React <https://reactjs.org/>


background, you might notice reactive state (i.e. data) is different to how
data is handled in React. In React, state should often be treated as
immutable <https://reactjs.org/docs/react-component.html#state> .
Reactive state is one of the key differences that make Vue unique. State (i.e.
data) management is often intuitive and easy to understand since
modifying state directly causes the view to update.

DATA DRIVEN APPLICATIONS


The reactive nature of data in Vue help facilitates the possibility of building
applications in a data-driven manner. To get a better understanding of this,
we’ll take a look at the simple application we built in yesterday’s article.

Hello World!
by Hassan Djirdeh who lives in Toronto

Change Greeting

Live version - https://30dofv-simpledatachange.surge.sh


<https://30dofv-simpledatachange.surge.sh>

Let’s assume we wanted to scale the application and introduce capabilities


like:

Displaying the current date and time.


Toggling the user and city information from a list of options.
Toggling the background color with the click of a button.
etc...

With all these capabilities, we would adopt the reactive nature of Vue and
introduce new data properties like date that’s given a value of the current
date ( new Date() ) or cities which could be an array that contains a
series of cities like ['Lagos', 'New York', 'Tokyo', 'Toronto'] .

The Mustache Syntax and certain directives (which we’ll start to see in the
next article), will help us in binding all or some of this information to the
template. With the help of methods and other inline capabilities, we can
trigger changes to the instance data which would update the template to
the situations we intend to see. This sort of explains the data-driven
mindset of how we build our UI.

If you've used React <https://reactjs.org> , Angular


<https://angular.io/> , or other modern-day front end
frameworks/libraries; you might be used to a similar pattern on
how modifying data/state drives the changes in an application
UI.

In contrast, let’s aim to reproduce the functionality of the application in the


last article (i.e. toggling the greeting message with the click of a button)
with the use of only standard (i.e. vanilla) JavaScript. Though there are a
few ways to achieve this, we might come up with something like this:

HTML
src/vanilla-js-toggle/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>Hello World!</h2>
<p>by Hassan Djirdeh who lives in Toronto</p>
<button onclick="changeGreeting()">
Change Greeting
</button>
</div>
<script src="./main.js"></script>
</body>
</html>

JS
src/vanilla-js-toggle/main.js

// Vanilla JS implementation

let greetingTag = document.getElementsByTagName("h2")[0];

changeGreeting = () => {
if (greetingTag.textContent === 'Hello World!') {
greetingTag.textContent = 'What is up!';
} else {
greetingTag.textContent = 'Hello World!';
}
}

The functionality that achieves the direct toggle between the different text
content is inherently similar to what we had before:

Check if the text content of the <h2> element is Hello World!.


If the text content is Hello World! - change it to What is up!.
If the text content isn’t Hello World! - change it back to Hello World!.

The difference in the two approaches can be seen in how we were able to
access and change the text content of the <h2> element. In the vanilla
JavaScript approach, the DOM is treated as the single source of truth.
To determine the text content of the <h2> element, we had to survey the
DOM, find the element, then survey its textContent value. This is because
the DOM is the only place that has this information!

With our Vue example, we were able to simply retrieve and change the
value of the data property being used in the template ( greeting ), without
having the need to survey the DOM. This is why the source of truth in Vue
applications is the data property of a Vue instance. In Vue applications,
we’ll hardly ever find ourselves using methods like
document.getElementsByTagName or
document.querySelector('img').setAttribute() and instead use the
data properties of our instances to drive the changes in the UI.

VUE DATA PROPERTIES


Vue initializes the reactivity of an instance upon instantiation, and as a
result, requires us to declare properties upfront before we decide to use
them. Because of this, we’re unable to directly add (or remove) properties
from an already created instance. As an example, this won’t work:

new Vue({
el: '#app',
data: {
user: 'Hassan Djirdeh',
city: 'Toronto'
},
methods: {
addGreeting() {
// greeting is not initialized :(
this.greeting = 'Hello World!';
}
}
});

In the example above, Vue would emit a console warning along the lines of:

Property ... "greeting" is not defined on the


instance...

We’re expected to initialize all the properties we intend to use upfront.


new Vue({
el: '#app',
data: {
greeting: '', // greeting is initialized
user: 'Hassan Djirdeh',
city: 'Toronto'
},
methods: {
addGreeting() {
// greeting can now be updated!
this.greeting = 'Hello World!';
}
}
});

Vue 3.0, which is expected to launch sometime in 2019, will use


a Proxy-based observation mechanism
<https://developer.mozilla.org/en-US/docs/Web/JavaScript
/Reference/Global_Objects/Proxy> to detect data changes.
This would allow us to delete or add new properties after an
instance has already been initialized! We take a deep dive to
the updates Vue 3.0 will bring in the second last article of the
series - Vue 3.0 and the future of Vue.

For more information on reactivity in Vue and declaring reactive properties


upfront, be sure to check out the Reactivity in Depth <https://vuejs.org
/v2/guide/reactivity.html> section of the Vue documentation.

That's it for today! In the next article, we’ll begin the discussion on some
important and very useful Vue directives.
30 Days of Vue

VUE DIRECTIVES
VUE DIRECTIVES
The last article was a little heavy on discussion. In today's
article, we’ll dive into more code examples by addressing
some important native Vue directives.

A Vue directive is essentially a special type of command that can be added


to HTML content and often appears as a prefixed HTML attribute. We’ll first
revisit the v-on directive since we’ve already used it in one of the earlier
articles.

EVENT HANDLING WITH V-ON


The v-on <https://vuejs.org/v2/guide/events.html> directive can be used
to create event listeners within the DOM to enable us to do something
when a particular event happens.

In article #2, the v-on directive was used to call an instance


changeGreeting() method when a button was clicked:

<button v-on:click="changeGreeting">
Change Greeting
</button>

Instead of triggering a method in the instance, we’re also able to run


JavaScript inline in the template:
<button v-on:click="greeting = 'Hi there!'">
Change Greeting
</button>

Though inline JavaScript works just as well, calling methods bound to the
instance is often preferable when intended functionality change gets more
complicated.

It's important to keep in mind that the v-on directive can be used with
virtually any native DOM event:

<h1 v-on:click="method">Click me!</h1>


<h1 v-on:dblclick="method">Double Click me!</h1>
<form v-on:submit="method">...</form>
<input v-on:keydown="method"
placeholder="Press down on keys" />
<input v-on:keyup="method"
placeholder="Release keys" />
<!-- ... -->

Here’s a code sample that shows some different event listeners with
different expected outcomes:

HTML
src/v-on-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>{{alertMessage}}</p>
<button v-on:click="changeGreeting">
Change Greeting
</button>
<button v-on:dblclick="changeGreeting">
Double click to change greeting
</button>
<input v-on:keyup="alertMessageGreeting"
placeholder="Type something" />
<input v-on:keyup.enter="alertEnterGreeting"
placeholder="Type and release Enter" />
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

JS
src/v-on-example/main.js

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
alertMessage: ''
},
methods: {
changeGreeting() {
this.greeting = this.greeting === 'Hello World!' ?
'What is up!' :
'Hello World!';
},
alertMessageGreeting() {
this.alertMessage = 'You typed something!';
},
alertEnterGreeting() {
this.alertMessage = 'You typed and pressed Enter!';
},
}
});

Hello World!

Change Greeting

Double click to change greeting

Type something

Type and release Enter


Live version - https://30dofv-von.surge.sh <https://30dofv-
von.surge.sh>

From the code sample above, you may have noticed the v-on directive be
used on a keyup.enter event. keyup.enter is one of the unique key
modifiers <https://vuejs.org/v2/guide/events.html#Key-Modifiers> Vue
provides to allow us to react to events from commonly used keys like the
Enter key.

Finally, event handlers usually have an event object that contains details
about the event. In the v-on directive, we’re able to access this original
event object by explicitly passing in the $event variable:

<h1 v-on:click="method($event)">Click me!</h1>

The v-on directive is essentially what we use to detect and handle events in
Vue applications.

ATTRIBUTE BINDING WITH V-BIND


The simplest form of data binding in Vue is the Mustache Syntax (i.e.
double curly braces) which is used to bind data values on to text content of
HTML elements.

In the code samples prepared in the first and second articles, we used the
Mustache Syntax to bind greeting , user , and city properties defined in
our instance on to the template:
<html>
<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>by {{ user }} who lives in {{city}}</p>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

We’re unable to use the Mustache syntax to bind data to standard


HTML attributes like href , id , src , etc. To bind HTML attributes, Vue
provides the native v-bind <https://vuejs.org/v2/guide
/syntax.html#Attributes> directive.

Here’s an example of using the v-bind directive to bind a data property


(named cityImage ) to the src attribute of an img element:

HTML
src/v-bind-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>by {{ user }} who lives in {{ city }}</p>
<img v-bind:src="cityImage" />
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

JS
src/v-bind-example/main.js

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
user: 'Hassan Djirdeh',
city: 'Toronto',
cityImage: 'https://bit.ly/2SKJPqJ'
}
});

With the cityImage appropriately bound to a hosted image of Toronto,


we’ll be presented with a view of Toronto’s skyline:
Hello World!
by Hassan Djirdeh who lives in Toronto

Live version - https://30dofv-vbind.surge.sh <https://30dofv-


vbind.surge.sh>

CONDITIONAL RENDERING WITH V-IF


(OR V-SHOW)
Oftentimes, we may find ourselves interested in conditionally rendering
content based on the value of an expression. In Vue, we can achieve this
with the help of the v-if <https://vuejs.org/v2/guide/conditional.html#v-if>
and v-show <https://vuejs.org/v2/guide/conditional.html#v-show>
directives.

To see an example of conditional rendering, let’s first add a button to our


previous example that would allow us to change the city title and image
from Toronto to Lagos:
HTML
<html>
<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p>by {{ user }} who lives in {{ city }}</p>
<img v-bind:src="cityImage" />
<button v-on:click="changeCity">
Change City
</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

JS
src/v-if-example/main.js

new Vue({
el: '#app',
data: {
greeting: 'Hello World!',
user: 'Hassan Djirdeh',
city: 'Toronto',
cityImage: 'https://bit.ly/2SKJPqJ'
},
methods: {
changeCity() {
if (this.city === 'Toronto') {
this.city = 'Lagos';
this.cityImage = 'https://bit.ly/2Rd4gQ3';
} else {
this.city = 'Toronto';
this.cityImage = 'https://bit.ly/2SKJPqJ';
}
}
}
});

The changeCity() method first checks if the city data value is


'Toronto' , if so - it changes the city to 'Lagos' and the cityImage to a
hosted image of the Lekki-Ikoyi Link Bridge <https://en.wikipedia.org
/wiki/Lekki-Ikoyi_Link_Bridge> in Lagos state. If the city information has
been already been changed, the changeCity() information simply reverts
the information back to Toronto.

If we wanted to render some content in certain conditions (e.g. when city


=== 'Toronto' ), the v-if or v-show directive would prove useful. Since I
(Hassan) live in Toronto, we can change the text content in the template to
better reflect my presence. For example, we’ll use two separate <p> tags
each containing a v-if directive to display the appropriate description text
content:

src/v-if-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p v-if="city === 'Toronto'">
by {{ user }} who lives in {{ city }}
</p>
<p v-if="city === 'Lagos'">
by {{ user }} who wishes to visit {{ city }}
</p>
<img v-bind:src="cityImage" />
<button v-on:click="changeCity">Change City</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

The text content of the first <p> tag that says by Hassan who lives in
Toronto will only render if the city property is equal to 'Toronto' . If
city is equal to 'Lagos' , the second <p> tag will instead only be
displayed and say by Hassan who wishes to visit Lagos :
Hello World!
by Hassan Djirdeh who lives in Toronto

Change City

Live version - https://30dofv-vif.surge.sh <https://30dofv-


vif.surge.sh>

Vue also provides the v-else <https://vuejs.org/v2/guide


/conditional.html#v-else> directive to describe an else block
and the v-else-if <https://vuejs.org/v2/guide
/conditional.html#v-else-if> directive to describe an else-if-
block.

Instead of the v-if directive, we could also use the v-show directive to
conditionally render content:
src/v-show-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<h2>{{ greeting }}</h2>
<p v-show="city === 'Toronto'">
by {{ user }} who lives in {{ city }}
</p>
<p v-show="city === 'Lagos'">
by {{ user }} who wishes to visit {{ city }}
</p>
<img v-bind:src="cityImage" />
<button v-on:click="changeCity">Change City</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>
Hello World!
by Hassan Djirdeh who lives in Toronto

Change City

Live version - https://30dofv-vshow.surge.sh <https://30dofv-


vshow.surge.sh>

Though they achieve a similar outcome, the v-if and v-show directives
differ from one another. The v-if directive does not render the element
only until the stated condition is true . The v-show directive, on the other
hand, always renders the element but controls the CSS display
<https://developer.mozilla.org/en-US/docs/Web/CSS/display> property
depending on whether the stated condition is true .

The v-if directive is usually preferred as long as you don’t need the element
to always be present in the DOM and you don’t expect the toggling
between displaying/hiding the element to happen very often. If we expect
the element to toggle often at runtime - the v-show directive would be
more appropriate.

SHORTHAND SYNTAX WITH V-ON AND


V-BIND
Vue provides unique shorthand syntax only for the commonly used v-bind
and v-on directives. The v-bind directive can be shortened with the :
symbol:

<!-- the full syntax -->


<img v-bind:src="dataProperty" />

<!-- the shorthand syntax -->


<img :src="dataProperty" />

And the v-on directive can be shortened with the @ symbol:

<!-- the full syntax -->


<button v-on:click="methodName"></button>

<!-- the shorthand syntax -->


<button @click="methodName"></button>

Though the shorthand syntax is entirely optional and achieves the exact
same outcome, we’ll stick with using the shorthand syntax for the rest of
the course.

Awesome! We'll stop here for today. In the next article, we’ll spend some
time discussing how the v-for directive can be used to help render lists of
elements.
30 Days of Vue

LIST RENDERING
WITH V-FOR
LIST RENDERING WITH V-FOR
Today we're going to work through how the v-for directive can
be used to dynamically render a list of elements based on a
data source.

Today's article is a summarized and simpler version of the article List


Rendering and Vue's v-for directive, which was originally posted on CSS-
Tricks <https://css-tricks.com/list-rendering-and-vues-v-for-directive/>
and the freeCodeCamp <https://medium.freecodecamp.org/an-
introduction-to-dynamic-list-rendering-in-vue-js-a70eea3e321> blogs.

LIST RENDERING
List rendering is one of the most commonly used practices in front-end
web development. Dynamic list rendering allows us to present a series of
similarly grouped information in a concise and friendly format to the user. In
almost every web application we use, we can see lists of content in
numerous areas of the app.

Take a website like Twitter <https://twitter.com> for example. When logged


in and in the main index route, we’re presented with a view similar to this:
Twitter uses lists of elements everywhere. Here we have a list of trends, a list
of tweets, and a list of potential followers

On the homepage, we’ve become accustomed to seeing a list of trends, a


list of tweets, a list of potential followers, etc. The content displayed in
these lists depends on a multitude of factors—our Twitter history, who we
follow, our likes, etc. As a result, it's probably safe to say all this data is
dynamic.

Though this data is dynamically obtained, the way this data is shown
remains the same. This is in part due to rendering lists of reusable
elements.

If we wanted to render a list of elements in Vue, the first thing that should
come to mind to accomplish this is the v-for <https://vuejs.org/v2/guide
/list.html> directive.

THE V-FOR DIRECTIVE


The v-for directive is used to render a list of items based on a data source.
The directive can be used on a template element and requires a specific
syntax along the lines of item in items , where items is a data collection
and item is an alias for every element that is being iterated upon:

Let’s see a very simple example of this in practice. Assume we have a


template currently displaying a list of static numbers in ascending order:
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<ul>
<li>1</li>
<li>10</li>
<li>100</li>
<li>1000</li>
<li>10000</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

1
10
100
1000
10000
Live version - https://30dofv-staticlist.surge.sh
<https://30dofv-staticlist.surge.sh>

If we had the list of numbers available to us in a collection (e.g. an array) in


our Vue instance:

src/v-for-example/main.js

new Vue({
el: '#app',
data: {
numbers: [1, 10, 100, 1000, 10000],
},
});

We could avoid repeating the <li> element in the template and instead
have the v-for directive do the work for us. Since numbers is the array we’ll
be iterating over, number would be an appropriate alias to use. We’ll add
the v-for directive on the element we want repeated - the <li> element:
src/v-for-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<ul>
<li v-for="number in numbers">{{ number }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

We’re using the Mustache syntax to bind the number alias on to the text
content of the repeated element since we’re interested in only displaying
the number values from the array.

At this moment, the v-for directive would display the list of static numbers
from the numbers data array:
1
10
100
1000
10000

Live version - https://30dofv-vfor.surge.sh <https://30dofv-


vfor.surge.sh>

In addition to helping make the template be more D.R.Y


<https://en.wikipedia.org/wiki/Don%27t_repeat_yourself> , the v-for
directive is helpful since our application is now entirely dynamic.
Regardless of how the numbers array changes over time, our set up will
always render all the numbers in the collection in the same markup we
expect.

THE KEY ATTRIBUTE


It’s common practice to specify a key <https://vuejs.org/v2/guide
/list.html#key> attribute for every iterated element within a rendered v-for
list. This is because Vue uses the key attribute to create unique bindings
for each node’s identity.

If there were any dynamic UI changes to our list (e.g. the numbers list gets
randomly reshuffled), Vue will (by default) opt towards changing data
within each element instead of moving the DOM elements accordingly. This
won’t be an issue in most cases. However, in certain instances where our
v-for list depends on DOM state and/or child component state, this can
cause some unintended behavior.

Let’s see an example of this. Instead of rendering just the number content
within each element, let’s render both the number value and an input
element for each number in the numbers array.

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<ul>
<li v-for="number in numbers">
<p>{{ number }}</p>
<input placeholder="type something..."/>
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

Assume we wanted to introduce another new feature into our app. This
feature would involve allowing the user to shuffle the list of numbers
randomly. To do this, we’ll first include a “Shuffle!” button in our HTML
template right after the unordered list:
src/v-for-no-key-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<ul>
<li v-for="number in numbers">
<p>{{ number }}</p>
<input placeholder="type something..."/>
</li>
</ul>
<button @click="shuffle">Shuffle!</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script
src="https://cdn.jsdelivr.net/npm/lodash/lodash.js">
</script>
<script src="./main.js"></script>
</body>
</html>

We’ve attached a click event listener on the button element to call a


shuffle method when triggered. We've also introduced a new <script>
tag in our template that has a src pointing to a CDN of the Lodash
<https://lodash.com/> utility library. We'll be using Lodash to help create
the shuffle functionality in our list.

In our Vue instance; we’ll create the shuffle method responsible for
randomly shuffling the numbers collection in the instance. To avoid having
to create a random shuffle of our own, we’ll use the Lodash _.shuffle
<https://lodash.com/docs/4.17.11#shuffle> method to achieve this:
src/v-for-no-key-example/main.js

new Vue({
el: '#app',
data: {
numbers: [1, 10, 100, 1000, 10000],
},
methods: {
shuffle() {
this.numbers = _.shuffle(this.numbers)
}
}
});

Lodash <https://lodash.com/> is a JavaScript utility library


that provides a large collection of additional methods to help
interact with arrays, objects, numbers, strings, etc. In our
application, we're simply using the _.shuffle
<https://lodash.com/docs/4.17.11#shuffle> method which
shuffles an array using a version of the Fisher-Yates algorithm
<https://exceptionnotfound.net/understanding-the-fisher-
yates-card-shuffling-algorithm/> .

If we save our changes, refresh the app, and click the shuffle button a few
times; we’ll notice the numbers in the list get randomly assorted with each
click.
1
type something...

10
type something...

100
type something...

1000
type something...

10000
type something...

Shuffle!

Live version - https://30dofv-vfornokey.surge.sh


<https://30dofv-vfornokey.surge.sh>

However, if we type some information in the input of each list element then
click shuffle; we’ll notice something peculiar happening:
GIF - Typing in inputs then shuffling

<https://bit.ly/2VyaAQI>

Though each rendered list element contains its own displayed number and
input field, when we shuffle the list - the number in the element is the
only portion that gets shuffled. This is because since we haven’t opted to
using the key attribute, Vue has not created unique bindings to each list
item. As a result, when we’re aiming to reorder the list items, Vue takes the
more performant saving approach to simply change (or patch) data in each
element. Since the temporary DOM state (i.e. the inputted text) remains in
place, we experience this potentially unintended mismatch.

To avoid this; we’ll have to assign a key to every rendered element in the
list. The key attribute for every element should be unique so we’ll
restructure our numbers collection to be a series of objects with each
object containing id and value properties:
src/v-for-with-key-example/main.js

new Vue({
el: '#app',
data: {
numbers: [
{id: 1, value: 1},
{id: 2, value: 10},
{id: 3, value: 100},
{id: 4, value: 1000},
{id: 5, value: 10000}
],
},
methods: {
shuffle() {
this.numbers = _.shuffle(this.numbers)
}
}
});

In the template, we’ll now reference number.value as the text content that
would be rendered and we’ll use the v-bind directive to bind number.id as
the key attribute for the v-for directive:
src/v-for-with-key-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>

<body>
<div id="app">
<ul>
<li v-for="number in numbers" :key="number.id">
<p>{{ number.value }}</p>
<input placeholder="type something..." />
</li>
</ul>
<button @click="shuffle">Shuffle!</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script
src="https://cdn.jsdelivr.net/npm/lodash/lodash.js">
</script>
<script src="./main.js"></script>
</body>
</html>

Vue will now recognize each list element’s identity; and thus reorder the
elements when we intend on shuffling the list. Give it a try here - type some
information in a few input fields and click shuffle a few times.
1
type something...

10
type something...

100
type something...

1000
type something...

10000
type something...

Shuffle!

Live version - https://30dofv-vforkey.surge.sh


<https://30dofv-vforkey.surge.sh>

Should the key attribute always be used? It’s recommended. The Vue
docs <https://vuejs.org/v2/guide/list.html#key> specify that the key
attribute should only be omitted if:

We intentionally want the default manner of patching elements in


place for performance reasons.
Or the DOM content is simple enough.
Great work today! Tomorrow we’ll be taking a look at Vue's v-model
directive.
30 Days of Vue

FORM HANDLING
WITH V-MODEL
FORM HANDLING WITH V-MODEL
In yesterday’s article, we discussed the v-for directive and its
role in helping display lists of elements. Today, we’ll spend a
little time taking a look at the v-model directive and its ability
to create two-way data binding.

V-MODEL
The v-model <https://vuejs.org/v2/guide/forms.html> directive creates
two-way data binding between form inputs and text area elements. In other
words, v-model directly binds user input with a Vue object’s data model
that as one changes, the other automatically gets updated.

The v-model directive syntax takes an expression which is the name of the
data property that the input is bound to:

v-model removes any difficulty in keeping user input and the data model in
sync and can be applied to virtually any form input element:

Text Inputs
Dropdowns
Checkboxes
Radio Inputs
etc…

Let’s see the v-model directive in action. For today's article, we’ll be setting
up a form that contains a series of different inputs:

The form will contain a text input field to capture the user’s name , a
dropdown for the user to select a subject , a checkbox to verify if the user
has approved termsAndConditions , radio inputs to determine if the user
has selected yesOrNo , and a Submit button.

Vue requires us to initialize any properties we intend to use in our


application, so we’ll initialize the properties that are to be used in our form
with empty/initial values. We’ll also set up a submit() instance method
that will console log the values of each of these properties.
src/v-model-example/main.js

new Vue({
el: '#app',
data: {
name: '',
subject: '',
termsAndConditions: false,
yesOrNo: 'No'
},
methods: {
submit() {
console.log('name', this.name);
console.log('subject', this.subject);
console.log(
'termsAndConditions',
this.termsAndConditions
);
console.log('yesOrNo', this.yesOrNo);
}
}
});

In the code sample above, notice how the properties in data are initialized
differently depending on the form input that we expect the property to be
used with?

We want the name and subject fields to be rendered with values of an


empty string.
termsAndConditions will be bound to a checkbox. HTML checkbox
elements traditionally have a boolean value unless otherwise
specified. We’ve initialized termsAndConditions with false since we
expect the checkbox to be rendered in the unchecked (i.e. false)
state.
Radio inputs are often arranged in groups with each input usually
given a unique value. We’ve initialized the yesOrNo property that
would be used in our radio inputs with a string value of 'No'.

We'll now create the template and use the v-model directive to bind the
appropriate data properties to their respective input elements. We’ll also
attach a listener to the submit button to call an instance submit() method
when clicked. This makes our entire template look like the following:
src/v-model-example/index.html
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<div class="field">
<label class="label">Name</label>
<input v-model="name"
class="input"
type="text"
placeholder="Text input" />
</div>

<div class="field">
<label class="label">Subject</label>
<div class="select">
<select v-model="subject">
<option disabled value="">
Select dropdown
</option>
<option>Engineering</option>
<option>Computer Science</option>
<option>Biology</option>
<option>Other...</option>
</select>
</div>
</div>

<div class="field">
<label class="checkbox">
<input v-model="termsAndConditions"
type="checkbox" />
I agree to the terms and conditions
</label>
</div>

<div class="field">
<label class="radio">
<input v-model="yesOrNo"
type="radio"
value="Yes" />
Yes
</label>
<label class="radio">
<input v-model="yesOrNo"
type="radio"
value="No" />
No
</label>
</div>

<div class="field">
<button class="button is-info" @click="submit">
Submit
</button>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

In the template, we've introduced a local styles.css file like we've done
before but we've also included the Bulma CSS <https://bulma.io
/documentation/> framework into our application from a CDN.

Bulma <https://bulma.io/documentation/> is a CSS


framework, much like Twitter’s popular Bootstrap
<http://getbootstrap.com/> framework. It provides us with a
grid system and some simple styling. Though we'll be using
Bulma from time to time, we won’t need to know Bulma in-
depth to go through this article or the rest of the course.

Let’s break down what each of the field elements in the template contain.

For every keystroke made in the 'Name' text input field, the v-model
directive will bind the value of this input field to the name data property.

src/v-model-example/index.html

<div class="field">
<label class="label">Name</label>
<input v-model="name"
class="input"
type="text"
placeholder="Text input" />
</div>

For every option selected in the 'Subject' dropdown, v-model will bind the
value of the option selected to the subject data property. We’ve set a
value of an empty string (which is also the initial value of the subject
property) to the first disabled option as to have it selected when the
application first renders.
src/v-model-example/index.html

<div class="field">
<label class="label">Subject</label>
<div class="select">
<select v-model="subject">
<option disabled value="">
Select dropdown
</option>
<option>Engineering</option>
<option>Computer Science</option>
<option>Biology</option>
<option>Other...</option>
</select>
</div>
</div>

Checkbox inputs naturally have a boolean value which dictates whether the
checkbox is checked or unchecked. Our termsAndConditions property is
initialized with false and bound directly to the checkbox input.

src/v-model-example/index.html

<div class="field">
<label class="checkbox">
<input v-model="termsAndConditions"
type="checkbox" />
I agree to the terms and conditions
</label>
</div>

Finally, since radio inputs usually have custom values specified with them,
we’ve set the expected values for each radio input alongside the v-model
directive.
src/v-model-example/index.html

<div class="field">
<label class="radio">
<input v-model="yesOrNo"
type="radio"
value="Yes" />
Yes
</label>
<label class="radio">
<input v-model="yesOrNo"
type="radio"
value="No" />
No
</label>
</div>

We can now go ahead and test our application. If we populate our form,
click the Submit button, and check the developer console - we’ll be able to
see the values of each of the data attributes within our instance.
Name

Text input

Subject

Select dropdown

I agree to the terms and conditions

Yes No

Submit

Live version - https://30dofv-vmodel.surge.sh


<https://30dofv-vmodel.surge.sh>

Here’s a sample expected response:

It’s important to keep in mind that when using the v-model directive -
v-model takes the data property of the Vue instance as the single
source of truth as opposed to HTML element attributes like checked or
selected .

Oftentimes we would probably find ourselves using the v-model directive


in the simplest possible way but there are ways to create unique value
bindings and modifiers in certain cases. Be sure to check out the Form
Input Bindings - Value Bindings <https://vuejs.org/v2/guide
/forms.html#Value-Bindings> and Form Input Bindings - Modifiers
<https://vuejs.org/v2/guide/forms.html#Modifiers> sections of the Vue
documentation if you're interested in seeing how some of these unique
bindings/modifiers can be applied.

TWO-WAY DATA BINDING


Though the v-model directive creates two-way data binding, we haven’t
really been able to see the two-way binding in the application we've built in
this article. We could witness this by creating functionality that changes the
values of the data properties being used in the form and verifying that our
template updates (i.e. re-renders) to show the new values.

A simpler way we can witness v-model's two-way data binding is by using


the Vue Devtools <https://github.com/vuejs/vue-devtools> - a browser
dev tools extension built and maintained by the Vue core team. Vue
Devtools provides incredibly useful capabilities in debugging Vue
applications and we’re going to spend the next article discussing it!

Until tomorrow!
30 Days of Vue

VUE DEVTOOLS
VUE DEVTOOLS
Today’s article is going to introduce the Vue Devtools - a
browser extension tool built to help debug Vue applications.

VUE DEVTOOLS
The Vue Devtools <https://github.com/vuejs/vue-devtools> is a
development tool built and maintained by the Vue core team. It can be
installed through one of the following formats:

Chrome Extension <https://chrome.google.com/webstore/detail


/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd>
Firefox Addon <https://addons.mozilla.org/en-US/firefox/addon/vue-
js-devtools/>
Standalone Electron App <https://github.com/vuejs/vue-devtools
/blob/master/shells/electron/README.md>

If you don’t have the Devtools installed - feel free to install it with the format
you prefer. We’ll be using and referencing the Devtools at separate points
throughout the course.

When successfully installed on to a browser, we'll be able to see the icon


available in our browser menu. If Vue is not detected in the page, the icon
in the browser menu would be greyed out and the prompt will tell us that
"Vue.js is not detected:
For applications that use Vue, the browser menu won’t be greyed out.
However, we’ll be notified that we’re unable to use the extension if the app
is in production or the Devtools is explicitly disabled:

This app is chess.com!

Finally, for applications we develop locally and don’t have the Devtools
explicitly disabled, we’ll be notified that Vue is detected and we’re able to
use the extension:

FILE BASED URLS - CHROME


If you’d like to use the Vue Devtools with applications opened via file://
protocol in Chrome - you’ll need to enable the “Allow access to file URLs”
setting for the extension in Chrome’s extension manager:
In Firefox, the extension should have access to file based URLs by default.

USING THE VUE DEVTOOLS


Let’s use the Vue Devtools on the application we’ve set up in the last
article. If we recall, we used the v-model directive to help bind data
properties to different inputs in a form:
Name

Text input

Subject

Select dropdown

I agree to the terms and conditions

Yes No

Submit

Live version - https://30dofv-vmodel.surge.sh


<https://30dofv-vmodel.surge.sh>

Launching the application, opening the browser Devtools, and locating the
Vue tab - we’ll be able use the Vue Devtools to debug our application:
To use the Vue Devtools, you may need to open the
application in a separate tab/window instead of surveying the
app within the iframe. All code samples can be found for each
respective article/day at the Github Repo <https://github.com
/fullstackio/30-days-of-vue/> .

In the "Components" tab, we're able to survey all the components (i.e.
instances and child-instances) in our application. Our application only
contains a single <Root> component which refers to the Vue instance of
the entire Vue app. By selecting the <Root> component, we’re then able to
survey the data properties instantiated in our app:
To better recognize how the v-model directive allows for two-way data
binding, we can:

Change something directly in our form and verify that data of our
<Root> instance, in the Vue Devtools, is automatically updated.
Change the value of a data property directly on the Vue Devtools and
verify that our template re-renders to show the updated value.

Here's a GIF displaying just that.

<https://bit.ly/2RxFolt>
By being able to directly update our application and monitor data changes
(and vice-versa), the Vue Devtools is an especially useful tool for
debugging Vue apps.

The Vue Devtools also offers a lot more capabilities like being able to track
Custom Events, inspect props (data) being passed from one component to
its child, and conduct time travel debugging in a Vuex integrated app. We’ll
be discussing these features when we investigate the relevant concepts in
the course.

Congrats on getting through the first week! In the next coming sections,
we’ll be taking a deeper dive into the Vue instance.
30 Days of Vue

METHODS AND
COMPUTED
PROPERTIES
METHODS AND COMPUTED
PROPERTIES
Today, we'll take a look at the computed property of a Vue
instance and see how it differs from using a standard instance
method.

Phew! We made it to week two (relatively unscathed)! Through this point,


we've talked through some of the basic features of the Vue instance (data
reactivity), template binding (Mustache syntax/directives), and the Vue
Devtools.

In this article, we're going to look at two specific properties of the Vue
instance - Methods and Computed Properties.

METHODS
We used methods in articles #2 and #3 to highlight reactivity of data
contained within a Vue instance. To reiterate, methods in a Vue instance
behave like normal JavaScript functions and are evaluated only when
explicitly called. Instead of using methods we could always write our
intended functionality change inline in the template.

Let’s see an example of this. We’ll reuse an example seen in the Vue
documentation that involves reversing a series of characters from a string.
We'll first create a Vue instance that contains a single message property
that has a value of 'Greetings!' :
src/inline-example/main.js

new Vue({
el: '#app',
data: {
message: 'Greetings!',
},
});

In the template, we’ll look to bind the message directly and also bind the
message in its reversed state. We’ll reverse the value of message by
splitting the property into an array of characters ( .split('') ), reversing
the elements in the array ( .reverse() ), and rejoining the reversed array
back into a single string ( .join('') ).
src/inline-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<div class="card">
<header class="card-header card-header-title">
<span>Original:</span>
{{ message }}
</header>

<header class="card-header card-header-title">


<span>Reversed:</span>
{{ message.split('').reverse().join('') }}
</header>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

With the help of the styling given to us by Bulma, our simple app will look
like the following:
Original: Greetings!

Reversed: !sgniteerG

Live version - https://30dofv-inline.surge.sh <https://30dofv-


inline.surge.sh>

There’s nothing inherently wrong with specifying functionality change, like


the above, inline. However, methods are often times more appropriate to
use when the intended changes get harder to decipher.

We can change the above example to instead use a method in our Vue
instance to help reverse the message string:

src/methods-example/main.js

new Vue({
el: '#app',
data: {
message: 'Greetings!',
},
methods: {
reverseString(string) {
return string.split('').reverse().join('');
},
}
});

The method is given a name of reverseString and expects a payload. We


can declare this method in the template and pass in the message property
as the payload:
src/methods-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<div class="card">
<header class="card-header card-header-title">
<span>Original:</span>
{{ message }}
</header>

<header class="card-header card-header-title">


<span>Reversed:</span>
{{ reverseString(message) }}
</header>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

Our UI would behave just the way it had before by displaying the message
greeting and the reversed version right below it:
Original: Greetings!

Reversed: !sgniteerG

Live version - https://30dofv-methods.surge.sh


<https://30dofv-methods.surge.sh>

Functionality wise - the above two examples achieve the same thing.
Methods might be seen to be more appropriate since it keeps the template
cleaner and easier to understand.

We’re also able to achieve the same outcome as above with the use of
another property - called the computed <https://vuejs.org/v2/guide
/computed.html#Computed-Properties> property.

COMPUTED PROPERTIES
Computed properties are used to handle complex calculations of
information that need to be displayed in the view. For our third iteration in
building the same simple app, we'll introduce a computed property called
reverseMessage that simply reverses the message data property like we’ve
done before:
src/computed-example/main.js

new Vue({
el: '#app',
data: {
message: 'Greetings!',
},
computed: {
reverseMessage() {
return this.message.split('').reverse().join('');
},
}
});

In the template, we can render the value of the reverseMessage computed


property just as we would have rendered any other data property:
src/computed-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<div class="card">
<header class="card-header card-header-title">
<span>Original:</span>
{{ message }}
</header>

<header class="card-header card-header-title">


<span>Reversed:</span>
{{ reverseMessage }}
</header>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

With this, our app will behave as desired:

Original: Greetings!

Reversed: !sgniteerG
Live version - https://30dofv-computed.surge.sh
<https://30dofv-computed.surge.sh>

This begs the question, what difference is there to using a computed


property or having a method instead return a value?

METHODS VS. COMPUTED PROPERTIES


In the examples above, using a method or a computed property pretty
much achieved the exact same outcome. The key difference to using
computed properties is that computed properties are cached based on
the dependencies they depend on.

If we take a look at the reverseMessage computed property we’ve


declared, we can see it has one data dependancy - the message property.

computed: {
reverseMessage() {
return this.message.split('').reverse().join('');
},
}

The value of reverseMessage directly depends on the message data


property. When the value of message changes, so does reverseMessage .
Computed properties are useful because as long as the dependant data
property ( message ) remains constant (i.e. unchanged), calling the
computed property ( reverseMessage ) multiple times will always return the
same cached value.

Let's see a simple example of this visually. We can place a console.log()


message in the computed property function to alert us when the function
has been run:

computed: {
reverseMessage() {
console.log('computed function is run!');
return this.message.split('').reverse().join('');
},
}

In the template, we can aim to render the reverseMessage computed


property a couple of times:
<div id="app">
<div class="card">
<header class="card-header card-header-title">
<span>Original:</span>
{{ message }}
</header>
</div>

<div class="card">
<header class="card-header card-header-title">
<span>Reversed:</span>
{{ reverseMessage }}
</header>
</div>

<div class="card">
<header class="card-header card-header-title">
<span>Reversed:</span>
{{ reverseMessage }}
</header>
</div>

<div class="card">
<header class="card-header card-header-title">
<span>Reversed:</span>
{{ reverseMessage }}
</header>
</div>
</div>

By running the application and opening our browser console, we’ll see the
console.log() message logged only once:
The first time the reverseMessage property is computed, its value is
cached. With every other call to render the value of reverseMessage , the
message property hasn’t changed, so the cached result is simply returned
without running the computed function again.

If we repeat a similar example but instead call methods multiple times in


the template, the console.log() message will be run every single time the
method is declared:

In conclusion, though methods can be used in place of computed


properties - computed properties should essentially be used if we intend
to compute a value from a data property. Caching can help our application
with performance once our application starts to have countless properties
with each derived functionality potentially being somewhat
computationally expensive.
Here's a table that highlights the main differences between using methods
or computed properties:

A good rule of thumb to follow:

Use methods when responding to changes (e.g. clicking a button,


submitting a form, etc.) or to run explicit functionality change within
the instance (e.g. have a method be called from a lifecycle hook).
Use computed properties for data manipulation (e.g. create a sorted
array from an unsorted array in the instance).

We’ll be stopping here for today and be taking a look at another instance
property called watchers tomorrow.
30 Days of Vue

WATCHERS
WATCHERS
Yesterday, we discussed how methods and computed
properties work within a Vue instance. Today, we’ll briefly
discuss another instance property called watchers (or
sometimes known as the watch property).

WATCHERS
The watch <https://vuejs.org/v2/guide/computed.html#Watchers>
property in Vue essentially allows us to perform work in reaction to
specific data changes.

To see an example of watchers, we’ll create a simple app that contains two
input fields - one to capture the distance in kilometers and the other in
meters:

When the user types information in one particular field, we want the other
input field to automatically display the equivalent value in its own unit. For
example, when the user types the number 1 into the KM input field - the M
input field should automatically display 1000 ( 1KM === 1000M ).

To get things started, we’ll instantiate a Vue instance that contains km and
m data properties both initialized with values of 0 .

new Vue({
el: '#app',
data: {
km: 0,
m: 0
}
});

We’ll use the v-model directive to bind the km and m data properties to
two separate input fields. Specifying placeholders and declaring input
type=number to both fields will have our template look something like the
following:
src/watchers-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<div class="field">
<label class="label">KM</label>
<input v-model="km"
class="input"
type="number"
placeholder="Type a length in KM!">
</div>

<div class="field">
<label class="label">M</label>
<input v-model="m"
class="input"
type="number"
placeholder="Type a length in M!">
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

To achieve the desired outcome of having one input field be updated in


response to the change in the other - we can use the watch property. To
watch the change in the KM input field, we’ll declare the watch property to
watch the km data value in our instance:

new Vue({
el: '#app',
data: {
km: 0,
m: 0
},
watch: {
km() {
// whenever km changes, this function runs
},
}
});

Since we also want the KM input field to be updated in response to


changes in the M input field, we’ll also declare a watcher on the m data
value:
new Vue({
el: '#app',
data: {
km: 0,
m: 0
},
watch: {
km() {
// whenever km changes, this function runs
},
m() {
// whenever m changes - this function runs
}
}
});

The functions we’ve declared within the watch property are run whenever
the km or m data values have been changed respectively. This is how we’re
essentially watching these properties.

Watch properties automatically provide payloads of the new value upon


change and the old value prior to the change:

watch: {
property(newVal, oldVal) {
// ...
}
}

For our application - we don’t need access to the old values, and the new
values would be equal to the properties being watched. As a result, we
won’t have the need to use any of these parameters.
As each watch function runs, we’ll update the other data property by either
multiplying or dividing the watched property by 1000:

src/watchers-example/main.js

new Vue({
el: '#app',
data: {
km: 0,
m: 0
},
watch: {
km() {
this.m = this.km ? this.km * 1000 : 0;
},
m() {
this.km = this.m ? this.m / 1000 : 0;
}
}
});

We’re using the ternary operator to update the value of the non-watched
input to 0 if the watched property is ever invalid. Now, whenever a value is
entered in an input; the other input field will be updated to show the
change!

KM

0
Live version - https://30dofv-watchers.surge.sh
<https://30dofv-watchers.surge.sh>

Awesome! Though this works perfectly well, a good question to ask right
now would be - do we need watchers to do what we just did?

WATCHERS VS. COMPUTED PROPERTIES


We could build the same application without the use of watchers and
instead use computed properties. We know computed properties allow us
to compute a new property from an existing data property. With no change
to our template, we could drop the m value from the instance data object
and instead compute it from the km value:

new Vue({
el: '#app',
data: {
km: 0
},
computed: {
m() {
return this.km * 1000;
}
}
});

When the value of km ever changes, the value of m will be updated. But
this only works one way. How would we be able to update the km data
property when the value of m gets changed? Though not the default way of
using computed properties, we can configure a computed property to be
both a getter and a setter <https://vuejs.org/v2/guide
/computed.html#Computed-Setter> :

new Vue({
el: '#app',
data: {
km: 0
},
computed: {
m() {
get() {
// get the value of m
},
set() {
// when m changes - this function runs
}
}
}
});

If we opt to declare a get() and set() function within a computed


property, the get() function would be where we simply compute the value
of the property. The set() function, however, would be the function that
runs whenever the computed property gets directly changed.

In our case, we’d use the get() function to get the value of m like we’ve
done before. In the set() function, we’ll compute what the value of km
would be whenever the m value ever changes. We’ll use the payload of the
set() function which is the new value of the computed m property upon
change to determine what the km value would be.
src/computed-example/main.js

new Vue({
el: '#app',
data: {
km: 0
},
computed: {
m: {
get() {
return this.km * 1000;
},
set(newValue) {
this.km = newValue/1000;
}
}
}
});

Our app would now work as expected! When one input changes, the other
is updated.

KM

0
Live version - https://30dofv-computed2.surge.sh
<https://30dofv-computed2.surge.sh>

Can we always use computed properties in place of a watcher? No. In


certain cases, you may find yourself in need of using the specific watch
property. The Vue documentation <https://vuejs.org/v2/guide
/computed.html#Watchers> states that “[watchers are] most useful when
you want to perform asynchronous or expensive operations in response to
changing data”. In most cases, however, watchers tend to be overused
where it might be more appropriate to instead use a computed property
<https://vuejs.org/v2/guide/computed.html#Computed-vs-Watched-
Property> .

Great work today! Tomorrow, we’ll be taking a look at the different lifecycle
hooks a Vue instance contains before finally discussing Vue Components!
30 Days of Vue

LIFECYCLE HOOKS
LIFECYCLE HOOKS
Today, we're going to talk a bit about how an instance lives in
an application. That is, we'll talk about the Vue instance’s
lifecycle.

In a Vue instance, Vue gives us some hooks where we can insert our own
functionality at different times in the instance’s lifecycle. In order to hook
into the lifecycle, we'll need to define functions on an instance which Vue
calls at the appropriate time for each hook. An example of such a lifecycle
hook is the created() hook.

CREATED
The created() hook is run when an instance has just been created, where
the instance data and events are active, and when the instance can be
accessed. Since the created() hook is run the moment an instance has
been created but before the DOM has been mounted/rendered, it’s often
the ideal moment to fetch data that is needed to populate the instance.

For today's article, let's attempt to display a card element in which its
content is to be obtained from an external source. This card would look
something like this:
The external data source that is to provide us with the data we need would
be the /users <https://jsonplaceholder.typicode.com/users> resource of
JSONPlaceholder - a fake online REST API for testing and prototyping.

As we set our up our Vue instance, we’ll need to initialize all the data we
intend to have displayed in the template:

new Vue({
el: '#app',
data: {
name: '',
email: '',
company: {
name: '',
catchPhrase: '',
}
},
});

We’ll utilize the Card UI element from Bulma <https://bulma.io


/documentation/components/card/> and bind the data on to our template:
src/created-example/index.html
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<div class="card">
<header class="card-header">
<p class="card-header-title">
{{name}}
</p>
</header>
<div class="card-content">
<div class="content">
<p>{{company.name}}</p>
<p>"{{company.catchPhrase}}"</p>
</div>
</div>
<footer class="card-footer">
<a href="#" class="card-footer-item">
{{email}}
</a>
</footer>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script
src="https://unpkg.com/axios/dist/axios.min.js">
</script>
<script src="./main.js"></script>
</body>
</html>

We've introduced a <script> tag that references the axios


<https://github.com/axios/axios> library which will be the HTTP library
we'll use to make our GET request.

At this moment, our card won’t display anything and look like the following:

The floating quotations are the quotations we’re using in the template to
wrap {{company.catchPhrase}}

Let’s update our Vue instance to make a request to the JSONPlaceholder


API. We’ll declare the created() hook and use the axios library to make
our request.
new Vue({
el: '#app',
data: {
name: '',
email: '',
company: {
name: '',
catchPhrase: '',
}
},
created() {
axios.get(
'https://jsonplaceholder.typicode.com/users'
).then((response) => {
// Use response to update data
});
},
});

The /users <https://jsonplaceholder.typicode.com/users> resource of the


api returns details of 10 different users. For our application, we’ll randomly
grab a single user object from the list and update the details of our instance
data from that object. This would have our created() hook look something
like this:
new Vue({
el: '#app',
data: {
// ...
},
created() {
axios.get(
'https://jsonplaceholder.typicode.com/users'
).then((response) => {
const data = response.data;
const randomUser = response.data[
Math.floor(Math.random() * data.length)
];

this.name = randomUser.name;
this.email = randomUser.email;
this.company.name = randomUser.company.name;
this.company.catchPhrase =
randomUser.company.catchPhrase;
});
},
});

When we launch our app, the created() hook would run even before the
DOM is mounted on the template. The axios call that gets fired in the hook
happens asynchronously. With potentially poorer connections, the DOM
might already be mounted while the async call is still in-flight. This could
have the user see the card in its blank slate temporarily.
<https://bit.ly/2sfiK2U>

To make a quick change to alert the user that data might still be in the
process of being fetched, we can initialize our instance data properties with
'Loading…' values instead of a blank string:

new Vue({
el: '#app',
data: {
name: 'Loading...',
email: 'Loading...',
company: {
name: 'Loading...',
catchPhrase: 'Loading...',
}
},
created() {
// ...
},
});

The user would now recognize that the app is “loading” if the data hasn’t
yet been made available.
<https://bit.ly/2QqCmuP>

At the final state, the app will render the card element with information
about a certain user.

Clementina DuBuque

Hoeger LLC

"Centralized empowering task-force"

Rey.Padberg@karina.biz

Live version - https://30dofv-created.surge.sh


<https://30dofv-created.surge.sh>

We should also always have an appropriate error case in our request to


notify the user that something’s wrong when our call ever fails. We won’t be
doing that here, but it might be a good exercise for you to try!

MOUNTED
The mounted() hook is run after the instance has been mounted and where
the rendered DOM can be accessed.

What does mounting mean?

Vue tracks and makes changes to a virtual representation of


nodes in the DOM tree before patching those changes on to
the actual DOM. This in-memory view that Vue maintains and
manages for us is often known as the Virtual DOM.

Vue uses the virtual DOM to maintain/manage and track the


changes in an application in a “less-expensive” way than
directly tracking the changes being made on the actual DOM.

When we talk about mounting, we're talking about the


process of converting the virtual elements into actual DOM
elements that are placed in the DOM by Vue.

In the mounted() hook of an instance, we’re able to access the rendered


DOM with this.$el .
new Vue({
el: '#app',
data: {
// ...
},
created() {
// ...
},
mounted() {
console.log(this.$el);
}
});

By console logging the rendered DOM in our card application, we’ll be able
to read the information of the user that’s being rendered in our application.
Since we’re able to access the resolved DOM, the mounted() hook is often
best used when DOM dependant changes need to be made (i.e. when you
need access to the DOM to make certain changes).

UPDATED
In article #3 of the course, we discussed how Vue applications are reactive
in nature. In short, when data is changed - the template is re-rendered (i.e.
updated) to show the change. The updated() hook gets fired whenever a
data change is made that causes the instance to be updated/re-rendered.

The updated() hook behaves like the watch property but for
the entire instance. It’s important to know that for specific state
changes, the watch property (or oftentimes computed
properties) should always be used instead.

In the updated() hook, the accessible DOM refers to the resolved DOM
after the update has been made.
new Vue({
el: '#app',
data: {
// ...
},
created() {
// instance has been created
},
mounted() {
// instance has been mounted
},
updated() {
// instance has been updated

console.log(this.$el) // DOM after update is complete


}
});

DESTROYED
The destroyed() hook is fired after an instance has been fully destroyed
(which can be done with the vm.$destroy() <https://vuejs.org/v2/api
/#vm-destroy> method). This is the last hook that’s fired in the lifecycle and
at this moment the instance event listeners, child instances and directives
are all removed.
new Vue({
el: '#app',
data: {
// ...
},
created() {
// instance has been created
},
mounted() {
// instance has been mounted
},
updated() {
// instance has been updated
},
destroyed() {
// instance had been destroyed
}
});

Though we may not find ourselves using the destroyed() hook often, we’re
able to use the hook to perform any last minute changes before our
instance is ever completely removed.

BEFORE HOOKS
For each of the standard lifecycle hooks ( created() , mounted() ,
updated() , and destroyed() ), Vue also provides hooks that run just before
these lifecycle events have occurred. These before hooks are useful if we
want to run some functionality before the intended lifecycle change has
been made. Here’s a simple diagram that shows each of the methods and
the stages of the lifecycle they represent:
The Vue Documentation has a very well laid out lifecycle diagram that
addresses, in more detail, what happens as the instance goes through each
stage of its lifecycle - Instance Lifecycle Diagram <https://vuejs.org
/v2/guide/instance.html#Lifecycle-Diagram> .

These are all the lifecycle hooks we can interact with in the Vue
framework. We'll often find ourselves using these as we build our Vue apps,
so it's a good idea to be familiar with them, that they exist, and how to hook
into the life of an instance.

In the next section, we’ll be discussing a topic that we’ve deliberately


avoided until now. We’ll be discussing Vue Components and their role in
building Vue applications!
30 Days of Vue

VUE COMPONENTS -
GLOBAL
COMPONENTS
VUE COMPONENTS - GLOBAL
COMPONENTS
In today's session, we'll be building our first global Vue
component.

In the last week and a half, we’ve covered a large number of properties and
features within a Vue instance. We’ve discussed an instance’s reactivity, its
directives, methods, computed properties, watchers, and lifecycle hooks.
For those who might have used other newer front end
libraries/frameworks, you may have noticed we haven’t discussed a vital
piece of modern UI development - components.

COMPONENTS
Vue provides the ability for us to create isolated components within an
application. Reusability and maintainability are some of the main reasons
as to why building an application with well-structured components are
especially important.

Vue components are intended to be self-contained modules since we can


group markup (HTML), logic (JS), and even styles (CSS) within them. This
allows for easier maintenance, especially when applications grow much
larger in scale.

An important note to keep in mind is that Vue components are Vue


instances. This means almost all the properties we’ve seen thus far (except
for a few root-level options) in a root instance are applicable to
components as well. In fact, the Vue documentation <https://vuejs.org
/v2/guide/components.html> states that “[Vue] components are reusable
Vue instances with a name…”.

To get a better understanding of components, we’ll go ahead and create


one.

SIMPLE TWITTER APP


By the end of tomorrow's article, we’ll look to have created a mock Twitter
application that displays a list of tweets from a data source.

The data source will be available to us on the client-side and passed into
the data property of the application instance:
const tweets = [
{
id: 1,
name: 'James',
handle: '@jokerjames',
img: './images/james.png',
tweet: "If you don't succeed, dust yourself off...",
likes: 10,
},
{
id: 2,
name: 'Fatima',
handle: '@fantasticfatima',
img: './images/fatima.png',
tweet: 'Better late than never but never late...',
likes: 12,
},
{
id: 3,
name: 'Xin',
handle: '@xeroxin',
img: './images/xin.png',
tweet: 'Beauty in the struggle...',
likes: 18,
}
];

new Vue({
el: '#app',
data: {
tweets
}
});
By binding the information of just the first tweet object on to the template,
our HTML will look something like the following:
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
<link rel="stylesheet"
href="
https://use.fontawesome.com/releases/v5.6.1/css/all.css
" />
</head>

<body>
<div id="app">
<div class="tweet">
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-64x64">
<img :src="tweets[0].img">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<strong>{{tweets[0].name}}</strong>
<small>{{tweets[0].handle}}</small>
<br>
{{tweets[0].tweet}}
</p>
</div>
<div class="level-left">
<a class="level-item">
<span class="icon is-small">
<i class="fas fa-heart"></i>
</span>
<span class="likes">
{{tweets[0].likes}}
</span>
</a>
</div>
</div>
</article>
</div>
<div class="control has-icons-left">
<input class="input is-small"
placeholder="Tweet your reply..." />
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</div>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

We've introduced the Font Awesome


<http://fontawesome.io/> library for icons and are referencing
images that are kept within the images folder of our project.

With the appropriate styles already prepared for us, our application will
now look like this:
Our aim is to show a tweet element for every single tweet object available
in our data. Since we’ll be rendering a list of elements, the best way to
achieve this is with the help of the v-for directive:
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
<link rel="stylesheet"
href="
https://use.fontawesome.com/releases/v5.6.1/css/all.css
" />
</head>

<body>
<div id="app">
<div class="tweet"
v-for="tweet in tweets"
:key="tweet.id">
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-64x64">
<img :src="tweet.img">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<strong>{{tweet.name}}</strong>
<small>{{tweet.handle}}</small>
<br>
{{tweet.tweet}}
</p>
</div>
<div class="level-left">
<a class="level-item">
<span class="icon is-small">
<i class="fas fa-heart"></i>
</span>
<span class="likes">
{{tweet.likes}}
</span>
</a>
</div>
</div>
</article>
</div>
<div class="control has-icons-left">
<input class="input is-small"
placeholder="Tweet your reply..." />
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</div>
</div>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

In the template above, we’re binding the contents of the iterated tweet
object onto our template. This renders a list of tweet elements with each
element containing details of a single tweet object:
GLOBAL COMPONENTS
If we take a look at the UI we've set up, we can distinctively point out the
pieces of our application that could be made to be self contained modules
of their own.

The Root Instance resembles the entire root instance of our application
while tweet-component could be the instance that isolates the markup
responsible for a single tweet element.
Let's go ahead and create this tweet-component. The simplest method for
creating a component is using the Vue.component() constructor.

Vue.component('tweet-component', {
// options
});

The Vue.component() constructor registers a component globally in an


application. In the constructor above, the first argument we've passed in is
the name (i.e. the identifier) of the component - tweet-component. In the
second argument, we've passed in an empty options object that will
contain the definition of the component such as its data, methods, etc.

Though there are a few different ways to declare the template of a


component, the standard way of doing so is using the template option
which expects a string value.

We haven’t found the need to use the template option in our


root instance since we were able to use the root-level option,
el <https://vuejs.org/v2/api/#vm-el> , to declare the view our
instance will depend upon.

To get things started, we’ll create a tweet-component with hard-coded


data. We’ll specify the template of the component to simply be the markup
associated with <div class="tweet">...</div> . We'll create this
component right before the instantiation of our root instance.
Vue.component('tweet-component', {
template: `
<div class="tweet">
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-64x64">
<img src="./images/james.png">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<strong>James</strong>
<small>@jokerjames</small>
<br>
If you don't succeed, dust yourself off...
</p>
</div>
<div class="level-left">
<a class="level-item">
<span class="icon is-small">
<i class="fas fa-heart"></i>
</span>
<span class="likes">
10
</span>
</a>
</div>
</div>
</article>
</div>
<div class="control has-icons-left">
<input class="input is-small"
placeholder="Tweet your reply..." />
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</div>
</div>
`
});

We’ve declared the template of the component within backticks (i.e. ES6
template literals <https://developer.mozilla.org/en-US/docs
/Web/JavaScript/Reference/Template_literals> ) to be able to neatly
arrange the markup in multi-line format.

Template literals are an unsupported feature for older browsers


like IE11 <https://developer.mozilla.org/en-US/docs
/Web/JavaScript/Reference
/Template_literals#Browser_compatibility> .

With the component created, we’ll now be able to render the component in
the root template. We’ll want the component to be rendered for every tweet
in the tweets array. Since we want to render a list of tweet-component's,
we'll declare the v-for directive where the component is being rendered.
Removing the old template code and rendering a list of tweet-
component's would have our root template be updated to the following:
src/global-components-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
<link rel="stylesheet"
href="
https://use.fontawesome.com/releases/v5.6.1/css/all.css
" />
</head>

<body>
<div id="app">
<tweet-component
v-for="tweet in tweets" :key="tweet.id">
</tweet-component>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

At this moment, our application would look like the following:


James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

Live version - https://30dofv-globalcomponents.surge.sh


<https://30dofv-globalcomponents.surge.sh>
Notice how the root template is a lot easier to read now? This
is in part due to using components to encapsulate relevant
content within themselves. This is a very simple application,
but for larger apps - the importance of well structured and
named components can’t be stressed enough.

Though we’re rendering a list of components, the application isn’t where we


want it to be since each component is rendering the same static
information. What we intend to do is have the template of every tweet-
component render the details of a single unique tweet object from the
tweets array.

The tweets data array is part of the root instance and tweet-component
is generally unaware of its existence. Since tweet-component is rendered
as a child of the root instance, we can use something known as props to
pass the relevant data down to the component.

This is where we'll be picking up tomorrow. In the next article, we’ll spend a
little time discussing what props are before updating our app to use props
to have our components contain the data they need.
30 Days of Vue

VUE COMPONENTS -
PROPS
VUE COMPONENTS - PROPS
Today, we'll discuss how props can help us pass data from
parent components down to child components.

In yesterday’s article, we created our first component (named tweet-


component ) as part of a mock twitter application. We went as far as having a
list of these tweet-component ‘s be rendered with only static information.

James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...


Live version - https://30dofv-globalcomponents.surge.sh
<https://30dofv-globalcomponents.surge.sh>

Our aim today is to have each tweet-component render unique tweet data
from the tweets data array kept in the root instance. We’ll achieve this with
the help of props <https://vuejs.org/v2/guide/components-props.html> .

PROPS
Vue gives us the ability to use props to pass data from parent instances
down to child instances.

Using props is fairly simple. The first step involves binding the data that is
to be passed to the prop attribute where the child component is being
rendered.

In our mock twitter application, we’re interested in passing a single tweet


object to every tweet-component that is rendered. Since the tweet
components are rendered with the help of the v-for directive, we can pass
the iterated tweet object as props down to each component.

src/props-example/index.html

<div id="app">
<tweet-component v-for="tweet in tweets"
:key="tweet.id"
:tweet="tweet">
</tweet-component>
</div>

We’re using the v-bind directive to bind the iterated tweet data object to a
prop of the same name.

For tweet-component to use the prop provided to it, it needs to explicitly


declare the props it receives with the props option. We’ll introduce a
props option in tweet-component and specify the tweet prop being
passed in:

Vue.component('tweet-component', {
template: `
// ...
`,
props: ['tweet']
});

In tweet-component , the tweet prop can now be accessed and used like
any other data property. We’ll bind information from the tweet prop on to
the template of the component instead of statically rendering information.
Vue.component('tweet-component', {
template: `
<div class="tweet">
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-64x64">
<img :src="tweet.img">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<strong>{{tweet.name}}</strong>
<small>{{tweet.handle}}</small>
<br>
{{tweet.tweet}}
</p>
</div>
<div class="level-left">
<a class="level-item">
<span class="icon is-small">
<i class="fas fa-heart"></i>
</span>
<span class="likes">
{{tweet.likes}}
</span>
</a>
</div>
</div>
</article>
</div>
<div class="control has-icons-left">
<input class="input is-small"
placeholder="Tweet your reply..." />
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</div>
</div>
`,
props: ['tweet']
});

Our application will now display information from all the different tweets in
our instance data.
James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

Fatima @fantasticfatima
Better late than never but never late is better.

♥ 12

" Tweet your reply...

Xin @xeroxin
Beauty in the struggle, ugliness in the success.

♥ 18

" Tweet your reply...

Live version - https://30dofv-props.surge.sh <https://30dofv-


props.surge.sh>

PROP VALIDATION
Vue also allows us to define requirements for props that get passed into a
component. Earlier, we declared the tweet prop in tweet-component in
this format:
Vue.component('tweet-component', {
template: `
// ...
`,
props: ['tweet']
});

To define prop validation requirements, we can instead provide an object


that has validation requirements to the props option.

Vue.component('tweet-component', {
template: `
// ...
`,
props: {
tweet: {
type: Object,
required: true
}
}
});

If we've stated validation requirements like the above and find ourselves
not passing the tweet prop or having the tweet prop be of a type that is
not Object , the Vue console will emit warnings. The Vue documentation
<https://vuejs.org/v2/guide/components-props.html#Prop-Validation> has
more examples on how we’re able to define default and custom validation
requirements as well.

PARENT - CHILD RELATIONSHIPS


Since every component has its own isolated scope, child components can
never (and should never) reference data directly from parent components.
For a child component to access data from a parent, data has to flow from
the parent down to the child with the help of props. This design greatly
simplifies the understanding of an applications data flow since child
components will never be able to mutate parent state directly.

Props can also be passed down multiple levels deep. For our mock twitter
application, assume we wanted tweet-component to be a parent of another
component labelled tweet-content .

We can use the Vue.component() constructor to create the tweet-content


component as we’ve done before. We’ll also declare a tweet prop in the
props property of tweet-content and bind the prop’s information to its
template. We'll create this tweet-content component right above the
creation of tweet-component .
src/nested-components-example/main.js

Vue.component('tweet-content', {
template: `
<div class="media-content">
<div class="content">
<p>
<strong>{{tweet.name}}</strong>
<small>{{tweet.handle}}</small>
<br>
{{tweet.tweet}}
</p>
</div>
<div class="level-left">
<a class="level-item">
<span class="icon is-small">
<i class="fas fa-heart"></i>
</span>
<span class="likes">{{tweet.likes}}</span>
</a>
</div>
</div>
`,
props: ['tweet']
});

tweet-component will now be able to render the tweet-content


component and pass in the tweet data object as props:
src/nested-components-example/main.js

Vue.component('tweet-component', {
template: `
<div class="tweet">
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-64x64">
<img :src="tweet.img">
</figure>
</div>
<tweet-content :tweet="tweet"></tweet-content>
</article>
</div>
<div class="control has-icons-left">
<input class="input is-small"
placeholder="Tweet your reply..." />
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</div>
</div>
`,
props: ['tweet']
});

Our application UI will remain the same but now be composed of two
nested components.
James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10

" Tweet your reply...

Fatima @fantasticfatima
Better late than never but never late is better.

♥ 12

" Tweet your reply...

Xin @xeroxin
Beauty in the struggle, ugliness in the success.

♥ 18

" Tweet your reply...

Live version - https://30dofv-nestcomponents.surge.sh


<https://30dofv-nestcomponents.surge.sh>

The Vue Devtools is incredibly useful in helping debug our application and
inspect what data is being passed from one component to another. By
launching the Vue Devtools, we’ll be able to see all the components in our
application and the props available within each component.
It’s important to remember that props can only travel in a unidirectional
format (parent to child to grandchild, etc.). If we wanted to communicate to
a parent component (e.g. the root instance) about changes made in a child
component (e.g. tweet-component ), Vue provides something known as
Custom Events to help facilitate this.

Good work so far! We’ll stop here for today and begin tomorrow by
discussing Custom Events and how to use them!
30 Days of Vue

VUE COMPONENTS -
CUSTOM EVENTS
VUE COMPONENTS - CUSTOM
EVENTS
Yesterday, we saw how props can be used to pass data
downwards from parent to child. Today, we'll see how custom
events can facilitate communication in the opposite direction
- from child to parent.

In the last two articles - we introduced components, seen how they help
make our Vue applications more modular and taken a look at how props
help pass data from parent to child components.

What if we needed to find a way to communicate information in the


opposite direction (i.e. have the child component notify the parent about
something)?

At the last stage of building our mock twitter app, we had the root instance
render a tweet-component which subsequently rendered a tweet-content
component of its own. Props were used to pass the relevant tweet data
object from the root instance downwards to tweet-component and further
down to tweet-content .
Assume we wanted to introduce a new link (or button) to the tweet
element.

Upon click of the new link, let's attempt to re-introduce another copy of the
same tweet element into the bottom of the list of tweets. Though this might
be a strange use case, it'll be a useful way to show how we intend to
communicate information from the child up to the parent.
The link that adds a new element upon click will be part of the tweet-
content component while the data ( tweets array) that controls the tweet
elements in our list is part of the root instance.

To add a new tweet to the list of tweets, we'll essentially need to push a
new tweet object into the tweets array. When the user clicks the link to
add another tweet, we'll need to have some sort of event propagated up to
the root instance to change the data kept in the instance. We can’t use
props for this since props can only be used to pass data downwards
(parent to child to grandchild). Since we want to create communication in
the opposite direction, we can use something known as Vue Custom
Events <https://vuejs.org/v2/guide/components-custom-events.html> .

CUSTOM EVENTS
Custom events in Vue behave very similar to native JavaScript custom
events <https://developer.mozilla.org/en-US/docs/Web/Guide/Events
/Creating_and_triggering_events> but with one key distinction - Vue
custom events are used primarily for communication between
components as opposed to communication between DOM nodes.

Vue custom events are triggered using $emit while specifying the name of
the custom event:
this.$emit('name-of-event');

The $emit function can have a second optional argument that allows the
caller to pass arbitrary values along with the emitted event:

this.$emit('name-of-event', {
data: {
course: '30 Days of Vue';
}
});

The this keyword is a reference to the instance in which the


event is being triggered. When declaring an event within the
template of a component, the this keyword can be omitted.

In our mock twitter application, let’s update the template of tweet-content


to contain the link that represents the ability to re-add the same tweet
element to the bottom of the list. We’ll attach a click event listener on the
link that when triggered will emit a custom event with the name of add . In
the custom event declaration, we’ll also pass the tweet prop object as the
event payload.
Vue.component('tweet-content', {
template: `
<div class="media-content">
<div class="content">
// ...
</div>
<div class="level-left">
// ...
<a class="level-item"
@click="$emit('add', tweet)">
<span class="icon is-small">
<i class="fas fa-plus"></i>
</span>
</a>
</div>
</div>
`,
props: ['tweet']
});

Upon click of the newly added link, the add event will get propagated one
level up to tweet-component . For parent components to be able to listen to
events emitted by children, the v-on directive can be used to create a
custom event listener. That custom event listener needs to be declared
where the child component is being rendered.

Since tweet-component doesn’t have access to the tweets data array that
we want to update, we’ll need to propagate the event one level higher. To
achieve this, we’ll create a custom event listener in tweet-component to
listen for when the add event is triggered in tweet-content . When the
event is triggered, we’ll trigger another add event and pass in the event
object (i.e. the tweet object payload). This updates the template of tweet-
component to the following:
Vue.component('tweet-component', {
template: `
<div class="tweet">
<div class="box">
<article class="media">
// ...
<tweet-content :tweet="tweet"
@add="$emit('add', $event)">
</tweet-content>
</article>
</div>
</div>
`,
props: ['tweet']
});

In the event listener, we’re able to access the event object that has been
passed with $event .

In the root template, we can now create the final listener. We’ll specify the
event listener where tweet-component is being rendered and when
triggered call a method labelled addTweetMessage . addTweetMessage will
pass in the $event payload as the only argument to the method.

src/custom-events-example/index.html

<tweet-component v-for="tweet in tweets"


:key="tweet.id"
:tweet="tweet"
@add="addTweetMessage($event)">
</tweet-component>

We’ll now create the addTweetMessage method in the root instance to


introduce a copy of the event tweet object into our tweets array. To keep
each tweet object unique, we’ll shallow copy the tweet event object and set
the id of the copied object to be one greater than the last item in our
tweets array. We’ll then push the newly copied item to the end of the data
array with the help of the Array .push() <https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/push>
method.

src/custom-events-example/main.js

new Vue({
el: '#app',
data: {
tweets
},
methods: {
addTweetMessage(tweet) {
let newTweet = {};
let lastTweetObjectID =
this.tweets[this.tweets.length - 1].id;

// shallow copy tweet object


newTweet = Object.assign({}, tweet);

// set tweet id to be one greater than last tweet


newTweet.id = lastTweetObjectID + 1;

this.tweets.push(newTweet);
}
}
});

Awesome! We’ll now be able to add copied tweet elements to the end of
the list by clicking the add icon on any tweet element.
James @jokerjames
If you don't succeed, dust yourself off and try again.

♥ 10 +

" Tweet your reply...

Fatima @fantasticfatima
Better late than never but never late is better.

♥ 12 +

" Tweet your reply...

Xin @xeroxin
Beauty in the struggle, ugliness in the success.

♥ 18 +

" Tweet your reply...

Live version - https://30dofv-customevents.surge.sh


<https://30dofv-customevents.surge.sh>

CUSTOM EVENTS & DATA


Let’s recap how data was manipulated in the app we just built. The root
instance contains the tweets array and uses this array to render a list of
tweet-component 's. For every tweet-component rendered, a unique tweet
object prop is passed in which is then subsequently passed in to a tweet-
content component. In each of the components, we bind the relevant
information from the available tweet prop on to their respective templates.

When the user clicks the icon to add another tweet element, the add event
is triggered in tweet-content and listened for in tweet-component . The
event listener in tweet-component triggers another add event that’s being
listened for in the root instance. With every event trigger, the tweet object
payload is being passed through.
When the root instance event listener is triggered, the tweet object is
shallow copied and added to the tweets array. In the root template, we use
the v-for directive to render a list of tweet elements from the tweets
array. As a result, when the tweets array is updated - our application is re-
rendered to show the newly added element.

The way we've built our application isn't the only way to achieve what we've
just done. In fact, the process of using just props and custom events to
handle data gets more complicated when we need to think about not only
parent-child relationships but sibling-sibling components as well. What
we’re starting to discuss here is the basis of application wide data
management or in other words state management. We’ll be taking a
deeper look into different state management processes soon (article #19 to
be exact) but in the coming articles, we'll be spending more time
discussing Vue components.

The main takeway from this article is that props are always used to pass
data downwards while custom events can be used to send information
upwards.
30 Days of Vue

VUE COMPONENTS -
NICE TO KNOWS
VUE COMPONENTS - NICE TO
KNOWS
Today, we'll cover some interesting points that govern the use
of Vue components such as the ability to locally register
components, the requirement to having the component data
property always be a function, and the restriction to using a
single root template.

In the last couple of articles, we've been introduced to how Vue


components help make our Vue applications more modular, taken a look at
how props help pass data from parent to child components, and discussed
how custom events can be used to facilitate communication from child
components upwards to parent instances.

In this article, we’ll be addressing a few nice-to-knows that we haven't


really discussed yet when it comes to using simple standard components.

GLOBAL VS. LOCAL REGISTRATION


So far, we've been using the Vue.component() constructor to create
components in our applications. Vue.component() registers the component
globally since the component is accessible in any Vue instance that has
been instantiated after this constructor.
Vue.component('global-component', {
template: `<p>Hello World!</p>`
});

new Vue({
template: `
<div>
<global-component></global-component>
</div>
`
});

Instead of having components registered globally, we’re also able to


register components locally within a particular instance. We can achieve
this by first assigning the options object of a component to a plain
JavaScript object.

const localComponent = {
template: `<p>Hello World!</p>`
}

In the sample above, we've created a component options object with the
name of localComponent . To have localComponent be registered locally
within a parent instance, we'll have to declare localComponent in the
components property of the instance we’d want the component registered
in.

The components property accepts key-value pairs with the key referring to
how the component is to be named in the instance, and the value being the
options object of the component.

If we wanted to register localComponent locally in an instance and have it


be declared as local-component in the template, we’ll register it like the
following:

const localComponent = {
template: `<p>Hello World!</p>`
}

new Vue({
template: `
<div>
<local-component></local-component>
</div>
`,
components: {
'local-component': localComponent
}
});

Local registration is useful since it helps us avoid having to declare all


components globally and instead have components encapsulated in the
instances that they intend to be rendered in.

DATA AS A FUNCTION
Since Vue components are Vue instances, they have almost all the same
functionality as a root level instance does. One differentiation to keep in
mind is that the data property of a Vue component should always be a
function.

Traditionally, we’ve defined data as a standard property object:


new Vue({
el: '#app',
data: {
message: 'Greetings!',
}
});

For components, data should always be a function that returns an


object.

let componentObject = {
template: '<p>{{message}}</p>',
data() {
return {
message: 'Greetings!'
}
}
}

The reason behind this is that Vue doesn’t recognize the difference
between data objects used in different instances and as a result treats them
all as the same single data object. If you try declaring a standard data object
in a component, Vue will emit a console warning along the lines of:

[Vue warn]: The "data" option should be a function...

By having data in components be functions that return objects, each


component is able to maintain its own independent data object. Here's an
adapted example from the Vue documentation <https://vuejs.org/v2/guide
/components.html#data-Must-Be-a-Function> that displays three identical
components with each having their own independent data object.
JS
src/components-data-example/main.js
let counterOne = {
template: `
<button class="component-button" @click="counter++">
You clicked me {{counter}} times
</button>
`,
data() {
return {
counter: 0
}
}
}

let counterTwo = {
template: `
<button class="component-button" @click="counter++">
You clicked me {{counter}} times
</button>
`,
data() {
return {
counter: 0
}
}
}

let counterThree = {
template: `
<button class="component-button" @click="counter++">
You clicked me {{counter}} times
</button>
`,
data() {
return {
counter: 0
}
}
}

new Vue({
el: '#app',
data: {
message: 'Greetings!',
},
components: {
'counter-one': counterOne,
'counter-two': counterTwo,
'counter-three': counterThree,
}
});

HTML
src/components-data-example/index.html

<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet"
href="https://unpkg.com/bulma/css/bulma.css" />
</head>

<body>
<div id="app">
<p>Three different components.</p>
<counter-one></counter-one>
<counter-two></counter-two>
<counter-three></counter-three>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./main.js"></script>
</body>
</html>

Three different components.

You clicked me 0 times You clicked me 0 times You clicked me 0 times

Live version - https://30dofv-componentsdata.surge.sh


<https://30dofv-componentsdata.surge.sh>

SINGLE ROOT TEMPLATE


With Vue template declarations, it’s a must to wrap the template of
components in a single root element. If we attempted to have the template
of a component render two sibling elements like below:

const localComponent = {
template: `
<p>Hello World!</p>
<button>Click Here!</button>
`
}

The Vue console will emit a warning referencing the incorrect template and
stating:

Component template should contain exactly one root element..

Instead, we are expected to wrap our component templates in a single root


element. For example, we can have the elements shown in the example
above be kept within a single parent <div> element:

const localComponent = {
template: `
<div>
<p>Hello World!</p>
<button>Click Here!</button>
</div>
`
}

This restriction is due to the technical constraints of Vue's diff algorithm


<https://github.com/vuejs/vue/issues/7088#issuecomment-348252040>
(i.e. algorithm on how changes are patched and implemented on the actual
DOM). If you're interested in reading more about this, here's an interesting
issue that was opened <https://github.com/vuejs/vue/issues/7088> , in
Nov 2017, on the Vue core library that discussed this constraint.

Today's article was a brief discussion on some nice-to-knows when it


comes to working with Vue components. In tomorrow’s article, we’ll look at
some of the different ways we’re able to declare the template of a
component. Good job getting through week two and see you tomorrow!
30 Days of Vue

VUE COMPONENTS -
TEMPLATE
DEFINITIONS
VUE COMPONENTS - TEMPLATE
DEFINITIONS
Today, we'll be taking a look at some of the different ways we
can declare the template/markup of a component.

For all the components we’ve seen so far, we’ve used either standard
strings or template literals to define the component template. In this article,
we'll summarize why we use either strings or template literals before
investigating some other ways of defining the template of a component.

COMPONENT TEMPLATES
TEMPLATE STRINGS
The template option of a component expects a string, so we’re able to
define the entire markup of a component within standard strings.

Here's an example of the root instance template rendering a single-line-


template component while passing in a message prop.

src/standard-strings-template/index.html

<div id="app">
<single-line-template :message="message">
</single-line-template>
</div>
The template of the single-line-template component is kept within
standard strings.

src/standard-strings-template/main.js

let singleLineTemplate = {
template: '<div class="card"><header class="card-header\
card-header-title">{{ message }}</header></div>',
props: ['message']
}

new Vue({
el: '#app',
data: {
message: 'Greetings!',
},
components: {
'single-line-template': singleLineTemplate
}
});

Greetings!

Live version - https://30dofv-singlestringtemp.surge.sh


<https://30dofv-singlestringtemp.surge.sh>

Standard strings (i.e. ‘ ‘ ) in JavaScript expect the string to be defined in a


single line which can make reading the markup of a component difficult. If
we wanted to break our component template into multi-line format - we
can take advantage of ES6 Template Literals <https://developer.mozilla.org
/en-US/docs/Web/JavaScript/Reference/Template_literals> (i.e. back-
ticks).

let multiLineTemplate = {
template: `
<div class="card">
<header class="card-header card-header-title">
{{ message }}
</header>
</div>
`,
props: ['message']
}

Template literals is an unsupported ES6 feature in older


browsers like IE11.

In addition to defining the template in strings, the Vue documentation


<https://vuejs.org/v2/guide/components-edge-cases.html#Alternate-
Template-Definitions> highlights two other alternative definitions - inline
templates and x-templates.

INLINE TEMPLATES
Inline templates allow us to simply regard the inner content of a declared
component as its template. This is achieved by using the special inline-
template keyword on the component.

To see an example of this, we'll first declare a local inlineTemp component


object that expects a message prop.

In the root template, we can render the inline-temp component and pass
in the message data as props.

<div id="app">
<inline-temp :message="message"></inline-temp>
</div>

As of this moment, the inline-temp component has no template and


nothing will be shown. We can use the inline-template keyword and
declare the template of the component as the inner content between the
component opening and closing tags.

src/inline-template/index.html

<inline-temp :message="message" inline-template>


<div class="card">
<header class="card-header card-header-title">
{{ message }}
</header>
</div>
</inline-temp>

Greetings!

Live version - https://30dofv-inlinetemplates.surge.sh


<https://30dofv-inlinetemplates.surge.sh>

The Vue documentation <https://vuejs.org/v2/guide/components-edge-


cases.html#Inline-Templates> states that inline templates make it harder to
understand the template of a component and as a best practice, should
usually not be used.

X TEMPLATES
With x-templates, we’re able to use <script> tags to define pockets of
markup that represent the templates of components. To recreate the same
example above with x-templates, we'll instantiate a xTemp component
object with the template property given a value of #x-template-component .
We can have the component registered in the root instance as well.

src/x-template/main.js

let xTemp = {
template: '#x-template-component',
props: ['message']
}

new Vue({
el: '#app',
data: {
message: 'Greetings!',
},
components: {
'x-temp': xTemp
}
});

In the root template, we can first render the x-temp component and pass
in the expected message prop.

<div id="app">
<x-temp :message="message"></x-temp>
</div>
To specify the template of the x-temp component, we can write a separate
<script></script> tag in the root markup that has type="text/x-
template" and id"=x-template-component" . The x-template-component id
is the value of the template option in the xTemp component object. The
template of the component will then be the inner contents of this script tag.

src/x-template/index.html

<div id="app">
<x-temp :message="message"></x-temp>

<script
type="text/x-template"
id="x-template-component">
<div class="card">
<header class="card-header card-header-title">
{{ message }}
</header>
</div>
</script>
</div>

Greetings!

Live version - https://30dofv-xtemplates.surge.sh


<https://30dofv-xtemplates.surge.sh>

Though x-templates may seem to be a pretty neat feature, there are


significant downsides to having the template of a component completely
separate from the actual component definition. For this reason, the Vue
documentation <https://vuejs.org/v2/guide/components-edge-
cases.html#X-Templates> states that x-templates should be avoided in
most cases.

SHORTCOMINGS AND RENDER FUNCTIONS


From what we've gathered in this article, template strings are a more viable
approach than using inline templates or x-templates. Though template
strings work fairly well, there are some shortcomings that get more
noticeable as our components start to become more complicated.

The first and probably more obvious shortcoming is that the template
property of a component expects the markup of a component to be kept
within a string. For simple templates, this works fine but as components
become larger; having no syntax highlighting and the entire markup of a
component kept within back-ticks (or strings) makes the template of
components hard to read.

The components we’ve created so far have done a good job in isolating
markup (HTML) and logic (JS). Wouldn’t it also be nice if we could isolate
the CSS a component contains to be within the component itself?

This is where one of Vue’s most useful features comes in - Single File
Components. Before we dive into Single File Components, we'll be taking a
quick detour tomorrow and discussing render functions - functions that
allow us to create the markup of components entirely with JavaScript.
30 Days of Vue

RENDER FUNCTIONS
AND JSX
RENDER FUNCTIONS AND JSX
We took a look at the different types of component templates
in yesterday's article. Today, we'll look to use a render function
to create the markup of a component entirely with JavaScript.

From what we’ve seen in yesterday's article, it’s probably safe to say that
creating the markup for Vue components (or instances) is a fairly
straightforward process. We’ve seen some alternate template definitions
like inline-templates and x-templates , but as a best practice, it’s best to
stick with using an instance template option for simple
components/instances.

We did mention that there’s another way to have the templates


of our components be defined (hint: Single-File
Components). We’ll be taking a small detour today before
diving into SFC’s tomorrow!

Vue, at build time, takes the templates we create for our


instances/components and compiles them to something known as render
<https://vuejs.org/v2/guide/render-function.html#Basics> functions. It’s at
these compiled render functions, where Vue builds a virtual
representation of nodes that make up the virtual DOM.
As mentioned in an earlier article, Vue operates not directly on
the browser’s Document Object Model (DOM) immediately,
but on a virtual DOM. Vue uses the virtual DOM to
maintain/manage and track the changes in our application in a
“less-expensive” way (i.e. less expensive than immediately
tracking the changes being made on the actual DOM).

Although Vue recommends for us to use templates to construct the markup


of our instances in most cases, we’re also given the opportunity to directly
use render functions to build the markup of our instances as well! By using
render functions, we skip the compile step that Vue takes to compile our
templates down.

We’ll spend some time in this article taking a look at these render
functions and how we can use them to construct the markup of the same
element we've built in the last article.

RENDER FUNCTIONS
In the last article, we discussed different template techniques to construct a
simple card element that accepted a message prop.

Greetings!

Live version - https://30dofv-singlestringtemp.surge.sh


<https://30dofv-singlestringtemp.surge.sh>
The markup of the card element we’ve created was pretty straightforward
and contained a <div> element encompassing a <header> element. The
text content of the <header> element displayed the value of the message
prop.

<div class="render-card">
<header class="card-header card-header-title">
{{ message }}
</header>
</div>

We’ll recreate the above markup step by step with the help of a render
function that’s available as a property in every instance.

let renderComponent = {
render() {
// render function
},
props: ['message']
}

render functions in Vue always receive a createElement function as an


argument.

let renderComponent = {
render(createElement) {
// render function
},
props: ['message']
}

The createElement function is able to create the “virtual” representation of


the DOM nodes that Vue uses to track and subsequently render on the
page. The createElement function takes three arguments of its own
<https://vuejs.org/v2/guide/render-function.html#createElement-
Arguments> :

1. An HTML tag name (or a component options object).


2. A data object that corresponds to the attributes to be added to the
HTML template (event listeners, class attributes, etc.).
3. Child nodes of the parent node.

The HTML tag name for the parent node we want to construct is a div
element. We'll return the createElement function and pass in a string of
value 'div' as the first argument:

let renderComponent = {
render(createElement) {
return createElement('div');
},
props: ['message']
}

Since we’re interested in applying a card CSS class to the parent div
element, we’ll declare the data object in the second argument of the
createElement function to have an attrs property. In attrs , we'll specify
a class key that has a string value of 'render-card' :
let renderComponent = {
render(createElement) {
return createElement(
'div', {
'attrs': {
class: 'render-card'
},
}
);
},
props: ['message']
}

Though we won’t be doing much more, there are numerous different ways
of defining attributes with the second argument data object. If you’re
interested, be sure to check out the Vue documentation <https://vuejs.org
/v2/guide/render-function.html#The-Data-Object-In-Depth> for a good
summary.

To mimic the card we've built in the last article, the parent <div> element
is to have a child <header> element of its own. In the third argument of the
createElement function, we’re able to either specify a simple string to
render text or an array to render more createElement functions (i.e. more
elements). Since we’ll be rendering another generated element as the child,
we’ll declare the createElement function within the child nodes array and
give it a string value of 'header' :
let renderComponent = {
render(createElement) {
return createElement(
'div', {
'attrs': {
class: 'render-card'
},
}, [
createElement('header')
]
);
},
props: ['message']
}

The header child element is to have classes of its own so we’ll pass in an
attributes object in the nested createElement function to declare the
classes the header element should have:
let renderComponent = {
render(createElement) {
return createElement(
'div', {
'attrs': {
class: 'render-card'
},
}, [
createElement('header', {
'attrs': {
class: 'card-header card-header-title',
}
})
]
);
},
props: ['message']
}

Thankfully, the child header element is to contain no child elements of its


own and instead simply display the value of the message prop. To have the
header element display the message prop as its child content we’ll declare
this.message in the third argument of the nested createElement function.
this.message will reference the message property available in the
component as props:
let renderComponent = {
render(createElement) {
return createElement(
'div', {
'attrs': {
class: 'render-card'
},
}, [
createElement('header', {
'attrs': {
class: 'card-header card-header-title',
},
}, this.message)
]
);
},
props: ['message']
}

And that’s it! Before we finish, it might be worth mentioning that oftentimes
instead of writing the createElement function as is, the term
createElement is often labelled as h (short for hyperscript which is a
term often used in virtual DOM implementations). Shortening the
createElement keyword to h would have our renderComponent now look
like the following:
src/render-functions-example/main.js

let renderComponent = {
render(h) {
return h(
'div', {
'attrs': {
class: 'render-card'
},
}, [
h('header', {
'attrs': {
class: 'card-header card-header-title',
},
}, this.message)
]
);
},
props: ['message']
}

By declaring the created component in the root template like we've done
before, we'll see the rendered card element as expected.

Greetings!

Live version - https://30dofv-renderfunctions.surge.sh


<https://30dofv-renderfunctions.surge.sh>
Sarah Drasner <https://twitter.com/sarah_edo> (Vue Core
Team Member) has a great article on CSS-Tricks that explains
the usage of the term h (short for hyperscript ) in Vue render
functions - https://css-tricks.com/what-does-the-h-stand-for-
in-vues-render-method/ <https://css-tricks.com/what-does-
the-h-stand-for-in-vues-render-method/> .

Whew. If you’re feeling confused here, no need to worry. Though render


functions give us more power in how we’d want to tailor the markup of our
components, using standard templates is usually a lot easier the vast
majority of time.

In certain few cases, render functions can make creating the markup of
components simpler, like this example shown here from the Vue
documentation <https://vuejs.org/v2/guide/render-function.html#Basics> .

RENDER FUNCTIONS AND JSX


A large reason as to why the implementation we’ve done above might be
seen as somewhat painful was due to us writing the render function with
raw native JavaScript. To help make writing render functions a lot easier,
Vue gives us the ability to write render functions with JSX with the help of
an appropriate Babel plugin <https://github.com/vuejs/babel-plugin-
transform-vue-jsx> !
If you come from a React background, JSX might already be a
familiar topic. Simply put, JavaScript XML (or more commonly
known as JSX) is an extension that allows us to write
JavaScript that looks like HTML (i.e. write XML-like syntax in
JavaScript).

JSX can help recreate our renderComponent implementation in a way that is


a lot easier to read since we can safely write HTML in the render function:

let renderComponent = {
render(h) {
return (
<div class="render-card">
<header class="card-header card-header-title">
{this.message}
</header>
</div>
);
},
props: ['message']
}

If we’re using JSX in our render functions, we must alias


createElement to h in the render function argument
<https://vuejs.org/v2/guide/render-function.html#JSX> .

CodeSandbox <https://codesandbox.io/> (an online code-editor) provides


great support for using JSX with Vue. Without worrying about the tooling
we need just yet, here’s a live CodeSandbox example
<https://codesandbox.io/s/6j1733lvqw> of creating the renderComponent
with JSX (the component is created in the RenderComponent.vue file).

With JSX, our render function doesn’t look too difficult! It’s important to
keep in mind that JSX is a development tool that always need to be
transpiled with the help of a Babel package (like babel-plugin-transform-
vue-jsx <https://github.com/vuejs/babel-plugin-transform-vue-jsx> ) to
standard JavaScript. babel-plugin-transform-vue-jsx also requires that
we’re building our app with the help of a module bundler like Webpack.

CONCLUSION
If you feel like you haven’t fully grasped the information in this article - that
is totally okay. Vue recommends us to use standard templates whenever
we can since render functions are harder to grasp and implement in an
application. As a fun-fact as well, the Vue team has stated that one of the
bigger changes to come in Vue 3.0 <https://medium.com/the-vue-
point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf#f25a> is the
Virtual DOM format used in render functions, particularly the native
JavaScript way of doing so. When Vue 3.0 does finally arrive, we’ll revamp
how the examples in this article is set up but as of now - this article is
intended to be an introduction to what render functions are. We won’t be
discussing or using render functions for the rest of the course.

We'll be taking a deep dive into many of the cool updates Vue
3.0 will bring in the second last article of the course - Vue 3.0
and the future of Vue.

In the application shown with CodeSandbox, you might be wondering why


the component files look and are named differently (e.g.
RenderComponent.vue ) and why the entire application structure looks a
little more advanced. This is because that application was a Vue app
bundled with Webpack <https://webpack.js.org/> with the components
being built in Single-File format. It’s safe to say that it’s finally time to dive
into what these components are, which we’ll be doing tomorrow!
30 Days of Vue

SINGLE FILE
COMPONENTS
SINGLE FILE COMPONENTS
Today, we'll discuss one of Vue's most useful features in
helping build large scale Vue applications - Single File
Components.

We've made it through 16 days already! Pat yourself on the back... but not
for too long... there is still a lot more.

From what we've seen in the past few articles, we now have a good picture
of how the Vue.component({}) constructor can be used to define global
components and how we’re able to instead locally register components in
the components property of a parent instance.

With either Vue.component({}) or components assigned to constant


variables, we had to write our component templates using ES6’s template
literals to obtain a presentable multiline format.

Vue.component('hello-world', {
template: `
<div>
<h1>Hello World</h1>
<p>This is the Hello World component</p>
</div>
`,
});
We’ve also taken a quick look at alternate template definitions
like inline-template 's and x-template 's but have come to
understand that both of these approaches are not
recommended to creating the markup of components.

Template strings do a good job for small to medium sized projects.


However, as an application grows, having no syntax highlighting and the
entire markup of a component kept within back-ticks (or strings) makes the
template of components hard to read. In addition, the components we’ve
defined thus far don’t allow us to isolate the unique CSS within them. This
is where Vue’s Single File Components <https://vuejs.org/v2/guide
/single-file-components.html> can be used to help reduce this
disorganization.

SINGLE FILE COMPONENTS


Single File Components allow us to define the HTML/CSS and JS of a
component all within a single .vue file.

A single-file component is composed of three parts:

The <template> section which contains the component’s markup in


plain HTML.
The <script> section which contains all the JS logic within that
component.
The <style> section which contains all the component styles.

Here’s an example of a single-file component given the name of


HelloWorld :
<template>
<div class="hello-world">
<h2>{{ getGreeting }}</h2>
<p>This is the Hello World component.</p>
</div>
</template>

<script>
export default {
name: 'HelloWorld',
data () {
return {
reversedGreeting: '!dlrow olleH'
}
},
computed: {
getGreeting() {
return this.reversedGreeting
.split("")
.reverse()
.join("");
}
}
}
</script>

<style scoped>
.hello-world {
width: 100%;
text-align: center;
}
</style>

The <template> of the HelloWorld component displays the value of the


getGreeting computed property declared in the <script> section as
well some static text.
The getGreeting computed property simply reverses the
reversedGreeting data property to return “Hello World!”.
The <style> section of the component has the scoped attribute which
dictates the styles declared in the component will be applied to this
and only this component.

If the application root template only rendered this component, our


application would look like the following.

Though the structure of a single-file component may look different,


everything we’ve discussed thus far with regards to instance/component
properties remains the same. We’re able to use all the properties a Vue
instance component contains like data, methods, computed properties,
lifecycle hooks, etc.

The main advantage to using single-file components is how we’re able to


neatly define the markup, logic, and styles of a component all within a
single file. By building our application in a module-based setting (e.g. with
Webpack), we’re also able to take advantage of modules
<https://webpack.js.org/concepts/modules/#what-is-a-webpack-module>
to break our application (and components!) to smaller discrete pieces.

As an interesting example, here's a link to the application code needed to


build Vuegram <https://codesandbox.io/s/8ypo1v7xq2> - a hobby app I
built a while back to mimic the mobile Instagram app. (Tutorial to building
Vuegram <https://medium.com/fullstackio/tutorial-build-an-instagram-
clone-with-vue-js-and-cssgram-24a9f3de0408> )
Vuegram was built entirely with the help of Single File Components. If we
were to achieve the same outcome with global Vue.component({})
constructors in a simple standalone Vue application, things would get hard
to read really quickly. This would be partly due to having no template syntax
highlighting as well as the inability to use modules to import/export
components.

SINGLE FILE COMPONENTS AND


SEPERATION OF CONCERNS
Traditionally, we may have found ourselves building the UI of an application
in three distinct files - with the CSS, HTML, and JS all declared separately.

login/
login-page.css // CSS Styling
login-page.html // Markup page that imports CSS and JS
login-page.js // JS functionality
Components, in modern web applications, have given us the ability to
break our application down to smaller pieces and modules. Single File
Components, in Vue, enhances this modular understanding by allowing us
to couple the CSS, HTML, and JS of a component all within a single file.

login/
LoginPage.vue // CSS, HTML, and JS all declared here

With Single File Components, we still separate the concerns of a section UI


(i.e. component) but achieve this in a slightly different way. Here’s a
reconstruction of an especially useful diagram, created and presented by
Evan You <https://www.youtube.com/watch?v=wZN_FtZRYC8&
feature=youtu.be&t=352> , that I've found helpful in visualizing this.

Evan You explains how useful single-file components are incredibly well in
a few talks he's given before. Here's a talk Evan's done at Laracon EU 2017
where he discusses single-file components <https://www.youtube.com
/watch?v=wZN_FtZRYC8&feature=youtu.be&t=352> .
SINGLE FILE COMPONENTS AND
WEBPACK
An important note to keep in mind is that single-file components are only
made possible due to build tools like Webpack. These tools work
alongside built Vue packages (like the vue-loader <https://github.com
/vuejs/vue-loader> library) to compile .vue components to plain JavaScript
modules.

A production-oriented Webpack bundled Vue application often contains the


set-up of multiple build tools like Webpack <https://webpack.js.org>
configuration, Babel <https://babeljs.io/> for transpiling ES6 code, ESLint
<https://eslint.org/> for code linting, single-file components compilation,
etc. The daunting process of understanding and setting up all these build
tools from scratch for newcomers (and non-newcomers alike) is sometimes
given the term JavaScript Fatigue <https://medium.com/@ericclemmons
/javascript-fatigue-48d4011b6fc4> .

Thankfully, Vue provides us with the vue-cli (i.e the Vue command line
interface) to help facilitate the rapid building and developing of Webpack
bundled Vue applications. With that being said, we’ll be stopping here
today and be taking a look at the vue-cli tomorrow!
30 Days of Vue

VUE CLI
VUE CLI
Today, we're going to discuss Vue's official development tool in
helping build and scaffold Webpack bundled Vue applications
rapidly.

Yesterday - we talked about single-file components, the advantages they


provide, and how we’re only able to use them with build tools like Webpack.
Today, we’ll talk about how we can rapidly scaffold a brand new Webpack
bundled Vue application with the Vue CLI <https://cli.vuejs.org/guide/>
(i.e. the Vue Command Line interface).

THE VUE CLI


The vue-cli is a tool built by the Vue core team to help facilitate the rapid
building and developing of Vue applications. The Vue command line
interface allows us to get started right away without having us worry about
all the steps needed to set up the different build tools in a module based
Vue app.

Before use, the @vue/cli package needs to be installed globally:

npm install -g @vue/cli


You'll need a working Node.js <https://nodejs.org/en/>
development environment along with an appropriate Package
manager (e.g. npm <https://www.npmjs.com/> as the
example shown above or yarn <https://yarnpkg.com/en/> ).

Once the vue-cli is installed globally, a new Vue project can be created
by running vue create within a certain directory and specifying the name
of the project we'd want to create. For this article, we’ll create a project with
the name of default_project .

vue create default_project

When the vue create command is run; we’re provided with a prompt
allowing us to select a default preset or manually pick series of features
that we might find useful when building our Vue app (like TypeScript, PWA
support, etc.).
We’ll select the default preset for this article. Once the selection is made,
the Vue CLI will then begin to install all the necessary dependencies and
create the project.
When complete, we’re provided with a new project directory that has a
scaffold similar to this:

default_project/
README.md
babel.config.js
node_modules/
package.json
public/
src/

Here’s a a quick walkthrough of some of the main files and folders that are
scaffolded in a default Vue CLI project:

README.md All extra information/run steps are listed in the README.md


file.

babel.config.js The babel.config.js file contains configuration of Babel


presets and plugins to help transpile ES6 JavaScript to ES5.

node_modules/ The node_modules/ directory refers to all the different


JavaScript libraries that have been installed into our project.

package.json The package.json file lists all the locally installed npm
packages in our application for us to manage in addition to having some
built-in scripts already created for us (e.g. serve , build , lint ).

public/ The public/ folder contains the root markup page ( index.html )
where our Vue application is to be mounted upon. index.html is where we
can also add additional webfonts, style tags, etc. that we might need in our
app.

src/ The src/ directory contains the Vue/JavaScript code that we’ll find
ourselves working directly with.

In addition to these files/folders, we would also see other files/directories


for the extra build tools that we can set-up during a manual set-up (eg.
tsconfig.json file for the TypeScript compiler options).

A vue-cli packaged app comes with a few built-in scripts it creates for us
(which can be seen in the package.json file). Once the setup of our newly
created project is complete, we can navigate into the project (cd
default_project ) and start our app using the npm run serve command.

npm run serve


With the Webpack server running, we’re then able to view our app at
http://localhost:8080/ <http://localhost:8080/> .
If we open up the project src/ folder in the code editor of our choice, we’ll
see a simple Vue app constructed with single-file components. The
src/App.vue file can be seen to be a basic Vue component that displays
the Vue logo image and renders the HelloWorld component created in the
src/components/HelloWorld.vue file.
The Vue CLI scaffolded project is hot-module reloaded
<https://webpack.js.org/concepts/hot-module-replacement/> which
allows the application UI to automatically update when there’s a change
(and save) in Vue code without the need to make a hard refresh.

How awesome is that! And when it comes to configuration, we’re able to


continue configuring the build tools of our app as much as we want with no
issues. There is no concept of ejecting.

Here’s a quick video of what we’ve just discussed above (the instantiation
and running of a brand new Vue application with the default Vue CLI
scaffold settings).
<https://vimeo.com/306929031>

Though we won’t dive into discussing deployment and deployment


patterns, a Vue CLI scaffolded project does have a build command for us
to create minified, optimized versions of our app that we can upload to a
server.

We can build our app using the npm run build command from the root of
our project:

npm run build


VUE CLI UI
As of Vue CLI 3.x, the Vue core team have built a full-blown graphical user
interface for us to be able to create a vue-cli scaffolded project, install
external plugins, and survey the details of said project all within a graphical
UI.

Anywhere in our terminal, we’re able to launch the Vue CLI UI with the
following command:

vue ui

This launches the Vue CLI UI where we’re presented with a series of
different tabs.
We're able to see the list of projects imported to the UI in the Projects tab
and create a brand new Vue CLI project with the Create tab. In our case,
we’re interested in importing the project we’ve already created -
default_project .

At the bottom of the screen, we’re presented with the status bar indicating
which working directory the UI is currently referring to.

By being in the default_project/ directory that we’ve created, we’re able


to import this directory by clicking the Import this folder button present
at the bottom of the screen in the Import tab. When imported, the
directory will be part of the list of projects in the UI.

By opening up an imported project in the UI project manager, we’ll be able


to perform a variety of different tasks such as viewing project dashboard
details, adding external plugins, installing dependencies, etc.

Here’s a quick video highlighting the different information/changes we can


assess from a project imported in the Vue CLI UI.
<https://vimeo.com/306933876>

The Vue CLI is an incredibly useful tool in helping us set up a live-reloading,


module based, single-page Vue app ready for development. Through the
rest of the series, we’ll be working with single-file components within Vue
CLI scaffolded projects. Tomorrow, we'll begin a series of discussions on
the different ways we can manage data within medium to large scale Vue
applications. Good job so far and see you tomorrow!
30 Days of Vue

CUSTOM EVENTS
AND THE EVENTBUS
CUSTOM EVENTS AND THE
EVENTBUS
Today's session will be the first of a series of articles
discussing different methods to managing application wide
data. We'll begin by reintroducing props and custom events
before taking a look at how a global EventBus can facilitate
application wide communication.

Handling data inside a client-side application often begins as a simple


process but can end up becoming a seemingly complex task. Today, we’ll
begin a three-part series on the different ways we can think about
managing application wide data. This three-part series was originally
published as the single article - Managing State in Vue.js
<https://medium.com/fullstackio/managing-state-in-vue-
js-23a0352b1c87> .

We'll begin today's article by reintroducing how props and custom events
allow us to communicate information between parent and child
components.

PROPS
From what we’ve seen so far, we know that Vue components are the
building blocks of Vue applications since they allow us to couple markup
(HTML), logic (JS), and styles (CSS) within them.

Here’s an example of a single-file component that displays a series of


numbers from a data property.

<template>
<div>
<h2>The numbers are {{ numbers }}!</h2>
</div>
</template>

<script>
export default {
name: 'NumberComponent',
data () {
return {
numbers: [1, 2, 3]
}
},
}
</script>

numbers is the array stored within the data() function of


NumberComponent . What if numbers needed to be accessed from another
component? For example, we may need a component to be responsible in
displaying numbers (like above) and another to manipulate the value of
numbers .

To see how numbers can be accessed between two components, we'll


replicate what NumberComponent is doing with a ParentComponent and a
ChildComponent . We’ll have ParentComponent pass the numbers array to
ChildComponent where ChildComponent will render the numbers value. As
we’ve seen from before, Vue gives us the ability to use props to pass data
from the parent down to the child.

PARENTCOMPONENT.VUE
src/props-example/src/components/ParentComponent.vue

<template>
<div>
<ChildComponent :numbers="numbers" />
</div>
</template>

<script>
import ChildComponent from "./ChildComponent";

export default {
name: "ParentComponent",
data() {
return {
numbers: [1, 2, 3]
};
},
components: {
ChildComponent
}
};
</script>

CHILDCOMPONENT.VUE
src/props-example/src/components/ChildComponent.vue

<template>
<div>
<h2>{{ numbers }}</h2>
</div>
</template>

<script>
export default {
name: "ChildComponent",
props: {
numbers: Array
}
};
</script>

The ParentComponent passes the numbers array as props of the same


name down to ChildComponent . ChildComponent simply binds the value of
numbers on to its template with the help of the Mustache syntax.

In the application UI, we'll simply be presented with the value of the
numbers array.

[ 1, 2, 3 ]
Live version - https://30dofv-props2.surge.sh
<https://30dofv-props2.surge.sh>

Awesome! Regardless of how large our application gets, using props to


pass data downwards often remains somewhat the same.

CUSTOM EVENTS
What if we needed to find a way to communicate information in the
opposite direction? An example of this could be allowing the user to
introduce a new number to the array presented in the example above from
the child component. We can’t use props since props can only be used to
pass data in a uni-directional format (from parent down to child down to
grandchild…). To facilitate having the child component notify the parent
about something, we can use Vue custom events.
We'll use custom events to have ChildComponent be able to facilitate a
change to the ParentComponent 's numbers data property.

CHILDCOMPONENT.VUE
src/custom-events-example/src/components/ChildComponent.vue

<template>
<div>
<h2>{{ numbers }}</h2>
<input v-model="number" type="number" />
<button
@click="$emit('number-added', Number(number))">
Add new number
</button>
</div>
</template>

<script>
export default {
name: "ChildComponent",
props: {
numbers: Array
},
data() {
return {
number: 0
};
}
};
</script>

PARENTCOMPONENT.VUE
src/custom-events-example/src/components/ParentComponent.vue

<template>
<div>
<ChildComponent
:numbers="numbers"
@number-added="numbers.push($event)"
/>
</div>
</template>

<script>
import ChildComponent from "./ChildComponent";

export default {
name: "ParentComponent",
data() {
return {
numbers: [1, 2, 3]
};
},
components: {
ChildComponent
}
};
</script>

The ChildComponent has an input that captures a number value and a


button, that upon click, emits a number-added custom event with the
captured number value. On the ParentComponent , a custom event listener
denoted by @number-added , is specified where the child component is
being rendered. When this event is emitted in the child, it pushes the
number value from the event to ParentComponent 's numbers array.

If we type a number in the input and click the 'Add new number' button, the
number will be added to the numbers array that’s being displayed.

[ 1, 2, 3 ]
0 Add new number

Live version - https://30dofv-customevents2.surge.sh


<https://30dofv-customevents2.surge.sh>

SIBLING-SIBLING COMPONENT
COMMUNICATION
We can use props to pass data downwards and custom events to send
messages upwards. How would we be able to either pass data or facilitate
communication between two different sibling components?
We can’t use custom events the way we have above because those events
are emitted within the interface of a particular component, and as a result
the custom event listener needs to be declared on where the
component is being rendered. In two isolated components, one
component isn’t being rendered within the other.

There are roughly 3 main ways we can begin to manage information


between sibling components and as a result start to handle application
wide state management:

1. Use a global EventBus


2. Use a simple global store
3. Use the flux-like library Vuex

In today’s article, we’ll only take a look at how a global EventBus works.

EVENTBUS
An EventBus is a Vue instance that is used to enable isolated
components to subscribe and publish custom events between one
another.
Wait… didn’t we just say isolated components can’t trigger and listen to
custom events between one another? They normally can’t, but an EventBus
helps us achieve this by being made global for this purpose.

Here’s an example of creating an EventBus instance within an event-


bus.js file:

src/event-bus-example/src/event-bus.js

import Vue from "vue";


export const EventBus = new Vue();

We can now use the interface of the EventBus to emit events.

Instead of the parent-child component layout we had earlier, assume we


wanted to achieve the same interaction between two sibling components -
NumberDisplay and NumberSubmit .

We'll have the NumberSubmit component send a custom event when a


button is clicked. This custom event, number-added , will pass the value of
what the user types in the input.
src/event-bus-example/src/components/NumberSubmit.vue

<template>
<div>
<input v-model="number" type="number" />
<button @click="addNumber">
Add new number
</button>
</div>
</template>

<script>
import { EventBus } from "../event-bus.js";

export default {
name: "NumberSubmit",
data() {
return {
number: 0
};
},
methods: {
addNumber() {
EventBus.$emit("number-added", Number(this.number));
}
}
};
</script>

Now we can have the completely isolated component, NumberDisplay , be


responsible in displaying a list of number values. NumberDisplay will also
be listening for a new number that is being entered in NumberSubmit .
src/event-bus-example/src/components/NumberDisplay.vue

<template>
<div>
<h2>{{ numbers }}</h2>
</div>
</template>

<script>
import { EventBus } from "../event-bus.js";

export default {
name: "NumberDisplay",
data() {
return {
numbers: [1, 2, 3]
};
},
created() {
EventBus.$on("number-added", number => {
this.numbers.push(number);
});
}
};
</script>

Since NumberDisplay is isolated from NumberSubmit , we’re declaring the


custom event listener programmatically with $on :

EventBus.$on('number-added', () => {
// callback function...
});

We’re attaching the EventBus listener, EventBus.$on , on the created()


lifecycle hook of the NumberDisplay component. Throughout the life of the
NumberDisplay component, whenever this event gets triggered - the
listener callback function will run which pushes the number event payload
to the component numbers array.

With the EventBus, we're be able to now facilitate some form of


communication between the two sibling components - NumberDisplay and
NumberSubmit .

[ 1, 2, 3 ]
0 Add new number

Live version - https://30dofv-eventbus.surge.sh


<https://30dofv-eventbus.surge.sh>

This answers the question we had in mind - An EventBus can be used to


facilitate communication between sibling components.
Notice how easy it was to set up and use an EventBus? Unfortunately, an
EventBus also brings along a clear disadvantage. Imagine our
hypothetical application looked more like this:

Assume all the white lines are props that are being passed from the parent
down to all the children, and the yellow dashed lines are events being
emitted and listened from and to a component. Each of these events aren’t
being tracked and can be fired from anywhere in our application. This
makes things hard to maintain really quickly which can make code
frustrating to work with and can become a source of bugs.

This is the primary reason as to why the Vue style-guide states that an
EventBus is not the recommended approach to application wide data
management <https://vuejs.org/v2/style-guide/#Non-flux-state-
management-use-with-caution> . As a result, we highly suggest to only use
an EventBus for very simple use-cases and experiments.

Instead of using an external Vue instance (an EventBus),


application wide custom events can also be propagated with
the help of the application root instance itself - this.$root
<https://vuejs.org/v2/guide/components-edge-
cases.html#Accessing-the-Root-Instance> . Using either the
app root instance or an EventBus to facilitate communication
is not a recommended approach to handling application data.

We’ll be looking at how a simple global store can be a much more useful
method to handling application data tomorrow. See you then!
30 Days of Vue

SIMPLE GLOBAL
STORE
SIMPLE GLOBAL STORE
Yesterday, we gathered an understanding of how a global
EventBus can use its events interface to have isolated
components communicate with one another. Today, we’ll see
how a Simple Global Store can be a more useful approach to
handling state management.

Though an EventBus is easy to set up - the significant disadvantage behind


using one is the difficulty in being able to track data changes appropriately.
In today's article, we’ll be looking at how a Simple Global Store can be
used to handle state management in a more robust manner.

SIMPLE GLOBAL STORE


The term state management is used quite a bit. State basically means data.
State management often refers to the management of application level
data.

Simple state management can be performed by creating a store pattern


that involves sharing a data store between components. The store can
manage the state of our application as well as the methods that are
responsible in changing the state.

As an example, let’s adapt the code samples we’ve seen in the last article
and look to create a store that will help us in this case. We can create a
store.js file in the src/ folder that exports a store object (which
contains a state object within):
export const store = {
state: {
numbers: [1, 2, 3]
}
};

The numbers array in our store will be the array that needs to be either
displayed or manipulated from more than one component. When it comes
to changing this numbers array - we can look to keep our store centralized
by adding all the methods (i.e. mutations) that can be done on the store
state in the store itself.

To mimic the interaction we had in the last article, we’ll introduce an


addNumber method to the store that accepts a payload and directly updates
the state.numbers value with that payload.

src/simple-global-store-example/src/store.js

export const store = {


state: {
numbers: [1, 2, 3]
},
addNumber(newNumber) {
this.state.numbers.push(newNumber);
}
};

We can now have a NumberDisplay component that’s responsible in


displaying the numbers array from the store:
src/simple-global-store-example/src/components/NumberDisplay.vue

<template>
<div>
<h2>{{ storeState.numbers }}</h2>
</div>
</template>

<script>
import { store } from "../store.js";

export default {
name: "NumberDisplay",
data() {
return {
storeState: store.state
};
}
};
</script>

A NumberSubmit component will allow the user to add a new number to our
store numbers data array:
src/simple-global-store-example/src/components/NumberSubmit.vue

<template>
<div>
<input v-model="numberInput" type="number" />
<button @click="addNumber(numberInput)">
Add new number
</button>
</div>
</template>

<script>
import { store } from "../store.js";

export default {
name: "NumberSubmit",
data() {
return {
numberInput: 0
};
},
methods: {
addNumber(numberInput) {
store.addNumber(Number(numberInput));
}
}
};
</script>

The NumberSubmit component has an addNumber() method that calls the


store.addNumber() mutation and passes the expected payload.

The store method receives the payload and directly mutates the
store.numbers array. Thanks to Vue’s reactivity, whenever the numbers
array in store state gets changed, the relevant DOM that depends on this
value ( <template> of NumberDisplay ) automatically updates. This is
because the NumberDisplay storeState data value is equivalent to the
store.state object. When store.state changes, NumberDisplay
storeState changes, and the template of NumberDisplay therefore re-
renders.

[ 1, 2, 3 ]
0 Add new number

Live version - https://30dofv-globalstore.surge.sh


<https://30dofv-globalstore.surge.sh>

When we say components interact with one another here, we’re using the
term ‘interact’ loosely. NumberDisplay and NumberSubmit aren’t going to do
anything directly to each other but instead invoke changes to one another
through the store.
Awesome! Everything works as intended and setting up a simple store
wasn’t actually that difficult.

Though we won’t be adding anything else to our store, an important note to


keep in mind is that we should always attempt to keep all application state
and potential state mutations within the store. For example, let’s assume we
wanted to introduce a few more potential store methods (like
removeLastNumber() or reverseNumbers() ) that we expect some
components to call:
store = {
state: {
numbers: [1, 2, 3]
},
addNumber(number) {
this.state.numbers.push(number);
},
removeLastNumber() {
this.state.numbers.pop();
},
reverseNumbers() {
this.state.numbers.reverse();
}
}

By centralizing everything within the store, it becomes easy to gather and


understand all the data (and changes in data) that need be accessed and
manipulated from an application wide standpoint.

SIMPLE GLOBAL STORE AND THE FLUX


PATTERN
If we take a closer look at all the pieces that directly interact with the store
between the NumberDisplay and NumberSubmit components, we can
establish a pretty clear pattern.

The method in the NumberSubmit component has the responsibility to


directly act on the store method, so we can label it as a store action.
The store method has a certain responsibility which is to directly
mutate the store state. So we’ll say it’s a store mutation.
NumberDisplay doesn’t really care about what type of methods exist in
the store or in NumberSubmit, and is only concerned with getting
information from the store. So we can label NumberDisplay as a store
getter of sorts.

An action commits to a mutation. The mutation mutates state which then


affects the view (i.e. components). Components retrieve store data with
getters. We’re starting to get very close to the Flux-like architecture of
managing application wide state.

This would be a good place to stop for today. Tomorrow, we’ll pick up by
looking at what the Flux architecture is before using Vue’s official Flux-like
state management library - Vuex!
30 Days of Vue

INTRODUCTION TO
FLUX
INTRODUCTION TO FLUX
Handling data inside large client-side applications is a
complex task. Today we're looking at a one method of
handling complex data proposed by Facebook called the Flux
Architecture.

As our applications get bigger and more complex, we'll need a better data
handling approach. With more data, we'll have more to keep track of.

Our code is required to handle more data and application state with new
features. From asynchronous server responses to locally-generated,
unsynchronized data, we have to not only keep track of this data, but also
tie it to the view in a sane way.

Recognizing this need for data management, the Facebook team released a
pattern for dealing with data called Flux <https://facebook.github.io
/flux/docs/overview.html> .

Today, we're going to take a look at the Flux architecture, what it is and why
it exists.

THE FLUX PATTERN


Flux <https://facebook.github.io/flux/docs/overview.html> is a design
pattern created by Facebook that manages how data flows through a
client-side application. As we've seen, the appropriate method of working
with components is through passing data from one parent component to its
children components. The Flux pattern makes this model the default
method for handling data.
The Flux pattern is usually composed of 4 parts organized as a one-way
data pipeline.

The major idea behind Flux is that there is a single-source of truth (the
store) that can only be updated by triggering actions. The actions are
responsible for calling the dispatcher, which the store can subscribe for
changes and update its own data accordingly.

The key idea behind Flux is data flows in one direction and
kept entirely in the store.

FLUX IMPLEMENTATIONS
Flux is a design pattern, not a specific library or implementation. Facebook
has open-sourced a library they use <https://github.com/facebook/flux>
that provides the interface for a dispatcher and a store that we can use in
our application.
Facebook’s implementation is not the exclusive option. Since Facebook
started sharing Flux with the community, the community has responded by
writing tons of different Flux implementations <https://github.com
/voronianski/flux-comparison> . Redux <https://github.com/reactjs
/redux> has been made incredibly popular within the React ecosystem
(and can actually be used in a Vue application with minor configuration
changes).

Within the Vue community, however, Vuex <https://github.com/vuejs


/vuex> is the most-widely used, flux-like, state management library.

Plug for fullstackvue

We discuss this material in-depth with regards Flux, using


Vuex, and even integrating Vuex to a large server-persisted
shopping-cart app. Check it out at fullstack.io/vue
<https://www.fullstack.io/vue/> .

VUEX
Though Vuex was inspired largely by Flux, it is not itself a pure flux
implementation. It also takes inspiration from the Elm Architecture
<https://guide.elm-lang.org/architecture/> . Vuex provides the same
general principles around how to update the data in our application, but in
slightly different (and easier) way.

The main principles of Vuex are:

The view (i.e components) dispatches actions that describe what


happened.
The store receives these actions and determines what state changes
should occur.
After the state updates, the new state is pushed to the view.

This simplicity is what makes Vuex a fairly easy library to understand since
it doesn’t include any of the more complicated functionality often found in
other flux-like libraries (e.g. middlewares, dispatcher payloads, etc).

In any case, this is enough of an introduction to the flux pattern. Tomorrow


we'll actually start working with Vuex to adapt the example applications
we've built in the last couple of articles.
30 Days of Vue

VUEX
VUEX
With the knowledge of the Flux pattern and Vuex, let's
integrate Vuex onto a Vue application to build on top of the
things we've learned using a simple global store.

In the article before last, we saw how a simple global store allows us to
manage application state in a reasonable manner that mimics the Flux
pattern. Today, we'll reimplement the same application using the most
robust and widely-used, flux-like, state management library in Vue - Vuex
<https://github.com/vuejs/vuex> .

We'll look to build the same NumberDisplay and NumberSubmit component


relationship that we've done before. As a refresher, we’ll want to build an
implementation where an input is entered in a NumberSubmit component
and that entered number is then shown in the template of a sibling
NumberDisplay component.

VUEX
Since Vuex is an external library, it needs to be explicitly introduced into an
application.

npm install vuex --save

For module based Webpack Vue applications, global level functionality (i.e.
plugins) like Vuex need to be called with the Vue.use() global method
prior to its use. The following code and the rest of the Vuex store
instantiation can be done in a src/store.js file.

import Vue from 'vue';


import Vuex from 'vuex';

Vue.use(Vuex);

The heart of a Vuex integration is the Vuex Store which can be created with
the new Vuex.Store({}) constructor. We can create the constructor and
assign it a const variable we'll call store . We'll export the store variable
since we'll need to include it in the application Vue instance shortly.

import Vue from 'vue';


import Vuex from 'vuex';

Vue.use(Vuex);

export const store = new Vuex.Store({


// store properties
});

The Vuex Store is made complete with 4 objects - state, mutations,


actions, and getters.
State is simply an object that contains the data that needs to be shared
within the application. We'll create a state object that only has a numbers
array.

const state = {
numbers: [1, 2, 3]
};

Mutations are functions responsible in directly mutating store state. In


Vuex, mutations always have access to the store state as the first
argument. In addition, actions may or may not pass in a payload as the
second argument. We'll create an ADD_NUMBER mutation that expects a
payload and pushes that payload to the state.numbers array.

const mutations = {
ADD_NUMBER(state, payload) {
state.numbers.push(payload);
}
};

In Flux architectures, mutation functions are often


characterized in capital letters to distinguish them from other
functions and for tooling/linting purposes.

Actions exist to call mutations. Actions are also responsible for performing
any or all asynchronous calls prior to committing to mutations. Actions
have access to a context object that provides access to store state (with
context.state ), to store getters (with context.getters ), and to the
commit function (with context.commit ).

Here’s an addNumber() action that simply directly commits to the


ADD_NUMBER mutation while passing in the expected payload:

const actions = {
addNumber(context, number) {
context.commit("ADD_NUMBER", number);
}
};

Getters are to a Vuex store what computed properties are to a Vue


component. Getters are primarily used to perform some
calculation/manipulation to store state before having that information
accessible to components.

Like mutations, getters have access to state as the first argument. Here’s a
getter called getNumbers that simply returns the state.numbers array:

const getters = {
getNumbers(state) {
return state.numbers;
}
};

For such a simple implementation like the application we're


building, a Vuex store may not really be necessary. We don’t
necessarily need getters to directly return a state value and our
action just simply commits to a mutation without any
additional work. The examples above are meant to show the
direct difference in implementation between using Vuex or a
simple global store to handle application state.

With all the different store objects prepared, we can declare these objects
within the new Vuex.Store({}) constructor to create our store instance.
This makes our entire src/store.js file look like the following:
src/vuex-store-example/src/store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const state = {
numbers: [1, 2, 3]
};

const mutations = {
ADD_NUMBER(state, payload) {
state.numbers.push(payload);
}
};

const actions = {
addNumber(context, number) {
context.commit("ADD_NUMBER", number);
}
};

const getters = {
getNumbers(state) {
return state.numbers;
}
};

export default new Vuex.Store({


state,
mutations,
actions,
getters
});
Through the course, you may have noticed that we’re using
ES6 property value shorthands to declare the values of
properties in objects.

For example, the store instantiation above can be expressed


as:

const store = new Vuex.Store({


state: state,
mutations: mutations,
actions: actions,
getters: getters
});

The ES6 property shorthand only works when the property


value has the same name as the property identifier.

When a Vuex store is prepared, it’s only made available to a Vue application
by declaring the store object within the Vue instance. We'll import the
store instance into the src/main.js file and pass it into the application
wide Vue instance.
src/vuex-store-example/src/main.js

import Vue from 'vue';


import App from './App.vue';
import store from "./store";

Vue.config.productionTip = false;

new Vue({
render: h => h(App),
store,
}).$mount('#app');

With a well built Vuex store, components often do one of two things. They
either:

1. GET state information (by accessing store state or getters) or


2. DISPATCH actions

The NumberDisplay component will directly display the value of the


state.numbers array from the store. It'll display the state.numbers value
by mapping the getNumbers store getter on to the components
getNumbers computed property.
src/vuex-store-example/src/components/NumberDisplay.vue

<template>
<div>
<h2>{{ getNumbers }}</h2>
</div>
</template>

<script>
export default {
name: "NumberDisplay",
computed: {
getNumbers() {
return this.$store.getters.getNumbers;
}
}
};
</script>

The NumberSubmit component will hold the responsibility to allow the user
to add numbers to the store state.numbers array. Adding numbers to the
store numbers array can be done by mapping an addNumber component
method to the store action of the same name.
src/vuex-store-example/src/components/NumberSubmit.vue

<template>
<div>
<input v-model="numberInput" type="number" />
<button @click="addNumber(numberInput)">
Add new number
</button>
</div>
</template>

<script>
export default {
name: "NumberSubmit",
data() {
return {
numberInput: 0
};
},
methods: {
addNumber(numberInput) {
this.$store.dispatch(
"addNumber",
Number(numberInput)
);
}
}
};
</script>

Notice how we’re able to access the application wide store instance with
this.$store .

Our application will now work as intended with the help of a Vuex store.
[ 1, 2, 3 ]
0 Add new number

Live version - https://30dofv-vuex.surge.sh <https://30dofv-


vuex.surge.sh>

We can see that Vuex extends the simple store method by introducing
explicitly defined actions, mutations, and getters. This is where the initial
boilerplate, as well as the main advantage comes in. In addition, another
significant advantage to using Vuex is the integration with the Vue Devtools
to provide time-travel debugging.

Here’s a quick gif that shows how the Vue Devtools helps us observe store
information as mutations occur, as well as the ability to time-travel the UI to
the moment a particular mutation has occurred.
<https://bit.ly/2VAhHIl>

The app in the gif above is the TodoMVC <https://vuejs.org


/v2/examples/todomvc.html> example implementation built
by the Vue core team.

Vuex isn’t the only Flux-like library that can be used with Vue. For example,
community supported libraries like redux-vue <https://github.com
/nadimtuhin/redux-vue> or vuejs-redux <https://github.com/titouancreach
/vuejs-redux> exist to help bind Vue components with a Redux store.
However, since Vuex was tailored to be used only within the Vue ecosystem
- it’s definitely the easiest to integrate with and use on a Vue application.

This now completes the series of articles catered to state management in


Vue applications! In summary - Vuex is the most robust and supported
method for managing application state but requires some flux-like
understanding and comes with additional boilerplate. The simple global
store method, on the other hand, is relatively easy to establish however
state and possible state changes aren’t explicitly defined.

If you'd like to re-absorb the contents mentioned in this and


the last couple of articles but in a different format - you can
watch a talk I’ve given on this subject here <https://youtu.be
/B7g7MOrDtMY> !

Tomorrow we'll begin looking at another crucial piece to building large-


scale Vue applications - client-side routing!
30 Days of Vue

INTRODUCTION TO
ROUTING
INTRODUCTION TO ROUTING
The majority of large-scale applications we tend to create will
usually have multiple views be kept in different routes. Today,
we'll discuss client-side routing and set up the starting point
of the routing application we'll build.

Most large-scale applications we use today tend to have multiple views be


separated and kept in different URL routes. For instance, an application
might have a login view where a user can log in or a search results page
that shows a user a list of their search results. These are two different views
with two different page structures.

In today's and tomorrow's article, we'll discuss and build an application that
has multiple views with the help of the Vue Router <https://github.com
/vuejs/vue-router> library. The app we'll build will be an adaptation of an
application first built as part of the Let’s Build a Custom Vue Router
<https://css-tricks.com/build-a-custom-vue-router/> article originally
posted on CSS-Tricks <https://css-tricks.com/> .

ROUTING
First and foremost: let’s define routing for those who may be new to the
concept.

In web development, routing often refers to splitting an application’s UI


based on rules derived from the browser URL. Imagine clicking a link and
having the URL go from https://website.com to https://website.com
/article/ . That’s routing.

Routing is often categorized in one of two main buckets:


Server-side routing: the client (i.e. the browser) makes a request to
the server on every URL change.
Client-side routing: the client only makes a request to the server
upon initial-page load. Any changes to the application UI based on
URL routes are then handled on the client.

Client-side routing is where the term single-page application (or SPA for
short) comes in. SPAs are web apps that load only once and are
dynamically updated with user interaction without the need to make
subsequent requests to the server. In modern SPAs, JavaScript is often the
driving force that dynamically renders different UI.

CASE STUDY: POKÉMON


All the apps we’ve built in this course have all been limited to a single view.
By the end of tomorrow’s article, we’ll look to have constructed a simple
Pokémon routing app that displays details of a particular Pokémon based
on the URL route.
The application will have three unique URL routes: /charizard ,
/blastoise , and /venusaur . Based on the URL route entered, a different
Pokémon will be shown.

Footer links will exist at the bottom of the application to direct the user to
each respective route upon click.

We could build this application without the need of routes and instead use
a simple parent-child component hierarchy that uses props to dictate the
information that should be displayed. You can see this in the Codepen
example here: Pokémon App - Props <https://codepen.io/itslit
/pen/yvymJL> .

Though the props version of the Pokémon app works, it misses a


substantial feature that’s expected from most web applications -
responding to browser navigation events. We’d want our Pokémon app to be
accessible and to show different details for different pathnames:
/charizard , /blastoise , and /venusaur . This would allow users to
refresh different pages and keep their location in the app, bookmark the
URLs to come back to later, and potentially share the URL with others.

To save us some time for the next article and not concern ourselves with
how the views in our app are to be semantically arranged; assume we are
to begin with three separate component files with each file representing
the markup and static information of a single Pokémon:

src/
components/
BlastoiseCard.vue
CharizardCard.vue
VenusaurCard.vue
App.vue
main.js

Each Pokémon component will be a simple presentational component that


has almost the exact same markup to every other Pokémon component.
The only difference would be the static Pokémon information shown in the
components. As an example, here’s how the CharizardCard.vue file will be
set up:
src/pokemon-routing/src/components/CharizardCard.vue
<template>
<div class="card card--charizard
has-text-weight-bold
has-text-white">
<div class="card-image">
<div class="card-image-container">
<img src="../../static/charizard.png"/>
</div>
</div>
<div class="card-content has-text-centered">
<div class="main">
<div class="title has-text-white">Charizard</div>
<div class="hp">hp 78</div>
</div>
<div class="stats columns is-mobile">
<div class="column">🔥 <br>
<span class="tag is-warning">Type</span>
</div>
<div class="column center-column">199 lbs<br>
<span class="tag is-warning">Weight</span>
</div>
<div class="column">1.7 m <br>
<span class="tag is-warning">Height</span>
</div>
</div>
</div>
</div>
</template>

<script>
export default {
name: "CharizardCard",
};
</script>

The uppermost parent App component will currently only declare the
CharizardCard component in its components property and render it as
<PokemonCard /> within its template.

src/pokemon-routing/src/App.vue

<template>
<div class="container">
<div class="pokemon">
<PokemonCard />
</div>
</div>
</template>

<script>
import CharizardCard from "./components/CharizardCard";

export default {
name: "App",
components: {
PokemonCard: CharizardCard
}
};
</script>

<style lang="css" src="./styles.css">


/* Styles from stylesheet */
</style>

All styles will be come from either the Bulma CSS framework
<https://bulma.io/documentation/> or a custom styles.css stylesheet
located within the src/ folder.
The starting point of our application will only have the CharizardCard
component be rendered regardless of what URL route we're in.
Charizard
hp 78

🔥 199 lbs 1.7 m


Type Weight Height
Live version - https://30dofv-pokemon-start.surge.sh
<https://30dofv-pokemon-start.surge.sh>

We’ve covered a simple introduction to routing and talked about the


application starting point we’ll use for our Vue Router example. We’ll begin
tomorrow by integrating and using the components Vue Router provides to
enable client-side routing in our Pokémon app!
30 Days of Vue

VUE ROUTER
VUE ROUTER
With our application scaffold established in the last article,
let's use Vue's official client-side routing library, Vue Router, to
enable routing in our application.

VUE ROUTER
In order to use the Vue Router <https://router.vuejs.org/> library, we'll
need to first install it into our project:

npm install vue-router --save

For module based Webpack applications, we’ll need to call


Vue.use(VueRouter) before we’re able to use the Vue Router library. We
can write the following code and the rest of the router instantiation in a
src/router.js file.

import Vue from 'vue';


import VueRouter from 'vue-router';

Vue.use(VueRouter);

Before we continue, let's take a step back and from a high level look at how
and why we plan to architect our application.

Conceptually, we've seen how we can create tree structures using


components and nested components. Using this perspective with a single
page app with routes, we can think of the different parts of a page as
children. Routing in a single page app from this perspective is the idea that
we can take a part of a subtree and switch it out with another subtree. We
can then dynamically switch out the different trees in the browser.

For a more simpler explanation, we'll essentially need to use a Vue


component that acts as a root component of the routable elements. We can
then tell Vue to change a view, which can just swap out an entire Vue
component for another one as though it's a completely different page
rendered by a server.

Before we use this root router component, we’ll need to tell our router
which views should be shown in which routes. This matching can be
dictated in a routes array that we’ll create. We’ll create this array in the
src/router.js file:

import Vue from 'vue';


import VueRouter from 'vue-router';
import BlastoiseCard from "./components/BlastoiseCard";
import CharizardCard from "./components/CharizardCard";
import VenusaurCard from "./components/VenusaurCard";
import NotFound from "./components/NotFound";

Vue.use(VueRouter);

const routes = [
{ path: "/", component: CharizardCard },
{ path: "/charizard", component: CharizardCard },
{ path: "/blastoise", component: BlastoiseCard },
{ path: "/venusaur", component: VenusaurCard },
{ path: "*", component: NotFound }
];

We’ve set each Pokémon path to their own respective component (e.g.
/blastoise will render the BlastoiseCard component). We’ve also set the
root path / to the CharizardCard component.

The path of * is how we, in Vue Router, can show a certain fallback
component if the user attempts to access a route that we haven’t explicitly
defined (i.e. the user is accessing a ‘Not Found’ template). Any route
entered in the URL that does not exist will return the NotFound component
which contains a simple header template that states 'Sorry. We couldn't
find that Pokémon :('.

src/pokemon-routing/src/components/NotFound.vue

<template>
<h3 class="subtitle has-text-white">
Sorry. We couldn't find that Pokémon :(.
</h3>
</template>

<script>
export default {
name: "NotFound",
};
</script>

We can now look to create our application wide router instance using the
new VueRouter({}) constructor. At the very minimum, the constructor
expects the routes array that maps components to their respective
pathnames:
import Vue from "vue";
import VueRouter from "vue-router";
// ...

const routes = [
// ...
];

export const router = new VueRouter({


routes
});

Vue Router’s default mode is hash. Hash mode URLs always contain a hash
symbol (#) after the hostname (i.e domain name). The hash mode basically
means our application routes will be displayed like this -
http://localhost:8080/#/charizard <http://localhost:8080/#/charizard> .
The benefit to this often lies with allowing us to have multiple client side
routes without having to provide the necessary server side fallbacks.

Since our application is a dead simple client-side app and we don’t want
the hash in our URLs, we can get rid of it. To remove hashes in our URLs,
we’ll specify the history mode property in our router instantiation:
import Vue from "vue";
import VueRouter from "vue-router";
// ...

const routes = [
// ...
];

export const router = new VueRouter({


mode: 'history',
routes
});

If we were to deploy our single-page app and depending on


the server configuration we have, we may have to provide a
catch-all fallback route to tell our server to always fall back to
the same index.html file. The Vue Router documentation
show some example server configurations
<https://router.vuejs.org/guide/essentials/history-
mode.html#example-server-configurations> we can use to
create this fallback route.

When a Vue Router instance is prepared, it’s made available to a Vue


application by declaring the router object within the application wide Vue
instance. We’ll do this in the main.js file where our Vue app is currently
being instantiated:
src/pokemon-routing/src/main.js

import Vue from 'vue';


import App from './App.vue';
import router from './router';

Vue.config.productionTip = false;

new Vue({
render: h => h(App),
router
}).$mount('#app');

With the router instance available everywhere in our app, we’re able to now
use the components given to us from the Vue Router library. Remember
that root router component we talked about earlier that would decide
which view to show based on the route that the user is in? In Vue Router,
this root router component is the <router-view> component.

In the template of the parent App component, we’ll remove the declaration
of <PokemonCard /> and instead render the <router-view> component
that Vue Router gives us.
<template>
<div class="container">
<div class="pokemon">
<router-view></router-view>
</div>
</div>
</template>

<script>
export default {
name: "App"
};
</script>

<style lang="css" src="./styles.css">


/* Styles from stylesheet */
</style>

<router-view> determines which component should be displayed based


on the app’s location.

In our app, we'll need to add a links (or an anchor tag -- <a /> ) to enable
our users to travel freely between the three different routes. However, using
the <a /> tag will tell the browser to treat the route like it's a server-side
route. Instead, we'll need to use a different component that Vue Router also
provides called: <router-link> .

Using the <router-link> component requires passing a to prop that


specifies the route we would want the link to navigate to. Let's update the
App component to use <router-link> to create the links we want in our
application:
src/pokemon-routing/src/App.vue

<template>
<div class="container">
<div class="pokemon">
<router-view></router-view>

<div class="pokemon-links has-text-centered">


<router-link to="/charizard">
/charizard
</router-link>
<router-link to="/blastoise">
/blastoise
</router-link>
<router-link to="/venusaur">
/venusaur
</router-link>
</div>
</div>
</div>
</template>

<script>
export default {
name: "App"
};
</script>

<style lang="css" src="./styles.css">


/* Styles from stylesheet */
</style>

<router-link> would allow the user to change the location of the browser
without making a web request.
And there we have it! If we save all the work we’ve done, we now have a
fully functional client-side application built with Vue and Vue Router.
Charizard
hp 78

🔥 199 lbs 1.7 m


Type Weight Height

/charizard /blastoise
/venusaur
Live version - https://30dofv-pokemon-routing.surge.sh
<https://30dofv-pokemon-routing.surge.sh>

If you’d like to re-absorb the subject of client-side routing in


Vue - you can watch a talk I’ve given on this subject here
<https://www.youtube.com/watch?v=YFnimUl8Qjo> !

Vue Router provides so much more functionality that we don't have time to
cover in our brisk intro to routing. Though this was a simple introduction,
Vue Router provides more intermediate and advanced features like
dynamic route matching, navigation guards, and lazy loading routes. For
more information, be sure to check out the following links:

https://router.vuejs.org/ <https://router.vuejs.org/>
Fullstack Vue - Routing <https://www.fullstack.io/vue/>
30 Days of Vue

INTRODUCTION TO
TESTING
INTRODUCTION TO TESTING
Test suites are an upfront investment that pay dividends over
the lifetime of a system. Today, we'll introduce the topic of
testing and discuss the different types of tests we can write.

Okay, close your eyes for a second... wait, don't... it's hard to read with your
eyes closed, but imagine for a moment your application is getting close to
your first deployment.

It's getting close and it gets tiring to constantly run through the features in
your browser... and so inefficient.

There must be a better way...

TESTING
When we talk about testing, we're talking about the process of automating
the process of setting up and measuring our assumptions against
assertions of functionality about our application.

The importance of testing in development can’t be stressed enough.


Testing can help reveal bugs before they appear, instill confidence in our
web applications, and make it easy to onboard new developers on an
existing codebase.

When we talk about front-end testing in the context of Vue, we're referring
to the process of making assertions about what our Vue app renders and
how it responds to user interaction.

There are two different software testing paradigms that are often done -
integration testing, and unit testing.

INTEGRATION TESTING
Integration testing (often labeled as end-to-end testing) is a top-down
approach where tests are written to determine whether an application has
been built appropriately from start to finish. We write end-to-end tests as
though we are a user’s movement through our application.

Though many integration suites exist, Nightwatch


<http://nightwatchjs.org/> is an end-to-end testing suite that is often used
with Vue applications. Nightwatch is Node.js based and allows us to write
tests that mimic how a user interacts with an application.

UNIT TESTING
Unit testing is a confined approach that involves isolating each part of an
application and testing it in isolation (i.e. as a unit). Tests are provided a
given input and an output is often evaluated to make sure it matches
expectations.

Unit tests typically do not require a browser, can run incredibly quickly (no
writing to the DOM required), and the assertions themselves are usually
simple and terse.

In the next couple of articles, we’ll be focusing solely on unit testing.

TESTING TOOLS
Numerous testing tools/suites exist within the JavaScript ecosystem. Here
are some popular tools that you may have come across before:

Mocha <https://mochajs.org/> : A JavaScript testing framework that


allows us to specify our test suites with describe and it blocks.
Chai <https://www.chaijs.com/> : A testing assertion library that
provides interfaces for us to create assertions for our tests (should...,
expect..., assert...).
Jest <https://jestjs.io/> : A JavaScript testing framework that comes
with an assertion library, mocking support, snapshot testing, and
more with minimal to no configuration/set-up.

We'll use the Jest testing framework since it comes with its own assertion
library that provides a readable testing language and assertions. Finally,
we'll use Vue’s official unit testing library, called Vue Test Utils, which
would provide some really useful testing utility functions that make writing
our assertions a breeze.

Tomorrow, we'll get an application set up with the testing in place so that
we can start testing the application and be confident it works as we expect.
See you tomorrow!
30 Days of Vue

IMPLEMENTING
TESTS
IMPLEMENTING TESTS
Yesterday, we discussed the importance of testing and some
of the different types of unit testing libraries/suites that exist.
Today, we'll begin to see unit testing in action.

As mentioned in the last article, we'll be using the Jest <https://jestjs.io/>


testing framework. One of the main advantages behind using Jest is its zero
configuration set-up. The Getting Started <https://jestjs.io/docs/en
/getting-started> guide in the Jest documentation shows us that we simply
need to install the Jest package, create a .test.js or .spec.js file, and
add a test script to the package.json file.

However, if we select the Jest suite as a dependancy we want in a Vue CLI


scaffolded project - this set-up would already be prepared for us. We'll
work within the test files of a project created with the Vue CLI that has the
Jest testing suite installed.

SIMPLIFIED TODOMVC
The app we’ll be running our tests for is a simplified version of the
TodoMVC <http://todomvc.com/> open source project. We’ll only test a
simple single-file Vue component which will help us understand how units
tests can be made in Vue.

Our simplified TodoMVC app will have three distinct features; an input to
enter new todo items, a list of entered todo items, and the ability to remove
a todo item from the list.
The single App.vue component file that makes up our app will look like the
following:
src/testing-todo-mvc/src/App.vue
<template>
<div id="app">
<section class="todoapp">
<header class="header">
<h1 class="title">todos</h1>
<input
class="new-todo"
placeholder="What needs to be done?"
v-model="newTodo"
@keyup.enter="addTodo"
/>
</header>
<section class="main">
<ul class="todo-list">
<li class="todo"
v-for="(todo, key) in todos"
:key="key">
<div class="view">
<label>{{ todo }}</label>
<button class="destroy"
@click="removeTodo(todo)">
</button>
</div>
</li>
</ul>
</section>
</section>
</div>
</template>

<script>
export default {
name: "App",
data() {
return {
todos: [],
newTodo: ""
};
},
methods: {
addTodo() {
if (this.newTodo !== "") {
this.todos.push(this.newTodo);
this.newTodo = "";
}
},
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
}
}
};
</script>

<style lang="css" src="./styles.css">


</style>

The <template> of the App component has the following details:

The <header> element has a title of ‘todos’.


The <header> has an input field that is bound to a newTodo data
property. This input field listens for the keyup.enter event (i.e. the
release of the enter key) and when triggered calls an addTodo method.
Below the <header> exists a list of todo items displayed from a todos
array stored in the components data. This list is rendered with the
help of the v-for directive.

The <script> section of the component can be seen to have the following
details:
todos and newTodo is initialized with a blank array and empty string
respectively. todos is the list of todo items displayed in the list and
newTodo is the data property tied to the controlled input.
In the methods property exists two methods - addTodo() and
newTodo(). The addTodo() method first checks if the newTodo property
isn’t blank and if not, it pushes the newTodo data value to the todos
array before clearing newTodo back to an empty string.
The removeTodo() method uses the Array splice()
<https://developer.mozilla.org/en-US/docs/Web/JavaScript
/Reference/Global_Objects/Array/splice> method to remove the
intended todo item based on its index in the array.

The <style> tag has a src referenced from a styles.css file located
within the src/ folder.

With the App component appropriately declared in the root Vue instance
(in the src/main.js file), our application will allow us to add and remove
todo items from a list of todos.

todos
What needs to be done?
Live version - https://30dofv-todomvc.surge.sh
<https://30dofv-todomvc.surge.sh>

WRITING TESTS
The Vue CLI introduces a tests/ directory adjacent to the src/ directory
for a scaffolded project that has either the 'Unit Testing' or 'E2E Testing'
option selected during manual set-up.

testing-todo-mvc/
README.md
babel.config.js
node_modules/
package.json
public/
src/
tests/

The tests/ directory will contain either the unit/ or e2e/ folders
depending on which testing environment was selected during the Vue CLI
set-up stage. In our case, only a tests/unit/ folder will be created which
would contain an .eslintrc.js file and an example.spec.js file.
testing-todo-mvc/
...
tests/
unit/
.eslintrc.js
example.spec.js

The .eslintrc.js file is often used to configure ESLint rules within a


particular directory or subdirectory <https://eslint.org/docs/user-
guide/configuring> . The .eslintrc.js file within our tests/unit folder
simply declares the Jest framework as an environment our linter should
recognize for any file within the tests/unit directory.

src/testing-todo-mvc/tests/unit/.eslintrc.js

module.exports = {
env: {
jest: true
}
}

Specifying jest: true in the env option will configure the linter to
recognize the set of predefined global variables that exists within the Jest
framework.

The Vue CLI creates an example.spec.js file that has a sample test
prepared for us. We'll remove this file and simply create an App.spec.js file
which will be the file where we write our tests for the App component.
testing-todo-mvc/
...
tests/
unit/
.eslintrc.js
App.spec.js

Jest provides a few native methods that we'll be using. Both of the following
methods accept two arguments, the first being a description string and the
second a function to execute:

describe() <https://jestjs.io/docs/en/api#describename-fn>
it() <https://jestjs.io/docs/en/api#testname-fn-timeout>

In Jest, the it() function is sometimes aliased to test() .

The describe() function provides a way for us to group our tests together
in logical bundles. Since we're writing a bunch of tests for the App
component, we'll use the describe() function in our test to indicate this.

describe('App', () => {
// where we'll group our tests for App
});

We can add our first test using the it() function which is the function we
can use to set our expectations. Let's set up our tests with our first
expectations, one passing and one failing so we can see the difference in
output.

In the same file, let's add two tests:


describe('App', () => {
it('passing test', () => {
expect(true).toBeTruthy();
});

it('failing test', () => {


expect(false).toBeTruthy();
});
});

We'll look at the possible expectations we can set in a moment. First, let's
run our tests.

EXECUTING TESTS
The Vue CLI package sets up a quality testing environment using Jest (if
selected) automatically for us. We can execute all our tests by using the
test:unit script declared in the package.json file.

npm run test:unit


From this output, we can see the two tests with one passing test (with a
green checkmark) and one failing test (with the red x and a description of
the failure).

Let's update the second test to make it pass by changing the expectation to
toBeFalsy() :

src/testing-todo-mvc/tests/unit/App.spec.js

describe('App', () => {
it('passing test', () => {
expect(true).toBeTruthy();
});

it('failing test', () => {


expect(false).toBeFalsy();
});
});

Re-running the test, we can see we have two passing tests.


npm run test:unit

EXPECTATIONS
Jest provides a few global commands in our tests by default (i.e. things you
don't need to require). One of those is the expect() command. The
expect() command has a few expectations which we can call on it,
including the two we've used already:

toBeTruthy() <https://jestjs.io/docs/en/expect#tobetruthy>
toBeFalsy() <https://jestjs.io/docs/en/expect#tobefalsy>
toBe() <https://jestjs.io/docs/en/expect#tobevalue>
toEqual() <https://jestjs.io/docs/en/expect#toequalvalue>
toBeDefined() <https://jestjs.io/docs/en/expect#tobedefined>
etc...

The entire suite of expectations (i.e. expect() matchers) is available on the


Jest documentation page at: https://jestjs.io/docs/en/expect
<https://jestjs.io/docs/en/expect> .

The expect() function takes a single argument: the value or function that
returns a value to be tested. For instance, our two tests we've already
written pass the boolean values of true and false .

Now that we've confirmed our testing setup, we'll actually get down to
testing our App component tomorrow. Great job today and see you
tomorrow!
30 Days of Vue

TESTING THE APP


TESTING THE APP
Today, we'll start creating unit tests by looking at a few
features of our application, thinking about where the edge
cases are and what we assume will happen with the
component.

Yesterday, we discussed the simplified TodoMVC <http://todomvc.com/>


app that we hope to test and established the test file where we’ll be
running our tests. Today, we’ll start by looking at two features of our
application and think about how we’ll create suitable test assertions for
them.

We'll essentially want to test any dynamic logic we have in our component.
For simplicity, however, we’ll begin by setting up our first assertions to
verify our app is initialized with the correct static content and data.

We like to start out testing by listing our assumptions about a component


and under what circumstances these assumptions are true. For instance,
our first tests will have these list of assumptions:
Under all circumstances, we can assume there will be a title with the
text content of ‘todos’.
Under all circumstances, we can assume there will be an input with
the placeholder text content of ‘What needs to be done?’.
Under all circumstances, we can assume our component will initialize
the todos and todoItem data properties with an empty array and a
blank string respectively.

These assumptions will translate into the tests we write.

Since these initial assumptions are simple enough and don’t


involve any dynamic logic, they may actually not warrant the
need to have tests. However, we’ll begin with these
tests/assumptions since they’ll pose to be a gradual and easy
introduction.

TESTING
Let's open the file tests/unit/App.spec.js . We left off with some dummy
tests in this file, so let's clear those off and start with a fresh describe
block:

describe('App', () => {
// Tests go here
});

For the upcoming tests that we want to write, we'll want to import and
bring in the vue library into our test file:
import Vue from 'vue';

describe('App', () => {
// Tests go here
});

Since we’re testing the App component, we'll want to bring that into our
workspace as well:

import Vue from 'vue';


import App from '@/App';

describe('App', () => {
// Tests go here
});

The Webpack configuration of the Vue CLI scaffolded project


resolves the @ symbol to import relatively from src anywhere
in our project.

Let's write our first test. Our first test will cover the first two assumptions
listed above - the header title and input should have the correct initial text
content and placeholder respectively.
import Vue from 'vue';
import App from '@/App';

describe('App', () => {
it('should render correct contents', () => {
// our assertion will go here
});
});

Since we'll be asserting the rendered DOM within this particular test, we'll
need to have our component be in the mounted state. There are a few
ways of achieving this but one way would involve first extending the App
module like so:

import Vue from 'vue';


import App from '@/App';

describe('App', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(App);
});
});

Vue.extend() is the internal Vue method that is called when a component


is created with Vue.component() . Unlike Vue.component() , Vue.extend()
doesn’t associate the created constructor with a string ID (i.e. the name of
the component).

With our constructor extended, we can now mount our component with the
$mount() method:
import Vue from 'vue';
import App from '@/App';

describe('App', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(App);
const vm = new Constructor().$mount();
});
});

vm will now reference the mounted component that we can use to access
the rendered HTML. The component’s HTML can be accessed with
vm.$el . With the component's HTML available to us, we’ll use the standard
JavaScript querySelector() <https://developer.mozilla.org/en-US/docs
/Web/API/Document/querySelector> method to return the appropriate
elements and assert their text content and placeholder.

import Vue from 'vue';


import App from '@/App';

describe('App', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(App);
const vm = new Constructor().$mount();

expect(vm.$el.querySelector('.title').textContent)
.toBe('todos');
expect(vm.$el.querySelector('.new-todo').placeholder)
.toBe('What needs to be done?');
});
});

With that, our tests should pass. As a reminder, we can run all the tests in
our project with the test:unit script.

npm run test:unit

The second test we wish to create is the assertion that the App component
is initialized with the correct initial data - todos is an empty array and
newTodo is a blank string. This test will be easier to conduct since we don’t
need our component to be in the mounted state.

If we recall from an earlier article in this course, we’ve mentioned that in


components the data property must be a function that returns an object of
key-value pairs. We can access this component data by simply invoking the
said function. For this test, we’ll create another it block with the test
description of 'should set correct default data' :
src/testing-todo-mvc/tests/unit/App.spec.js

import Vue from 'vue';


import App from '@/App';

describe('App.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(App);
const vm = new Constructor().$mount();

expect(vm.$el.querySelector('.title').textContent)
.toBe('todos');
expect(vm.$el.querySelector('.new-todo').placeholder)
.toBe('What needs to be done?');
});

it('should set correct default data', () => {


const initialData = App.data();

expect(initialData.todos).toEqual([]);
expect(initialData.newTodo).toEqual('');
});
});

By running our test suite, we should expect to see both tests pass.

npm run test:unit


Though everything we’ve done works well, things would start to become a
little more difficult when we find ourselves needing to assert more
complicated functionality (e.g. assert that when the user enters an input
and clicks the remove icon, the todo item is removed from the list).

This is where the official Vue testing library, Vue Test Utils <https://vue-
test-utils.vuejs.org/> , comes in. Vue Test Utils provides an easier and
higher-level interface for asserting against a Vue component under test.
We'll refactor the tests we've written and compile a few a more assertions,
all with the Vue Test Utils library, tomorrow!
30 Days of Vue

BETTER TESTING
WITH VUE TEST
UTILS
BETTER TESTING WITH VUE TEST
UTILS
Today, we'll look at Vue’s official testing library called Vue Test
Utils <https://vue-test-utils.vuejs.org/> that makes testing fun
and relatively easy.

Yesterday, we constructed our tests through simple assertions that involved


either mounting the component constructor or invoking the component
data function. Although this worked well, testing can become a bit
cumbersome when more complicated functionality needs to be tested. Vue
Test Utils is a testing utility library created and maintained by the Vue core
team that offers a nicer, higher-level API for dealing with Vue components
under test.

In the last article, we were testing against an App component which


rendered a simple todo list.
USING VUE TEST UTILS
Yesterday, we wrote our first two tests as the following:

src/testing-todo-mvc/tests/unit/App.spec.js

import Vue from 'vue';


import App from '@/App';

describe('App.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(App);
const vm = new Constructor().$mount();

expect(vm.$el.querySelector('.title').textContent)
.toBe('todos');
expect(vm.$el.querySelector('.new-todo').placeholder)
.toBe('What needs to be done?');
});

it('should set correct default data', () => {


const initialData = App.data();

expect(initialData.todos).toEqual([]);
expect(initialData.newTodo).toEqual('');
});
});

Let's see what these tests look like when we rewrite it with the Vue Test
Utils library.

Since Vue Test Utils is an external library we want to introduce, we’ll first
usually need to install it into our application.
npm install @vue/test-utils --save-dev

Vue Test Utils allows us to test the output of just a single component (e.g.
App ) rather than testing the entire complete component tree. This is often
called shallow rendering and basically means that if the parent App
component had any children, they wouldn’t be rendered in our tests.

Vue Test Utils makes shallow rendering super easy by allowing us to use
the shallowMount() function to mount our component.

Let’s update the src/App.spec.js file to include the shallowMount()


function from Vue Test Utils. We won’t have the need to import the Vue
library itself anymore as well.

import App from '@/App';


import { shallowMount } from '@vue/test-utils';

describe('App', () => {
it('should render correct contents', () => {
// our old tests
});

it('should set correct default data', () => {


// our old tests
});
});
Shallow? Mount?

The advantages behind shallow rendering is the enforcement


of testing the component in isolation and it being a faster
approach overall. Since App doesn’t render any children
components of its own - normal mounting with the mount()
function from Vue Test Utils will achieve a similar result here.

To render our component, we can use the shallowMount() method and


store the result in a variable. Then, we'll query the rendered component for
different HTML elements that are rendered inside its virtual DOM.

Our first test will be comprised of only a few lines:

import App from '@/App';


import { shallowMount } from '@vue/test-utils';

describe('App', () => {
it('should render correct contents', () => {
let wrapper = shallowMount(App);
expect(wrapper.find('.title').text())
.toBe('todos');
expect(wrapper.find('.new-todo').element.placeholder)
.toBe('What needs to be done?');
});

it('should set correct default data', () => {


// our old tests
});
});

We’re using the test utility wrapper find() <https://vue-test-utils.vuejs.org


/api/wrapper/find.html> method to return a wrapper of the HTML elements
we’re looking for. For the header title, we’re then simply able to use the
wrapper text() <https://vue-test-utils.vuejs.org/api/wrapper/#text>
method to return the text content of the title element. For the input field,
since a placeholder method doesn’t exist in Vue Test Utils, we retrieve the
element from wrapper.find('new-todo') from which we can get the
placeholder.

Our second test would be fairly simple as well. With Vue Test Utils, we’re
able to access the properties of an instance with wrapper.vm <https://vue-
test-utils.vuejs.org/api/wrapper/#properties> . With wrapper.vm available
to us, we can directly access the component todos and newTodo data
properties to create our assertions:

import App from '@/App';


import { shallowMount } from '@vue/test-utils';

describe('App', () => {
it('should render correct contents', () => {
let wrapper = shallowMount(App);
expect(wrapper.find('.title').text())
.toBe('todos');
expect(wrapper.find('.new-todo').element.placeholder)
.toBe('What needs to be done?');
});

it('should set correct default data', () => {


let wrapper = shallowMount(App);
expect(wrapper.vm.todos).toEqual([]);
expect(wrapper.vm.newTodo).toEqual('');
});
});

We can run our tests in the same manner as we've done before by using
the test:unit script available in our project.

npm run test:unit

Our tests pass and are now more maintainable moving forward!

Let's continue writing assertions. We’ll declare a few more assumptions


that we expect our upcoming tests to address:

When the user populates the text input field, it should update the
newTodo data property.
When the user populates the text input field and releases the Enter
key, it should add the entered todo item to the todos array.
When the user populates the text input field, releases the Enter key,
then clicks the remove icon of the entered item, it removes this todo
item from the todos array.

We'll structure the rest of our test suite first by writing out our describe
and it blocks. We'll fill out the specs with assertions after. Since we’ll
need the shallow mounted wrapper for every test, we can initialize the
mounting process in the Jest beforeEach() <https://jestjs.io/docs/en
/setup-teardown#repeating-setup-for-many-tests> function which would
run before every unit test. With all that said, our test suite will now be
structured as the following:
import App from '@/App';
import { shallowMount } from '@vue/test-utils';

describe('App', () => {
let wrapper;

beforeEach(() => {
wrapper = shallowMount(App);
});

it('should render correct contents', () => {


let wrapper = shallowMount(App);
expect(wrapper.find('.title').text())
.toBe('todos');
expect(wrapper.find('.new-todo').element.placeholder)
.toBe('What needs to be done?');
});

it('should set correct default data', () => {


expect(wrapper.vm.todos).toEqual([]);
expect(wrapper.vm.newTodo).toEqual('');
});

describe('user populates the text input field', () => {


it('should update "newTodo"', () => {});

describe('and presses Enter', () => {


it('should add a new todo to "todos"', () => {});
});

describe('and presses Enter + removes todo', () => {


it('should have the new todo removed', () => {});
});
});
});

If we were following Test Driven Development (or TDD for


short), we would write these assumptions first and then build
the component to pass these tests.

Let's fill in these tests so that they pass against our existing App
component.

The interactions a user can have with our app has to start with first
populating the input field to add a new item. We want to simulate this
behavior in the next set of specs. To avoid the repetition of finding the
input, updating the value, and triggering an event for each remaining test;
we can extrapolate this set-up to a beforeEach() function in the nested
describe() block:
import App from '@/App';
import { shallowMount } from '@vue/test-utils';

describe('App', () => {
let wrapper;

// ...

describe('user populates the text input field', () => {


let inputField;

beforeEach(() => {
inputField = wrapper.find('.new-todo');
inputField.element.value = 'New Todo';
inputField.trigger('input');
});

it('should update "newTodo"', () => {});

describe('and presses Enter', () => {


it('should add a new todo to "todos', () => {});
});

describe('and presses Enter + removes todo', () => {


it('should have the new todo removed', () => {});
});
});
});

In the newly created beforeEach function, we’re simulating the behavior of


how a user creates an input event by:

1. First finding the input wrapper with .find().


2. Then setting the value of the input DOM element to ‘New Todo’.
3. And finally using the wrapper trigger() <https://vue-test-
utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events>
method to fire off the input event and simulate actual user interaction.

We’re now able to write specs related to the context where the user has just
populated the input field. Our first upcoming test is fairly simple since we
want to assert that when the user populates the text input field, it updates
the newTodo data property in the component.

To verify if the newTodo data property has been updated, we’ll simply assert
whether the newTodo property is equal to the input value we’ve set in the
beforeEach() function:
describe('user populates the text input field', () => {
let inputField;

beforeEach(() => {
inputField = wrapper.find('.new-todo');
inputField.element.value = 'New Todo';
inputField.trigger('input');
});

it('should update "newTodo"', () => {


expect(wrapper.vm.newTodo).toEqual('New Todo');
});

describe('and presses Enter', () => {


it('should add a new todo to "todos"', () => {});
});

describe('and presses Enter + removes todo', () => {


it('should have the new todo removed', () => {});
});
});

The next test involves asserting whether the todo item is added to the list
when the user releases the Enter key after the text input field is populated.
For this assertion, we’ll first trigger the keyup.enter event before asserting
the value of the todos array in the component data.
describe('user populates the text input field', () => {
// ...
describe('and presses Enter', () => {
it('should add a new todo to "todos"', () => {
inputField.trigger('keyup.enter');
expect(wrapper.vm.todos).toEqual(['New Todo']);
});
});
// ...
});

For the last assertion, we’ll first trigger the keyup.enter event before
asserting whether the removal of a todo item works as intended. To mimic
the removal of a todo item, we’ll find() the wrapper of the remove icon
element based on its class ( .destroy ), trigger a click event on this found
wrapper, and assert that the value of the todos array doesn’t contain the
item that was just added in the beforeEach() function.

describe('user populates the text input field', () => {


// ...
describe('and presses Enter + removes todo', () => {
it('should have the new todo removed', () => {
inputField.trigger("keyup.enter");
const removeIcon = wrapper.find(".destroy");

removeIcon.trigger("click");

expect(wrapper.vm.todos).toEqual([]);
});
});
});

Our entire test suite will now look like the following:
src/testing-todo-mvc/tests/unit/App.spec.js
import App from '@/App';
import { shallowMount } from '@vue/test-utils';

describe('App', () => {
let wrapper;

beforeEach(() => {
wrapper = shallowMount(App);
});

it('should render correct contents', () => {


let wrapper = shallowMount(App);
expect(wrapper.find('.title').text())
.toBe('todos');
expect(wrapper.find('.new-todo').element.placeholder)
.toBe('What needs to be done?');
});

it('should set correct default data', () => {


expect(wrapper.vm.todos).toEqual([]);
expect(wrapper.vm.newTodo).toEqual('');
});

describe('user populates the text input field', () => {


let inputField;

beforeEach(() => {
inputField = wrapper.find('.new-todo');
inputField.element.value = 'New Todo';
inputField.trigger('input');
});

it('should update "newTodo"', () => {


expect(wrapper.vm.newTodo).toEqual('New Todo');
});

describe('and presses Enter', () => {


it('should add a new todo to "todos"', () => {
inputField.trigger('keyup.enter');
expect(wrapper.vm.todos).toEqual(['New Todo']);
});
});

describe('and presses Enter + removes todo', () => {


it('should have the new todo removed', () => {
inputField.trigger("keyup.enter");
const removeIcon = wrapper.find(".destroy");

removeIcon.trigger("click");

expect(wrapper.vm.todos).toEqual([]);
});
});
});
});

By running our test suite, we should have all five of our tests pass.
WHAT IS THIS WRAPPER?
Before we close out for today, we’ll talk briefly about the interface of a Vue
Test Utils shallow-rendered (or normally rendered) component - with which
we’ve named in our tests the Wrapper object. The Vue Test Utils
documentation <https://vue-test-utils.vuejs.org/> is great, so we’ll keep
this brief.

When we’ve shallow mounted the App component, or used the find()
method to locate an element, the returned values aren’t the component or
element itself but instead a Wrapper object. This Wrapper object (i.e.
instance) contains the mounted component (or element) and the
accompanying methods to help test the component/element. The Wrapper
has a bunch of functions we can use to make our assertions easier and
more maintainable:

wrapper.html(): returns the HTML of the rendered instance.


wrapper.find(): returns the Wrapper instance of the found node.
wrapper.trigger(): triggers an event on the Wrapper node.
wrapper.setData(): sets the data on the Wrapper instance.
wrapper.setProps(): sets the props the Wrapper would receive.
wrapper.setMethods(): sets the methods on the Wrapper instance.
etc…

Be sure to check out the Vue Test Utils documentation <https://vue-test-


utils.vuejs.org/> to see all the different functions and helper methods we
can apply on a Wrapper instance.

Phew! That's a lot of new information today, but look how quickly we wrote
our follow-up tests with the Vue Test Utils library. It's much quicker to read
and makes it easier to discern what's actually happening.

Here's some pretty big news as well - we’ve practically covered everything
we’ve set out to in this course! That's it! Tomorrow, we’ll be taking a brief
look at what Vue 3.0 is expected to bring when it arrives before heading to
our very last day.
30 Days of Vue

VUE 3.0 AND THE


FUTURE OF VUE
VUE 3.0 AND THE FUTURE OF VUE
In today's article, we'll take a brief look at some of the cool
features Vue 3.0 is expected to bring when it arrives.

Everything we’ve discussed within this course covers the latest current
release of Vue, often labeled as version 2.x <https://github.com/vuejs
/vue/releases> .

In Sept. 2016 <https://medium.com/the-vue-point/vue-2-0-is-here-


ef1f26acf4b8> , the Vue framework was rewritten and released as version
2.0. Vue 2.0 introduced new concepts such as a lightweight virtual DOM
implementation, render functions, and server-side rendering capabilities. In
addition, version 2.0 was rewritten to provide significant performance
improvements over version 1.x.

Exactly two years later, Evan You published Plans for the Next Iteration of
Vue.js <https://medium.com/the-vue-point/plans-for-the-next-iteration-of-
vue-js-777ffea6fabf> , an article that summarizes a talk he gave at Vue.js
London <https://vuejs.london/summary/> presenting a sneak peek of
what’s coming in the upcoming Vue release. At November 15th, 2018 and as
the keynote speaker of VueConfTO <https://vuetoronto.com/schedule/> -
Evan took a deeper dive to explain some of the features Vue 3.0 would
bring.

The Plans for the Next Iteration of Vue.js article and Evan’s slides from
VueConf TO <https://docs.google.com/presentation
/d/1yhPGyhQrJcpJI2ZFvBme3pGKaGNiLi709c37svivv0o/edit> are both
stellar in their explanations. As a result, we won’t be going into too much
detail in this article but will summarize some of the cool updates coming
with Vue 3.0!
VUE 3.0
BREAKING CHANGES
Though Vue 3.0 will have some breaking changes, the good news is that
the Vue API will largely either remain the same or have a clear compatibility
build with Vue 2.x. Scoped slots <https://vuejs.org/v2/guide/components-
slots.html#Scoped-Slots> and the native JavaScript process of writing
render() functions <https://vuejs.org/v2/guide/render-
function.html#Basics> are to experience some significant changes.

VIRTUAL DOM RE-WRITE (WITH TYPESCRIPT!)


The core Virtual DOM source code implementation is getting a complete
re-write with TypeScript! The Virtual DOM implementation is being re-
written to achieve 100% faster mounting and patching which supports one
of the main goals of making the Vue library even faster then it is right now.

The source code has moved away from using Flow <https://flow.org/> to
now instead use TypeScript <https://www.typescriptlang.org/> as the main
types interface. This doesn't mean we will have to use TypeScript with
Vue but it does pave the way for improved TypeScript support in Vue 3.0!

TypeScript will be a prerequisite if you're interested in


contributing to the Vue 3.0 source code directly 😛 .

IE11 SUPPORT
Vue 3.0 is to adapt a Proxy based observation mechanism for the
detection and observation to changes within Vue instances. This is great
because now, some of the caveats we’ve mentioned in this course like the
inability to add/delete new properties in an instance or directly mutate data
arrays will no longer be a problem in Vue 3.0!

Since Proxy based observations aren’t compatible and can’t be transpiled


for IE11, a separate build will be available for developers building for IE11.
This particular build will default to Vue 2.x’s Object.defineProperty API to
detect Vue instance changes - which would result in having the same
detection caveats that currently exist.

CLASS BASED COMPONENTS


Class based components (which we haven’t really discussed in this course)
will be getting first class support in Vue 3.0!

Class based components will involve using the ES6 class syntax to create a
component instance. Here's an example of a class-based component,
called Hello , that Evan originally displayed in his Vue 3.0 Updates
presentation:

interface HelloProps {
text: string
}

class Hello extends Component<HelloProps> {


count = 0
render() {
return <div>
{this.count}
{this.$props.text}
</div>
}
}
The Hello component above is constructed with the class syntax and
extends a Component keyword. The component uses the render() function
with JSX to create the markup of the component.

Some people prefer using the class-based syntax to construct components


due to improved TypeScript declaration and having components be written
in a more concise manner.

Though potentially different to what Vue 3.0 would bring: you can try out
class-based components in Vue 2.x by scaffolding a Vue CLI project,
selecting the Typescript feature, and answering ‘Yes’ to using a class-style
component syntax. This will scaffold the project and allow the declaration
of class components with the help of the vue-class-component
<https://github.com/vuejs/vue-class-component> library.

EXPOSED REACTIVITY API


Vue 3.0 aims to expose the reactivity observable API to allow developers to
handle simple cross-component state management. Here’s a code snippet
that Evan also shared in the Vue 3.0 Updates presentation that shows an
example of how an observable/effect pattern can be pulled and directly
used:
import { observable, effect } from 'vue'

const state = observable({


count: 0
})

effect(() => {
console.log(`count is: ${state.count}`)
}) // count is: 0

state.count++ // count is: 1

In the example above, the observable and effect constructors are


imported directly from the Vue library. A state object is created and set to
an observable constructor that wraps an object containing a count
variable set to 0. The effect constructor declares a call back function that
has access to and console logs the state.count value. Whenever
state.count is ever changed, the effect callback function runs!

Though this is a brief example, this shows us that having direct access to
the exposed reactivity API could open the doors to allowing components
share information in a really easy way!

CONCLUSION
What we’ve talked about thus far are only some of the things that are in the
pipeline with what Vue 3.0 is expected to bring. There are a whole slew of
more advanced core changes being done to the library such as Static Tree
Hoisting, Optimized Slots Generations, Monomorphic Calls, etc. to help
achieve the goals of having a smaller, more manageable, and faster library.

The Vue team has mentioned that there are to be a series of steps in the
coming year such as the Public Feedback via RFCs stage, Alpha Phase,
and Beta Phase, before the library is launched. If you’re interested, keep
your eyes peeled for these phases and be sure to read and check out the
following links for more details:

Plans for the Next Iteration of Vue.js <https://medium.com/the-vue-


point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf> .
Evan’s slides from VueConf TO <https://docs.google.com
/presentation
/d/1yhPGyhQrJcpJI2ZFvBme3pGKaGNiLi709c37svivv0o>

Now when exactly is Vue 3.0 expected to launch? We know that it’ll be
within sometime next year.

Though when exactly in the year is still a little too soon to say, we’ll be here
to keep you up to date when it happens! We’ll see you tomorrow for the
very last day of the course!
30 Days of Vue

WRAP-UP AND
MORE RESOURCES
WRAP-UP AND MORE RESOURCES
We've made it! Day 30. Congrats! Now you have enough
information to create Vue applications with the Vue CLI,
integrate data with Vuex, enable client-side routing with Vue
Router, and even conduct testing with the Vue Test Utils
library.

We've covered a lot of material in the past 30 days. Some of the high-level
topics we discussed are:

The Vue Instance and Data Reactivity


Vue Directives
The Vue Instance lifecycle
Vue Components and Single-File Components
How to use the vue-cli to bootstrap our apps
State management best practices
Client-side routing
Unit Testing

THERE IS SO MUCH MORE!


Although we covered a lot of topics in our first 30 days, there is so much
more! How do we know? We wrote a book <https://www.fullstack.io/vue/>
!

FULLSTACK VUE
Did you happen to enjoy this course and are interested in going deeper
with Vue, with us? Check out Fullstack Vue <https://www.fullstack.io/vue>
! Not only do we cover in-depth the topics we briefly introduced in this 30
days, we go into plenty of other content as well.

We list some of the topics Fullstack Vue covers, in more detail, in the series
conclusion. Here are other resources within the Vue ecosystem that we’ve
also found really useful in our journey working with Vue.

THE VUE CORE DOCUMENTATION


Link: https://vuejs.org/v2/guide/ <https://vuejs.org/v2/guide/>

The Vue core documentation has a reputation for being one of the best-
written technical documentations out there… and for very good reason!
Translated in seven languages, the Vue docs explains all the nifty details in
a simple and clean manner.

THE VUE STYLE GUIDE


Link: https://vuejs.org/v2/style-guide/ <https://vuejs.org/v2/style-guide/>

The Vue Style Guide is the official style guide for Vue specific code and
provides a set of standards that help avoid errors and general anti-patterns.

THE VUE COOKBOOK


Link: https://vuejs.org/v2/cookbook/ <https://vuejs.org/v2/cookbook/>

The Vue Cookbook differentiates from the core documentation by


highlighting ‘recipes’ that dive in to certain topics in a broader sense (for
e.g. how do we package Vue Components for npm? <https://vuejs.org
/v2/cookbook/packaging-sfc-for-npm.html> ).

VUE MASTERY
Link: https://www.vuemastery.com/ <https://www.vuemastery.com/>

A learning resource for Vue developers helmed by the ever talented Gregg
Pollack <https://twitter.com/greggpollack> and Adam Jahr
<https://twitter.com/AdamJahr> . The Vue Mastery video courses are
crisply composed with engaging graphics to help guide viewers to
understand all the ins and outs of the Vue framework.

Gregg and Adam also co-produce the official Vue.js News and
Podcast <https://news.vuejs.org/> !
VUE LAND - THE VUE DISCORD CHANNEL
Link: https://vue-land.js.org/ <https://vue-land.js.org/>

The Vue Discord channel is a fantastic hang-out spot consisting of some of


the core team members, contributors, and other developers. Have a
question or want to stay up to date with announcements? Hang out at Vue
Land!

With that said, congrats on making it to day 30. We look forward to seeing
what you accomplish with Vue! If you feel like you've learned certain things
that you'd like others to know, share it by blogging and/or tweeting about
it!

Potrebbero piacerti anche