# vue

The Majesty of Vue.js 2
Alex Kyriakidis, Kostas Maniatis and Evan You This book is for sale at http://leanpub.com/vuejs2
This version was published on 2017-03-31
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do.
© 2016 – 2017 Alex Kyriakidis, Kostas Maniatis and Evan You

Tweet This Book!
The suggested tweet for this book is:
I’m learning @vuejs with @tmvuejs. Get it at https://leanpub.com/vuejs2 #vuejs The suggested hashtag for this book is #vuejs2.
Find out what other people are saying about the book by clicking on this link to search for this hashtag on Twitter:
Contents
Introduction………………………………… i
Angular1 …………………………………. iv Angular2 …………………………………. v React……………………………………. vi Ember ……………………………………viii Polymer ………………………………….. ix Riot…………………………………….. x
Welcome………………………………………. xi AbouttheBook …………………………………. xi WhoisthisBookfor……………………………….. xi GetInTouch…………………………………… xi Homework……………………………………. xii SampleCode…………………………………… xii Errata………………………………………. xii Conventions…………………………………… xii
I Vue.jsFundamentals……………………… 1 1. InstallVue.js…………………………………… 2
1.1
1.2 1.3
2. GettingStarted …………………………………. 4 2.1 HelloWorld ……………………………….. 4 2.2 Two-wayBinding…………………………….. 6
CONTENTS
2.3 ComparisonwithjQuery………………………….. 7 2.4 Homework………………………………… 9
3. AFlavorofDirectives………………………………. 10
3.1 3.2
3.3 3.4 3.5
v-show………………………………….. 10
v-if……………………………………. 13 3.2.1 Templatev-if…………………………….. 14 v-else…………………………………… 15 v-ifvs.v-show………………………………. 18 Homework………………………………… 19
4. ListRendering………………………………….. 20
4.1 4.2
4.3
4.4 4.5
Install&UseBootstrap ………………………….. 20
v-for…………………………………… 22 4.2.1 Rangev-for……………………………… 22 ArrayRendering……………………………… 23 4.3.1 LoopThroughanArray ……………………….. 23 4.3.2 LoopThroughanArrayofObjects ………………….. 25 Objectv-for ……………………………….. 28 Homework………………………………… 30
5. Interactivity…………………………………… 31
5.1 EventHandling ……………………………… 31 5.1.1 HandlingEventsInline………………………… 31 5.1.2 HandlingEventsusingMethods……………………. 33 5.1.3 Shorthandforv-on………………………….. 34
5.2 EventModifiers ……………………………… 35
5.3 KeyModifiers ………………………………. 39
5.4 ComputedProperties …………………………… 40
5.5 Homework………………………………… 46
6. Filters ……………………………………… 48
6.1
6.2 6.3 6.4 6.5
FilteredResults ……………………………… 48 6.1.1 UsingComputedProperties ……………………… 51 OrderedResults ……………………………… 58 CustomFilters………………………………. 62 UtilityLibraries ……………………………… 63 Homework………………………………… 67
7. Components…………………………………… 68 7.1 WhatareComponents?………………………….. 68 7.2 UsingComponents ……………………………. 68 7.3 Templates ………………………………… 70 7.4 Properties ………………………………… 71
CONTENTS
7.5 Reusability………………………………… 74 7.6 Altogether………………………………… 78 7.7 Homework………………………………… 85
8. CustomEvents …………………………………. 86
8.1 EmitandListen ……………………………… 86 8.1.1 LifecycleHooks …………………………… 88
8.2 Parent-ChildCommunication……………………….. 89
8.3 PassingArguments ……………………………. 91
8.4 NonParent-ChildCommunication…………………….. 96
8.5 RemovingEventListeners…………………………. 99
8.6 Backtostories………………………………. 100
8.7 Homework………………………………… 103
9. ClassandStyleBindings……………………………..105
9.1 9.2
9.3 9.4
Classbinding ………………………………. 105 9.1.1 ObjectSyntax ……………………………. 105 9.1.2 ArraySyntax…………………………….. 108
Stylebinding……………………………….. 110 9.2.1 ObjectSyntax ……………………………. 110 9.2.2 ArraySyntax…………………………….. 111
BindingsinAction…………………………….. 112 Homework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
II ConsuminganAPI……………………….116
10. Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 10.1 CRUD …………………………………..117 10.2 API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
11.Workingwithrealdata ……………………………..122
11.1 GetDataAsynchronous………………………….. 122
11.2 Refactoring………………………………… 125
11.3 UpdateData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
11.4 DeleteData ………………………………..130
12. Integrating vue-resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 12.1 Overview …………………………………133 12.2 Migration …………………………………134 12.3 EnhancingFunctionality …………………………. 135
12.3.1 Edit Stories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
CONTENTS
12.4 12.5 12.6
12.3.2CreateNewStories………………………….. 139 12.3.3Store&UpdateUnit …………………………. 144 JavaScriptFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 SourceCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Homework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 12.6.1 Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 12.6.2APIEndpoints ……………………………. 152 12.6.3YourCode ………………………………152
13. Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 13.1 Implementation ……………………………… 155
13.2 13.3
PaginationLinks……………………………… 158 Homework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
14.1 14.2
14.3 14.4 14.5 14.6 14.7
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 14.1.1Compatibility…………………………….. 164 VariableDeclarations …………………………… 164 14.2.1LetDeclarations …………………………… 164 14.2.2 Constant Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 ArrowFunctions……………………………… 165 Modules………………………………….166 Classes…………………………………..167 DefaultParameterValues…………………………. 168 Templateliterals……………………………… 169
Building Large-Scale Applications . . . . . . . . . . . . 162
III
14. ECMAScript 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
15. AdvancedWorkflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
15.1
15.2
15.3
CompilingES6withBabel ………………………… 171 15.1.1Installation ……………………………… 173 15.1.2Configuration ……………………………. 175 15.1.3Buildalias ……………………………… 176 15.1.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 15.1.5Homework ……………………………… 178
WorkflowAutomationwithGulp …………………….. 180 15.2.1TaskRunners…………………………….. 180 15.2.2Installation ……………………………… 181 15.2.3 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 15.2.4 Watch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 15.2.5Homework ……………………………… 183
ModuleBundlingwithWebpack . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 15.3.1ModuleBundlers…………………………… 184
CONTENTS
15.3.2 Webpack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 15.3.3Installation ……………………………… 188 15.3.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 15.3.5Automation……………………………… 189
15.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 16. Working with Single File Components . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
16.1
16.2
16.3
Thevue-cli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 16.1.1Vue’sTemplates …………………………… 193 16.1.2Installation ……………………………… 194 16.1.3 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
WebpackTemplate ……………………………. 197 16.2.1 Project Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 16.2.2index.html ……………………………… 200 16.2.3 Hello.vue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 16.2.4 App.vue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 16.2.5 main.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Forming.vueFiles ……………………………. 205 16.3.1NestedComponents …………………………. 214
17. Eliminating Duplicate State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
17.1 SharingwithProperties………………………….. 219
17.2 GlobalStore ……………………………….. 224
18. Swapping Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 18.1 DynamicComponents ………………………….. 228 18.1.1Theisspecialattribute ……………………….. 228 18.1.2Navigation ……………………………… 231
19. Vue Router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
19.1 Installation…………………………………236
19.2 Usage……………………………………237
19.3 NamedRoutes……………………………….239
19.4 Historymode ………………………………. 240
19.5 Nestedroutes ………………………………. 242
19.6 Auto-CSSactiveclass……………………………244
19.6.1CustomActiveClass…………………………. 246
19.7 RouteObject………………………………..247
19.8 DynamicSegments ……………………………. 248
19.9 RouteAlias…………………………………255
19.11 Transitions………………………………… 258
19.11.1Introduction …………………………….. 258 19.11.2Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
CONTENTS
19.11.33rd-partyCSSanimations ………………………. 261 19.12 NavigationGuards ……………………………. 262 19.13 Homework…………………………………264
Introduction
Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is very easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.1
If you are an experienced frontend developer and you want to know how Vue.js compares to other libraries/frameworks, check out the Comparison with Other Frameworks chapter.
“Vue.js is what made me love JavaScript. It’s extremely easy and enjoyable to use. It has a great ecosystem of plugins and tools that extend its basic services. You can quickly include it in any project, small or big, write a few lines of code and you are set. Vue.js is fast, lightweight and is the future of Front end development!”
—Alex Kyriakidis
“When I started picking up Javascript I got excited learning a ton of possibilities, but when my friend suggested to learn Vue.js and I followed his advice, things went wild. While reading and watching tutorials I kept thinking all the stuff I’ve done so far and how much easier it would be if I had invest time to learn Vue earlier. My opinion is that if you want to do your work fast, nice and easy Vue is the JS Framework you need. “
—Kostas Maniatis
1https://github.com/vuejs/awesome- vue#libraries- – plugins 2http://vuejs.org/guide/overview.html

“Mark my words: Vue.js will sky-rocket in popularity in 2016. It’s that good.”
— Jeffrey Way
“Vue is what I always wanted in a JavaScript framework. It’s a framework that scales with you as a developer. You can sprinkle it onto one page, or build an advanced single page application with Vuex and Vue Router. It’s truly the most polished JavaScript framework I’ve ever seen.”
— Taylor Otwell
“Vue.js is the first framework I’ve found that feels just as natural to use in a server-rendered app as it does in a full-blown SPA. Whether I just need a small widget on a single page or I’m building a complex Javascript client, it never feels like not enough or like overkill.”
“Vue.js has been able to make a framework that is both simple to use and easy to understand. It’s a breath of fresh air in a world where others are fighting to see who can make the most complex framework.”
— Eric Barnes
“The reason I like Vue.js is because I’m a hybrid designer/developer. I’ve looked at React, Angular and a few others but the learning curve and terminology has always put me off. Vue.js is the first JS framework I understand. Also, not only is it easy to pick up for the less confidence JS’ers, such as myself, but I’ve noticed experienced Angular and React developers take note, and liking, Vue.js. This is pretty unprecedented in JS world and it’s that reason I started London Vue.js Meetup.”
—Jack Barham

Comparison with Other Frameworks Angular 1
Some of Vue’s syntax will look very similar to Angular (e.g. v-if vs ng-if). This is because there were a lot of things that Angular got right and these were an inspiration for Vue very early in its development. There are also many pains that come with Angular however, where Vue has attempted to offer a significant improvement.
Complexity
Vue is much simpler than Angular 1, both in terms of API and design. Learning enough to build non-trivial applications typically takes less than a day, which is not true for Angular 1.
Flexibility and Modularity
Angular 1 has strong opinions about how your applications should be structured, while Vue is a more flexible, modular solution. That’s why a Webpack template3 is provided, that can set you up within minutes, while also granting you access to advanced features such as hot module reloading, linting, CSS extraction, and much more.
Data binding
Angular 1 uses two-way binding between scopes, while Vue enforces a one-way data flow between components. This makes the flow of data easier to reason about in non-trivial applications.
Directives vs Components
Vue has a clearer separation between directives and components. Directives are meant to encapsulate DOM manipulations only, while components are self-contained units that have their own view and data logic. In Angular, there’s a lot of confusion between the two.
Performance
Vue has better performance and is much, much easier to optimize because it doesn’t use dirty checking. Angular 1 becomes slow when there are a lot of watchers, because every time anything in the scope changes, all these watchers need to be re-evaluated again. Also, the digest cycle may have to run multiple times to “stabilize” if some watcher triggers another update. Angular users often have to resort to esoteric techniques to get around the digest cycle, and in some situations, there’s simply no way to optimize a scope with many watchers.
3https://github.com/vuejs- templates/webpack

Vue doesn’t suffer from this at all because it uses a transparent dependency-tracking observation system with async queueing – all changes trigger independently unless they have explicit depen- dency relationships.
Interestingly, there are quite a few similarities in how Angular 2 and Vue are addressing these Angular 1 issues.
Angular 2
There is a separate section for Angular 2 because it really is a completely new framework. For example, it features a first-class component system, many implementation details have been completely rewritten, and the API has also changed quite drastically.
Size and Performance
In terms of performance, both frameworks are exceptionally fast and there isn’t enough data from real world use cases to make a verdict. However if you are determined to see some numbers, Vue 2.0 seems to be ahead of Angular 2 according to this 3rd party benchmark4.
Size wise, although Angular 2 with offline compilation and tree-shaking is able to get its size down considerably, a full-featured Vue 2.0 with compiler included (23kb) is still lighter than a tree-shaken bare-bone example of Angular 2 (50kb).
Flexibility
Vue is much less opinionated than Angular 2, offering official support for a variety of build systems, with no restrictions on how you structure your application. Many developers enjoy this freedom, while some prefer having only one Right Way to build any application.
Learning Curve
To get started with Vue, all you need is familiarity with HTML and ES5 JavaScript (i.e. plain JavaScript). With these basic skills, you can start building non-trivial applications within less than a day of reading the guide.
Angular 2’s learning curve is much steeper. Even without TypeScript, their Quickstart guide5 starts out with an app that uses ES2015 JavaScript, NPM with 18 dependencies, 4 files, and over 3,000 words to explain it all – just to say Hello World.
4http://stefankrause.net/js- frameworks- benchmark4/webdriver- ts/table.html 5https://angular.io/docs/js/latest/quickstart.html

React
React and Vue share many similarities. They both:
• utilize a virtual DOM
• provide reactive and composable view components
• maintainfocusinthecorelibrary,withconcernssuchasroutingandglobalstatemanagement
handled by companion libraries
Performance Profiles
In every real-world scenario that has been tested so far, Vue outperforms React by a fair margin.
Render Performance
When rendering UI, manipulating the DOM is typically the most expensive operation and unfortu- nately, no library can make those raw operations faster. The best it can be done is:
1. Minimize the number of necessary DOM mutations. Both React and Vue use virtual DOM abstractions to accomplish this and both implementations work about equally well.
2. Add as little overhead as possible on top of those DOM manipulations. This is an area where Vue and React differ. In React, let’s say the additional overhead of rendering an element is 1 and the overhead of an average component is 2. In Vue, the overhead of an element would be more like 0.1, but the overhead of an average component would be 4, due to the setup required for the reactivity system.
This means that in typical applications, where there are many more elements than components being rendered, Vue will outperform React by a significant margin. In extreme cases however, such as using 1 normal component to render each element, Vue will usually be slower.
Both Vue and React also offer functional components, which are stateless and instanceless – and therefore, require less overhead. When these are used in performance-critical situations, Vue is once again faster.
Update Performance
In React, you need to implement shouldComponentUpdate everywhere and use immutable data structures to achieve fully optimized re-renders. In Vue, a component’s dependencies are automat- ically tracked so that it only updates when one of those dependencies change. The only further optimization that sometimes can be helpful in Vue is adding a key attribute to items in long lists.
This means updates in unoptimized Vue will be much faster than unoptimized React and actually, due to the improved render performance in Vue, even fully-optimized React will usually be slower than Vue is out-of-the-box.
In Development
Obviously, performance in production is the most important and that’s what we’ve been discussing so far. Performance in development still matters though. The good news is that both Vue and React remain fast enough in development for most normal applications.
However, if you’re prototyping any high-performance data visualizations or animations, you may find it useful to know that in scenarios where Vue can’t handle more than 10 frames per second in development, we’ve seen React slow down to about 1 frame per second.
This is due to React’s many heavy invariant checks, which help it to provide many excellent warnings and error messages.
Ember
Ember is a full-featured framework that is designed to be highly opinionated. It provides a lot of established conventions and once you are familiar enough with them, it can make you very productive. However, it also means the learning curve is high and flexibility suffers. It’s a trade-off when you try to pick between an opinionated framework and a library with a loosely coupled set of tools that work together. The latter gives you more freedom but also requires you to make more architectural decisions.
That said, it would probably make a better comparison between Vue core and Ember’s templating and object model layers:
• VueprovidesunobtrusivereactivityonplainJavaScriptobjectsandfullyautomaticcomputed properties. In Ember, you need to wrap everything in Ember Objects and manually declare dependencies for computed properties.
• Vue’s template syntax harnesses the full power of JavaScript expressions, while Handlebars’ expression and helper syntax is intentionally quite limited in comparison.
• Performance-wise, Vue outperforms Ember by a fair margin, even after the latest Glimmer engine update in Ember 2.0. Vue automatically batches updates, while in Ember you need to manually manage run loops in performance-critical situations.
Polymer
Polymer is yet another Google-sponsored project and in fact was a source of inspiration for Vue as well. Vue’s components can be loosely compared to Polymer’s custom elements and both provide a very similar development style. The biggest difference is that Polymer is built upon the latest Web Components features and requires non-trivial polyfills to work (with degraded performance) in browsers that don’t support those features natively. In contrast, Vue works without any dependencies or polyfills down to IE9.
In Polymer 1.0, the team has also made its data-binding system very limited in order to compensate for the performance. For example, the only expressions supported in Polymer templates are boolean negation and single method calls. Its computed property implementation is also not very flexible.
Polymer custom elements are authored in HTML files, which limits you to plain JavaScript/CSS (and language features supported by today’s browsers). In comparison, Vue’s single file components allows you to easily use ES2015+ and any CSS preprocessors you want.
When deploying to production, Polymer recommends loading everything on-the-fly with HTML Imports, which assumes browsers implementing the spec, and HTTP/2 support on both server and client. This may or may not be feasible depending on your target audience and deployment environment. In cases where this is not desirable, you will have to use a special tool called Vulcanizer to bundle your Polymer elements. On this front, Vue can combine its async component feature with Webpack’s code-splitting feature to easily split out parts of the application bundle to be lazy-loaded. This ensures compatibility with older browsers while retaining great app loading performance.
Riot
Riot 2.0 provides a similar component-based development model (which is called a “tag” in Riot), with a minimal and beautifully designed API. Riot and Vue probably share a lot in design philosophies. However, despite being a bit heavier than Riot, Vue does offer some significant advantages:
• True conditional rendering. Riot renders all if branches and simply shows/hides them.
• A far more powerful router. Riot’s routing API is extremely minimal.
• More mature tooling support. Vue provides official support for Webpack, Browserify, and
SystemJS, while Riot relies on community support for build system integration.
• Transition effect system. Riot has none.
and thus suffers from the same performance issues as Angular 1.
For updated comparisons feel free to check Vue.js guide.
This book will guide you through the path of the rapidly spreading Javascript Framework called Vue.js!
Some time ago, we started a new project based on Laravel and Vue.js. After thoroughly reading Vue.js guide and a few tutorials, we discovered lack of resources about Vue.js around the web. During the development of our project, we gained a lot of experience, so we came up with the idea to write this book in order to share our acquired knowledge with the world. Now that Vue.js 2 is out we decided it was time to update our book by publishing a second version where all examples and their relative contents are rewritten.
This book is written in an informal, intuitive, and easy-to-follow format, wherein all examples are appropriately detailed enough to provide adequate guidance to everyone.
We’ll start from the very basics and through many examples we’ll cover the most significant features of Vue.js. By the end of this book, you will be able to create fast front end applications and increase the performance of your existing projects with Vue.js 2 integration.
Who is this Book for
Everyone who has spent time to learn modern web development has seen Bootstrap, Javascript, and many Javascript frameworks. This book is for anyone interested in learning a lightweight and simple Javascript framework. No excessive knowledge is required, though it would be good to be familiar with HTML and Javascript. If you dont’t know what the difference is between a string and an object, maybe you need to do some digging first.
This book is useful for developers who are new to Vue.js, as well as those who already use Vue.js and want to expand their knowledge. It is also helpful for developers who are looking to migrate to Vue.js 2.
Get In Touch
In case you would like to contact us about the book, send us feedback, or other matters you would like to bring to our attention, don’t hesitate to contact us.
Welcome
xii
Name
The Majesty of Vue.js Alex Kyriakidis Kostas Maniatis
Homework
hello@tmvuejs.com @tmvuejs alex@tmvuejs.com @hootlex kostas@tmvuejs.com @kostaskafcas
The best way to learn code is to write code, so we have prepared one exercise at the end of most chapters for you to solve and actually test yourself on what you have learned. We strongly recommend you to try as much as possible to solve them and through them gain a better understanding of Vue.js. Don’t be afraid to test your ideas, a little effort goes a long way! Maybe a few different examples or ways will give you the right idea. Of course we are not merciless, hints and potential solutions will be provided!
Sample Code
You can find most of the code examples used in the book on GitHub. You can browse around the code here6.
If you prefer to download it, you will find a .zip file here7.
This will save you from copying and pasting things out of the book, which would probably be
terrible.
Errata
Although every care have been taken to ensure the accuracy of our content, mistakes do happen. If you find a mistake in the book we would be grateful if you could report it to us. By doing so, you can protect other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please submit an issue on our GitHub repository8.
Conventions
The following notational conventions are used throughout the book. A block of code is set as follows:
JavaScript
6https://github.com/hootlex/the- majesty- of- vuejs- 2 7https://github.com/hootlex/the- majesty- of- vuejs- 2/archive/master.zip 8https://github.com/hootlex/the- majesty- of- vuejs- 2

Welcome xiii
1 function(x, y){
2 // this is a comment 3}
Code words in text, data are shown as follows: “Use .container for a responsive fixed width container.”
New terms and important words are shown in bold. Tips, notes, and warnings are shown as follows:
This is a Warning
This element indicates a warning or caution.
This is a Tip
This element signifies a tip or suggestion.
This is an Information box
Some special information here.
This is a Note
This is a Hint
This is a Terminal Command
Commands to run in terminal.
This is a Comparison text
A small text comparing things relative to the subject.

Welcome
xiv
This is a link to Github.
Links lead to the repository of this book, where you can find the code samples and potential homework solutions of each chapter.

I Vue.js Fundamentals
1. Install Vue.js
When it comes to download Vue.js you have a few options to choose from.
To install Vue you can simply download and include it with a script tag. Vue will be registered as a global variable.
1. Development Version from http://vuejs.org/js/vue.js1
2. Production Version from http://vuejs.org/js/vue.min.js2.
Tip: Don’t use the minified version during development. You will miss out all the nice
warnings for common mistakes.
1.1.2 Include from CDN
Vue.js.org3 recommends unpkg4, which will reflect the latest version as soon as it is published to npm.
You can find Vue.js also on jsdelivr5 or cdnjs6
It takes some time to sync with the latest version so you have to check frequently for
1http://vuejs.org/js/vue.js
2http://vuejs.org/js/vue.min.js 3https://vuejs.org/v2/guide/installation.html#CDN 4https://unpkg.com/vue/dist/vue.js 5https://cdn.jsdelivr.net/vue/2.0.1/vue.min.js 6https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js

Install Vue.js 3
NPM is the recommended installation method when building large scale apps with Vue.js. It pairs nicely with a CommonJS module bundler such as Webpack7 or Browserify8.
1 # latest stable
2 $npm install vue 3 # latest stable + CSP-compliant 4$ npm install vue@csp
5 # dev build (directly from GitHub):
6 $npm install vuejs/vue#dev 1.3 Download using Bower 1 # latest stable 2$ bower install vue
For more installation instructions and updates take a loot at the Vue.js Installation Guide9
In most book examples we are including Vue.js from the cdn, although you are free to install it using any method you like.
7http://webpack.github.io/ 8http://browserify.org/ 9http://vuejs.org/guide/installation.html

1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9
2. Getting Started
Let’s start with a quick tour of Vue’s data binding features. We’re going to make a simple application that will allow us to enter a message and have it displayed on the page in real time. It’s going to demonstrate the power of Vue’s two-way data binding. In order to create our Vue application, we need to do a little bit of setting up, which just involves creating an HTML page.
In the process you will get the idea of the amount of time and effort we save using a javascript Framework like Vue.js instead of a javascript tool (library) like jQuery.
2.1 Hello World
We will create a new file and we will drop some boilerplate code in. You can name it anything you like, this one is called hello.html.

Hello Vue

This is a simple HTML file with a greeting message.
Now we will carry on and do the same job using Vue.js. First of all we will include Vue.js and create a new Instance.

Hello Vue

10
11
12
13
14
15
16
17

For starters, we have included Vue.js from cdnjs1 and inside a script tag we have our Vue instance. We use a div with an id of #app which is the element we refer to, so Vue knows where to ‘look’. Try to think of this as a container that Vue works at. Vue won’t recognize anything outside of the targeted element. Use the el option to target the element you want.
Now we will assign the message we want to display, to a variable inside an object named data. Then we’ll pass the data object as an option to Vue constructor.
vardata={
};
new Vue({
el: '#app',
data: data })
To display our message on the page, we just need to wrap the message in double curly brackets . So whatever is inside our message it will appear automatically in the h1 tag.

# {{ message }}

It is as simple as that. Another way to define the message variable is to do it directly inside Vue
constructor in data object.
1https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js
1 2 3 4 5 6 7
1 2 3
Getting Started 5

6
data: {
Both ways have the exact same result, so you are again free to pick whatever syntax you like.
Info
The double curly brackets are not HTML but scripting code, anything inside mustache tags is called binding expression. Javascript will evaluate these expressions. The {{ message }} brings up the value of the Javascript variable. This piece of code {{1+2}} will display the number 3.
2.2 Two-way Binding
What is cool about Vue is that it makes our lives easier. Assume we want to change the message on user input, how can we easily accomplish it? In the example below we use v-model, a directive of Vue (we will cover more on directives in the next chapter). Then we use two-way data binding to dynamically change the message value when the user changes the message text inside an input. Data is synced on every input event by default.

# {{ message }}

Getting Started 6
1
2
3
4 5}
new Vue({
el: '#app',
});
1 2 3 4
1
2
3
4 5} 6 })
new Vue({
el: '#app',
data: {
That’s it. Now our heading message and user input are bound! By using v-model inside the input tag we tell Vue which variable should bind with that input, in this case message .
Getting Started
7
Two-way data binding
Two-way data binding means that if you change the value of a model in your view, everything will be kept up to date.
2.3 Comparison with jQuery.
Probably, all of you have some experience with jQuery. If you don’t, it’s okay, the use of jQuery in this book is minimal. When we use it, its only to demonstrate how things can be done with Vue instead of jQuery and we will make sure everybody gets it.
Anyway, in order to better understand how data-binding is helping us to build apps, take a moment and think how you could do the previous example using jQuery. You would probably create an input element and give it an id or a class, so you could target it and modify it accordingly. After this, you would call a function that changes the desired element to match the input value, whenever the keyup event happens. It’s a real bother.
More or less, your snippet of code would look like this.
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18

Hello Vue

This is a simple example of comparison and, as you can see, Vue appears to be much more beautiful, less time consuming, and easier to grasp. Of course, jQuery is a powerful JavaScript library for Document Object Model (DOM) manipulation, but everything comes with its ups and downs!
Code Examples
You can find the code examples of this chapter on GitHub2.
Getting Started 8
2https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/codes/chapter2.html
Getting Started 9
2.4 Homework
A nice and super simple introductory exercise is to create an HTML file with a Hello, {{name}} heading. Add an input and bind it to name variable. As you can imagine, the heading must change instantly whenever the user types or changes his name. Good luck and have fun!
Example Output
Note
The example’s output makes use of Bootstrap. If you are not familiar with bootstrap you can ignore it for now, it is covered in a later chapter.
Potential Solution
You can find a potential solution to this exercise here3. 3https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter2.html

1 2 3 4 5 6 7 8 9
10 11 12 13 14
3. A Flavor of Directives.
In this chapter we are going through some basic examples of Vue’s directives. Well, if you have not used any Framework like Vue.js or Angular.js before, you probably don’t know what a directive is. Essentially, a directive is a special token in the markup that tells the library to do something to a DOM element. In Vue.js, the concept of directive is drastically simpler than that in Angular. Some of the directives are:
• v-show which is used to conditionally display an element
• v-if which can be used instead of v-show
• v-else which displays an element when v-if evaluates to false.
Also, there is v-for, which requires a special syntax and its use is for rendering (e.g. render a list of items based on an array). We will elaborate about the use of each, later in this book.
Let us begin and take a look at the directives we mentioned.
3.1 v-show
To demonstrate the first directive, we are going to build something simple. We will give you some tips that will make your understanding and work much easier! Suppose you find yourself in need to toggle the display of an element, based upon some set of criteria. Maybe a submit button shouldn’t display unless you’ve first typed in a message. How can we accomplish that with Vue?

Hello Vue

Here we have an HTML file with our known div id="app" and a textarea. Inside the textarea we are going to display our message. Of course, it is not yet bound and by this point you may have already figured it out. Also you may have noticed that in this example we are no longer using the minified version of Vue.js. As we have mentioned before, the minified version shouldn’t be used during development because you will miss out warnings for common mistakes. From now on, we are going to use this version in the book but of course you are free to do as you like.

Hello Vue

{{ $data }}  It is time to bind the value of textarea with our message variable using v-model so it displays our message. Anything we type in is going to change in real time, just as we saw in the example from the previous chapter, where we were using an input. Additionally, here we are using a pre tag to spit out the data. What this is going to do, is to take the data from our Vue instance, filter it through json, and finally display the data in our browser. Vue will nicely format the output for us automatically whether it’s a string, number, array, or a plain object. We believe, that this gives a much better way A Flavor of Directives. 12 to build and manipulate our data, since having everything right in front of you is better than looking constantly at your console. Info JSON (JavaScript Object Notation) is a lightweight data-interchange format. You can find more info on JSON here1. The output of {{$data }} is bound with Vue data and will get updated on every change.
1
2
3 Hello Vue
4
5
6

7

8
9
12

13 {{ $data }} 14  15 16 17 18 26 A Flavor of Directives. 13 Info An element with v-show will always be rendered and remain in the DOM. v-show simply toggles the display CSS property of the element. 1 # You must send a message for help! 2 3 What we want to accomplish in this example, is to toggle different elements. In this step, we need to hide the warning inside the h1 tag, if a message is present. Οtherwise hide the message by setting its style to display: none. 3.2 v-if At this point you might ask ‘What about the v-if directive we mentioned earlier?’. So, we will build the previous example again, only this time we’ll use v-if! 1 2 3 Hello Vue 4 5 6 7 # You must send a message for help! 8 9 12 13 {{$data }}
14 

15

16
17
18
As shown, the replacement of v-show with v-if works just as good as we thought. Go ahead and try to make your own experiments to see how this works! The only difference is that an element with v-if will not remain in the DOM.
3.2.1 Template v-if
If sometime we find ourselves in a position where we want to toggle the existence of multiple elements at once, we can use v-if on a

element. In occasions where the use of div or span doesn’t seem appropriate, the
element can also serve as an invisible wrapper. The
won’t be rendered in the final result.

# You must send a message for help!

Dispatch a messenger immediately!

To nearby kingdom of Hearts!

{{ $data }}  1 2 3 4 5 6 7 8 9 10 11 12 13 14 A Flavor of Directives. 14 A Flavor of Directives. 15 Template v-if Using the setup from the previous example we have attached the v-if directive to the template element, toggling the existence of all nested elements. Warning The v-show directive does not support the syntax. 3.3 v-else When using v-if you can use the v-else directive to indicate an “else block” as you might have already imagined. Be aware that the v-else directive must follow immediately the v-if directive - otherwise it will not be recognized. A Flavor of Directives. 16 1 2 3 Hello Vue 4 5 6 7 # You must send a message for help! 8 ## You have sent a message! 9 10 13 14 {{$data }}
15 

16

17
18
19
27
Vue({
el: '#app',
data: {
message: 'Our king is dead! Send help!'
}
A Flavor of Directives.
17
v-if in action
v-else in action
Just for the sake of the example we have used an h2 tag with a different warning than before, which is displayed conditionally. If no message is presented, we see the h1 tag. If there is a message, we see the h2 using this very simple syntax of Vue v-if and v-else. Simple as a pimple!

A Flavor of Directives. 18
Warning
The v-show directive doesn’t work anymore with v-else, in Vue 2.0.
3.4 v-if vs. v-show
Even though we have already mentioned a difference between v-if and v-show , we can deepen a bit more. Some questions may arise out of their use. Is there a big difference between using v-show and v-if? Is there a situation where performance is affected? Are there problems where you’re better off using one or the other? You might experience that the use of v-show on a lot of situations causes bigger time of load during page rendering. In comparison, v-if is truly conditional according to the guide of Vue.js.
When using v-if, if the condition is false on initial render, it will not do anything - - the conditional block won’t be rendered until the condition becomes true for the first time. Generally speaking, v-if has higher toggle costs while v-show has higher initial render costs. So prefer v-show if you need to toggle something very often, and prefer v-if if the condition is unlikely to change at runtime.
So, when to use which really depends on your needs.
Code Examples
You can find the code examples of this chapter on GitHub2.
2https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter3
A Flavor of Directives. 19
3.5 Homework
Following the previous homework exercise, you should try to expand it a bit. The user now types in his gender along with his name. If user is a male, then the heading will greet the user with “Hello Mister {{name}}”. If user is a female, then “Hello Miss {{name}}” should appear instead.
When gender is neither male or female then the user should see the warning heading “So you can’t decide. Fine!”.
Hint
A logical operator would come handy to determine user title.
Example Output
Potential Solution
You can find a potential solution to this exercise here3. 3https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter3.html

4. List Rendering
In the fourth chapter of this book, we are going to learn about list rendering. Using Vue’s directives we are going to demonstrate how to:
1. Render a list of items based on an array.
2. Repeat a template.
3. Iterate through the properties of an object.
4.1 Install & Use Bootstrap
To make our work easier on the eye, we are going to import Bootstrap.
Info
Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web.
Head to http://getbootstrap.com/1 and click the download button. For the time being, we’ll just use Bootstrap from the CDN link2 but you can install it in any way that suits your particular needs. For our example we need only one file, for now: css/bootstrap.min.css. When we use this .css file in our app, we have access to all the pretty structures and styles. Just include it within the head tag of your page and you are good to go.
Bootstrap requires a containing element to wrap site contents and house our grid system. You may choose one of two containers to use in your projects. Note that, due to padding and more, neither container is nestable.
• Use .container for a responsive fixed width container.
1

2 ...
3

• Use.container-fluidforafullwidthcontainer,spanningtheentirewidthofyourviewport.
1http://getbootstrap.com/ 2https://www.bootstrapcdn.com/

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
1

2 ...
3

At this point, we would like to make an example of Vue.js with Bootstrap classes. Of course, not much study or experimentation is required in order to make use of combined Vue and Bootstrap.

Hello Bootstrap

# Hello Bootstrap, sit next to Vue.

List Rendering 21
type="text/javascript">
Vue({
el: '.container'
})

Notice this time, instead of targeting app id, we have targeted the container class within the el
option inside the Vue instance.
Tip
In the above example we target the element with class of .container. Be careful when you are targeting an element by class, when the class is present more than 1 time, Vue.js will mount on the first element only.
The el: property can be a CSS selector or an actual HTML Element. It is not recommended to mount the root instance to or .

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4.2 v-for
In order to loop through each item in an array, we will use the v-for directive.
This directive requires a special syntax in the form of item in array where array is the source data
Array and item is an alias for the Array element being iterated on. Warning
If you are coming from the php world you may notice that v-for is similar to php’s foreach function. But be careful if you are used to foreach($array as$value).
Vue’s v-for is exactly the opposite, value in array. The singular first, the plural next.
4.2.1 Range v-for
Directive v-for can also take an integer. Whenever a number is passed instead of an array/object, the template will be repeated as many times as the number given.
Hello Vue

# The multiplication table of 4.

• {{ i-1 }} times 4 equals {{ (i-1) * 4 }}.

List Rendering 22
type=”text/javascript”>
Vue({
el: ‘.container’
})

List Rendering 23 The above code displays the multiplication table of 4.
Multiplication Table of 4
Note
Because we want to display all the multiplication table of 4 (until 40) we repeat the template 11 times since the first value i takes is 1.
4.3 Array Rendering 4.3.1 Loop Through an Array
In the next example we will set up the following array of Stories inside our data object and we will display them all, one by one.

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
stories: [
“I crashed my car today!”,
“Yesterday, someone stole my bag!”,
“Someone ate my chocolate…”,
]
What we need to do here, is to render a list. Specifically, an array of strings.

Stories

# Let’s hear some stories!

• Someone said “{{ story }}”
{{ $data }}  List Rendering 24 type=”text/javascript”> Vue({ el: ‘.container’, data: { stories: [ }) ] } “I crashed my car today!”, “Yesterday, someone stole my bag!”, “Someone ate my chocolate…”, List Rendering 25 Info Both list-group and list-group-item classes are Bootstrap classes. Here you can find more information about Bootstrap list styling.3 Rendering an array using v-for. Using v-for we have managed to display our stories in a simple unordered list. It is really that easy! 4.3.2 Loop Through an Array of Objects Now, we alter the Stories array to contain story objects. A story object has 2 properties: plot and writer. We will do the same thing we did before but this time instead of echoing story immediately, we will echo story.plot and story.writer respectively. 3http://getbootstrap.com/css/#type- lists 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 Stories # Let’s hear some stories! • {{ story.writer }} said “{{ story.plot }}” {{$data }}


Additionally, when you need to display the index of the current item, you can use the index special variable. It works like this:

• {{index}} {{ story.writer }} said “{{ story.plot }}”

The index inside the curly braces, clearly represents the index of the iterated item in the given example.
Rendered array with index

List Rendering 28
4.4 Object v-for
You can use v-for to iterate through the properties of an Object. We mentioned before that you can bring to display the index of the array, but you can also do the same when iterating an object. In addition to index, each scope will have access to another special property, the key.
Info
When iterating an object, index is in range of 0 … n-1 where n is the number of object properties.
We have restructured our data to be a single object with 3 attributes this time: plot, writer and upvotes.

# Let’s hear some stories!

• {{ value }}

new Vue({
el: ‘.container’, data: {
story: {
plot: “Someone ate my chocolate…”,
writer: ‘John’,
} }
})
We can provide a second and third argument, for the key and index respectively.

7 8 9
10
{{index}} : {{key}} : {{value}}

As you can see in the example code above, we use key and index to bring inside the list, the key-value pairs, as well as the index of each pair.
The result will be:
Iterate though object’s properties.
Code Examples
You can find the code examples of this chapter on GitHub4.
4https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter4
List Rendering 29

# Let’s hear some stories!

• List Rendering 30
4.5 Homework
Keeping in mind what we reviewed in this chapter, for this homework, create an object with your personal attributes. By personal attributes, I mean your name, weight, height, eyeColor, and your favoriteFood.
Using v-for, iterate through each property and bring it into display in the format of: index: key = value.
Example Output
Potential Solution
You can find a potential solution to this exercise here5. 5https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter4.html

5. Interactivity
In this chapter, we are going to create and expand previous examples, learn new things concerning ‘methods’, ‘event handling’ and ‘computed properties’. We will develop a few examples using different approaches. It’s time to see how we can implement Vue’s interactivity to get a small app, like a Calculator, running nice and easy.
5.1 Event Handling
HTML events are things that happen to DOM elements. When Vue.js is used in HTML pages, it can react to these events.
Events can represent everything from basic user interactions, to things happening in the rendering model.
These are some examples of HTML events:
• A web page has finished loading • An input field was changed
• A button was clicked
• A form was submitted
The point of event handling is that you can do something whenever an event takes place. In Vue.js, to listen to DOM events you can use the v-on directive.
The v-on directive attaches an event listener to an element. The type of the event is denoted by the argument, for example v-on:keyup listens to the keyup event.
Info
The keyup event occurs when the user releases a key. You can find a full list of HTML events here1.
5.1.1 Handling Events Inline
Enough with the talking, let’s move on and see event handling in action. Below, there is an ‘Upvote’ button which increases the number of upvotes every time it gets clicked.
1http://www.w3schools.com/tags/ref_eventattributes.asp

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Upvote

Interactivity 32
Interactivity
33
There is an upvotes variable within our data. In this case, we bind an event listener for click, with the statement that is right next to it. Inside the quotes, each time the button is pressed we’re simply increasing the count of upvotes by one, using the increment operator (upvotes++).
5.1.2 Handling Events using Methods
Now we are going to do the exact same thing as before, using a method instead. A method in Vue.js is a block of code designed to perform a particular task. To execute a method, you have to define it and then invoke it.
1
2
3
5 Upvote
6
7
8

9

We are binding a click event listener to a method named ‘upvote’. It works just as before, but cleaner and easier to understand when reading your code.
Warning
Event handlers are restricted to execute one statement only.
5.1.3 Shorthand for v-on
When you find yourself using v-on all the time in a project, you will find out that your HTML will quickly become dirty. Thankfully, there is a shorthand for v-on, the @ symbol. The @ replaces the entire v-on: and when using it, the code looks a lot cleaner. Using the shorthand is totally optional.
With the use of @ the button of our previous example will be:

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
Listening to ‘click’ using v-on:

Listening to ‘click’ using @ shorthand

5.2 Event Modifiers
Now we will move on and create a Calculator app. To do so, we’ll use a form with two inputs and one dropdown, to select the desired operation.
Even though the following code seems fine, our calculator does not work as expected.

Calculator

# Type 2 numbers and choose operation.

Interactivity 35

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

## Result: {{a}} {{operator}} {{b}} = {{c}}

{{ $data }}  case “+”: this.c = this.a + break; case “-“: this.c = this.a – break; case “*”: this.c = this.a * break; case “/”: this.c = this.a / break; this.b this.b this.b this.b Interactivity 37 If you try to run this code yourself, you will find out that when the “calculate” button is clicked, instead of calculating, it reloads the page. This makes sense, because when you click “calculate”, in the background, you are submitting the form and thus the page reloads. To prevent the submission of the form, we have to cancel the default action of the onsubmit event. It is a very common need to call event.preventDefault() inside our event handling method. In our case the event handling method is called calculate. So, our method will become: calculate: function(event){ event.preventDefault(); switch (this.operator) { } } case “+”: this.c = this.a + this.b break; case “-“: this.c = this.a – this.b break; case “*”: this.c = this.a * this.b break; case “/”: this.c = this.a / this.b break; Interactivity 38 Using Event Modifiers to build a calculator. Although we can do this easily inside methods, it would be better if the methods can be purely ignorant about data logic rather than having to deal with DOM event details. Vue.js provides four event modifiers for v-on to prevent the event default behavior: 1. .prevent 2. .stop 3. .capture 4. .self So, using .prevent, our submit button will change from: 1 to: 1 2 And we can now safely remove event.preventDefault() from our calculate method. Note .capture and .self are rarely used so we won’t bother elaborating any further. If you are interested in learning more about Event Order have a look at this tutorial2. 2http://www.quirksmode.org/js/events_order.html Interactivity 39 5.3 Key Modifiers When you focus on one of the inputs and you hit enter, you will notice that the calculate method is getting invoked. If the button wasn’t inside the form, or if there was no button at all, you could listen for a keyboard event instead. When listening for keyboard events, we often need to check for key codes. The key code for Enter button is 13. So we could use it like this: 1 Remembering all the keyCodes is a hassle, so Vue provides aliases for the most commonly used keys: • enter • tab • delete • esc • space • up • down • left • right So, to execute calculate method when Enter is pressed in our example, the inputs will be like this: 1 2 Tip When you have a form with a lot of inputs/buttons/etc and you need to prevent their default submit behavior, you can modify the submit event of the form. For example: Finally, the calculator is up and running. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 5.4 Computed Properties Vue.js inline expressions are very convenient, but for more complicated logic, you should use computed properties. Practically, computed properties are variables which their value depends on other factors. Computed properties work like functions that you can use as properties. But there is a significant difference. Every time a dependency of a computed property changes, the value of the computed property re-evaluates. In Vue.js, you define computed properties within the computed object inside your Vue instance. Hello Vue a={{ a }}, b={{ b }} {{$data }}

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Interactivity 41 We’ve set two variables, the first, a, is set to 1 and the second, b, will be set by the returned result of
the function inside the computed object. In this example the value of b will be set to 2.

Hello Vue

a={{ a }}, b={{ b }}

{{ $data }}  The above example is the same as the previous one, but with one difference. An input is bound to the a variable. The desired outcome would be to change the value of the binded attribute and immediately update the result of b. But notice here, that it does not work as we would expect. If you run this code and set variable a to 5, you expect that b will be equal to 6. Sure, but it doesn’t, b is set to 51. Interactivity 42 Why is this happening? Well, as you might have already thought, b takes the given value from the input a as a string, and appends the number 1 at the end of it. One possible solution is to use the parseFloat() function that parses a string and returns a floating point number. new Vue({ el: ‘.container’, data: { a: 1, }, computed: { b: function () { return parseFloat(this.a) + 1 } } }); Another option that comes to mind, is to use the which is used for input fields that should contain a numeric value. But there is a more neat way. With Vue.js, whenever you want user’s input to be automatically persisted as number, you can append the special modifier .number. a={{ a }}, b={{ b }} {{$data }}

The number modifier is going to give us the desired result without any further effort.
To demonstrate a wider picture of computed properties, we are going to make use of them and build
the calculator we have already shown, but this time using computed properties instead of methods. Lets start with a simple example, where a computed property c contains the sum of a plus b.
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Hello Vue

# Enter 2 numbers to calculate their sum.

+

## Result: {{a}} + {{b}} = {{c}}

 {{ $data }}  The initial code is ready, and at this point the user can type in 2 numbers and get their sum. A calculator that can do the four basic operations is the goal, so let’s continue building! Since the HTML code will be the same with the calculator we build in the previous section of this chapter (except now we don’t need a button), I am am going to show only the Javascript codeblock. Interactivity 43 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 new Vue({ el: ‘.container’, data: { a: 1, b: 2, operator: “+”, }, computed: { c: function () { switch (this.operator) { case “+”: return this.a + this.b break; case “-“: return this.a – this.b break; case “*”: return this.a * this.b break; case “/”: return this.a / this.b break; } } }, }); Interactivity 44 The calculator is ready for use. The only thing we had to do, was to move whatever was inside calculate method to the computed property c! Whenever you change the value of a or b the result updates in real time! We don’t need any buttons, events, or anything. How awesome is that?? Note Note here that a normal approach would be to have an if statement to avoid error of division. But, there is already a prediction for this kind of flaws. If the user types 1/0 the result automatically becomes infinity! If the user types a text the displayed result is “not a number”. Interactivity 45 Calculator built with computed properties. Code Examples You can find the code examples of this chapter on GitHub3. 3https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter5 Interactivity 46 5.5 Homework Now that you have a basic understanding of Vue’s event handling, methods, computed properties etc, you should try something a bit more challenging. Start by creating an array of “Mayor” candidates. Each candidate has a “name” and a number of “votes”. Use a button to increase the count of votes for each candidate. Use a computed property to determine who is the current “Mayor”, and display his name. Finally, add an input. When this input is focused, and key ‘delete’ is pressed, the elections start from the beginning. This means that all votes become 0. Hint Javascript’s sort() and map() methods could prove very useful and Key modifiers will get you there. Example Output Interactivity 47 Potential Solution You can find a potential solution to this exercise here4. 4https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter5.html 6. Filters In the two previous chapters we reviewed list rendering, methods, and computed properties. Now it is a good time to make some examples using all the information above. In this chapter, we will cover how to: 1. Filter an array of items. 2. Order an array of items. 3. Apply a custom filter. 4. Use utility libraries. The plan is to go through similar examples as before, combining some or all of the techniques we saw. 6.1 Filtered Results Sometimes we need to display a filtered version of an array without actually mutating or resetting the original data. Continuing the previous example, Loop Through an Array of Objects, we would like to display one list with the stories written by Alex and one list with the stories written by John. We can achieve this, by creating a method which filters our array and returns the results to be rendered. Info As of Vue 2.0, Vue filters cannot be used within v-for. Filters can now only be used inside text interpolations ({{ }}). Vue’s team suggests to move filters’ logic into JavaScript, so that it can be reused throughout your component. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 User Stories # Let’s hear some stories! ### Alex’s stories • {{ story.writer ### John’s stories }} said “{{ story.plot }}” • {{ story.writer }} said “{{ story.plot }}” {{$data }}

As it appears we have created a method named storiesBy which takes in a writer, as argument, and returns a filtered array with the writer’s stories. We can then use this, to display each writer’s stories using the v-for directive in the format of story in storiesBy(‘Alex’), as you can see in the example above.
1https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Filters 50
plot: “Someone ate my chocolate…”,
Info
Within storiesBy method, we use javascript’s built-in filter function.1 The filter() function creates a new array with all elements that pass the test, implemented by the provided function.

Filters
51
Note
Stories filtered by writer.
As you may noticed, our li tag is getting really big, so we have splitted it in more lines. The actual result remains the same as with the use of filters, introduced in Vue 1.x.
Simple enough, right?
6.1.1 Using Computed Properties
A computed property can also be used to filter an array. Using a computed property to perform array filtering gives you in-depth control and more flexibility, since it’s full JavaScript, and allows you to access the filtered result elsewhere. For example you can get the length of a filtered array anywhere in your code.
First we will enhance our Stories with a new property, called upvotes. Then, we will filter the famous stories. As famous, we define the stories that have more than 25 upvotes. This time, we will create a computed property that returns the filtered Array.
Filters 52
new Vue({
el: ‘.container’, data: {
stories: [ {
plot: “I crashed my car today!”,
writer: “Alex”,
}, {
}, {
}, {
}, ]
},
computed: {
famous: function() {
return this.stories.filter(function(item){
} }
})
plot: “Yesterday, someone stole my bag!”,
writer: “John”,
plot: “Someone ate my chocolate…”,
writer: “John”,
plot: “I ate someone’s chocolate!”,
writer: “Alex”,
In our HTML code, instead of stories array, we will render the famous computed property.
Filters 53

# Let’s hear some famous stories! ({{famous.length}})

• {{ story.writer }} said “{{ story.plot }}”
and upvoted {{ story.upvotes }} times.

Filter array using a computed property
That’s it. We have filtered our array using a computed property. Did you notice how easily we man-
aged to display the number of famous stories next to our heading message using {{famous.length}}?
Next we will implement a very basic (but awesome) search. When the user types a part of a story, we can guess which story it is and who wrote it, in real time. We’ll add a text input, bound to an empty variable query, so we can dynamically filter our Stories array.

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16

# Lets hear some stories!

### Search results:

Filters 54

• {{ story.writer }} said “{{ story.plot }}”

Then we will create a computed property named search. Along with the built-in filter function, we are going to use the includes2 Javascript’s function, which determines whether one string may be found within another string.
new
Vue({
el: ‘.container’,
data: {
stories: […],
query: ‘ ‘
}, methods:{
storiesBy: function (writer) {
return this.stories.filter(function (story) {
return story.writer === writer })
} },
computed: {
search: function () {
var query = this.query
2https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/String/includes

17
18
19
20
21
22
return this.stories.filter(function (story) { return story.plot.includes(query)
}) }
} })
Filters 55
Filters
56
Search Stories.
Filters
57
Isn’t that awesome??
Searching for ‘choco’.
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
6.2 Ordered Results
Sometimes, we may want to display the items of an Array ordered by some criteria. We can use a computed property to display our array, ordered by the count of each story’s upvotes. To sort the array we are going to use JavaScript’s sort3 function, which sorts the elements of an array in place and returns the array.
The more famous a story is, the higher it should appear.

Famous Stories

# Let’s hear some stories!

• {{ story.writer }} said “{{ story.plot }}”
and upvoted {{ story.upvotes }} times.
{{ $data }}  Stories array ordered by upvotes. Hmmm, the array is ordered but this is not what we expected. We wanted the famous stories first. To change the order of the sorted array we have to take a look at the sort function. In JavaScript’s sort(compareFunction), if compareFunction is supplied, the array elements are sorted according to the return value of compareFunction. If a and b are two elements being compared, then: • IfcompareFunction(a,b)islessthan0,sortatoalowerindexthanb. • IfcompareFunction(a,b)is0,leaveaandbunchanged. • IfcompareFunction(a,b)isgreaterthan0,sortbtoalowerindexthana. In our use case, the compareFunction will be: Filters 59 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 and upvoted {{ story.upvotes }} times. {{$data }}

Filters 60
compareFunction
function(a, b){
}
So, to change the order from ascending to descending, we can multiple the returned value by -1. (return (a.upvotes – b.upvotes) * -1)
We can change the order dynamically, by using a variable, order. A button will be used, which will toggle the value of the new variable, between -1 and 1.
1
2
3
4
5 6> 7

<

div class=”container”>

# Let’s hear some stories!

<

ul class=”list-group”>
new
Vue({
el: ‘.container’,
data: {
stories: […],
order : -1 },
computed: {
orderedStories: function () {
var order = this.order;
return this.stories.sort(function(a, b) {
} }
})
<li
v-for=”story in orderedStories”
class=”list-group-item”
{{ story.writer }} said “{{ story.plot }}”
Filters 61
We initialize order variable with the value of -1 and then we pass it to our computed property, so every time the button is clicked, the variable changes value and the array changes order.
Array in descending order

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
6.3 Custom Filters
To demonstrate custom filters, we will make a new simple example. Assume we are now in charge of the Gotham’s city news paper the “The Gotham Gazette”. Our first job is to spread the news of the secret identities of heroes. We know the first and last names of them, and we want to make a nice list where each secret identity will be exposed. This is where the global Vue.filter() method comes in, to create a filter which can take a hero and return all his information for display, without polluting our HTML code. To register a filter we can pass in a filterID and a filterFunction, which returns a processed value. Then we will use the filter inside text interpolations like so:

The Gotham Gazette

# Real identities of Super Heroes!

• {{ hero | snitch }}

6.4 Utility Libraries
Custom filter ‘famous’ in action.
At this point, we would like to point out that when you need to sort/filter/index data in more advanced ways, you should consider using a JavaScript utility library. There are some great utility libraries out there, such as Lodash4, Underscore5, Sugar6, etc.
To get a better understanding, we are going to include Lodash and update the previous example. If you follow along, make sure you include Lodash from a cdn in your HTML file.
4https://lodash.com 5http://underscorejs.org/ 6https://sugarjs.com/

Filters 64
Lodash’s orderBy method returns a new sorted array. So, we’ll use it within our computed property to sort the Stories array.
Syntax
Lodash’s orderBy syntax is:
.orderBy(collection, [iteratees=[.identity]], [orders])
Don’t let the second argument confuse you. It is really simple. The first argument represents the array you want to sort. The second argument expects an array of keys, that the sorting will be based on. The third argument, expects an array of orders for each key.
For example if we had an array:
var kids = [
{ name: ‘Stan’, strength: 70, intelligence: 70}, { name: ‘Kyle’, strength: 40, intelligence: 80}, { name: ‘Eric’, strength: 45, intelligence: 80}, { name: ‘Kenny’, strength: 100, intelligence: 70}
]
And we run:
_.orderBy(kids, [‘intelligence’, ‘strength’], [‘desc’, ‘asc’]) Our array will have this order:
var kids = [
{ name: ‘Kyle’, strength: 40, intelligence: 80}, { name: ‘Eric’, strength: 45, intelligence: 80}, { name: ‘Stan’, strength: 70, intelligence: 70}, { name: ‘Kenny’, strength: 100, intelligence: 70}
]
Because the array is primarily sorted by kid’s intelligence in descending order and secondary, by kid’s strength in ascending order.
We will use _.orderBy within our computed property like this:
Filters 65
computed: {
orderedStories: function () {
var order = this.order
}
This works, but if no orders argument is passed, the array will be sorted in ascending order. Additionally, it should be possible to change the order of the array with a button, just like before. To do it dynamically, we can set a data property order : ‘desc’ and create a method to change its value:
methods: {
reverseOrder: function () {
this.order = (this.order === ‘desc’) ? ‘asc’ : ‘desc’ }
},
computed: {
orderedStories: function () {
var order = this.order
} }
And the button will be almost the same, but not quite.

As you may have noticed, we had to do the exact same change 3 times and I don’t know about you, but I hate repeating myself. If it doesn’t seem like a big deal for you, imagine that you may use the above code block in 100 places, what would you do then? Fortunately, Vue provides a solution for that kind of situations, and this solution has a name, Component.
Tip
Whenever you find yourself repeating a piece of functionality, the most efficient way to deal with it is to create a dedicated Component.
Luckily, we have created a story Component in the previous example, which displays the writer and the body for a specified story. We can use the custom element inside our HTML and pass each story, as we did before, with the :story tag. This time we will use it inside a v-for directive.
So our code will be:
Note
In this particular example syntax highlighting is turned off.

Components 76

# Lets hear some stories!

### Search results:

If you try to run this code you will get the following warning:
Vue warn: Unknown custom element: – did you register the component correctly? For recursive components, make sure to provide the “name” option.
1 2 3 4
Vue warning
To fix this, we need to register the Component again. This time we have to make some changes to the component’s template. We will change plot attribute to body and

<

h1> tag to

• to suit our needs.
So, the story’s template will be:

• {{ story.writer }} said “{{ story.body }}”

• The component will remain the same.
Vue.component(‘story’, {
props: [‘story’],
template: ‘#story-template’
});
If you run the above code, you will see for yourself that everything works the same as before, but this time with the use of a custom component.
Pretty neat huh?
Warning
Please be responsible. Don’t drink and drive.
Components
77

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
7.6 Altogether
Using our newly acquired knowledge, we should be able to build something a bit more complex. Based on the structure of the example above, we are going to create a voting system for our stories, and add a mark as favorite feature. The way to accomplish this, is through methods, directives, and of course, components.

Hello Vue

# Let’s hear some stories!

{{ $data }} • {{ story.writer }} said “{{ story.plot }}” • 54 The next step allows the user to give a vote to the story he prefers. To apply this limit (1 vote per story) we will display the ‘Upvote’ button only if the user has not already voted. So, every story must have a voted property, that becomes true when upvote function executes. • {{ story.writer }} said “{{ story.plot }}”. Story upvotes {{ story.upvotes }}. • class=”btn btn-default” Components 80 Vue.component(‘story’, { template: “#story-template”, props: [‘story’], methods:{ upvote: function(){ this.story.upvotes += 1; this.story.voted = true; }, } }); new Vue({ el: ‘#app’, data: { stories: [ { plot: ‘My horse is amazing.’, writer: ‘Mr. Weebl’, upvotes: 28, voted: false, }, { }, { }, { }, ] } }) plot: ‘Narwhals invented Shish Kebab.’, writer: ‘Mr. Weebl’, upvotes: 8, voted: false, plot: ‘The dark side of the Force is stronger.’, writer: ‘Darth Vader’, upvotes: 49, voted: false, plot: ‘One does not simply walk into Mordor’, writer: ‘Boromir’, upvotes: 74, voted: false, Components 81 Ready to vote! We have implemented, with the use of methods, the voting system. I think it looks good, so we can continue with the ‘favorite story’ part. We want the user to be able to choose only one story to be his favorite. The first thing that comes to my mind is to add one new empty object (favorite) and whenever the user chooses one story to be his favorite, update favorite variable. This way we will be able to check if a story is equal to the user’s favorite story. Let’s do this. • {{ story.writer }} said “{{ story.plot }}”. Story upvotes {{ story.upvotes }}. Components 82 • Vue.component(‘story’, { template: “#story-template”, props: [‘story’], methods:{ upvote: function(){ this.story.upvotes += 1; this.story.voted = true; }, setFavorite: function(){ this.favorite = this.story; }, }, computed:{ isFavorite: function(){ return this.story == this.favorite; }, } }); new Vue({ el: ‘#app’, data: { stories: [ … ], favorite: {} } }) If you try to run the above code, you will notice that it does not work as it should be. Whenever you try to favorite a story, the variable favorite inside$data remains null.
It seems that our story component is unable to update favorite object, so we are going to pass it on each story and add favorite to component’s properties.
Components 83

Vue.component(‘story’, {

props: [‘story’, ‘favorite’],

});
setFavorite method malfunctioning
Hmmm, favorite still doesn’t get updated when setFavorite is executed. The button disappears as expected and a star icon appears, but variable favorite is still null. This results in the user being able to favorite all stories.
The problem with this approach is that we don’t keep things synced. By default, all props form a one- way-down binding between the child property and the parent. When the parent property updates, it will flow down to the child, but not the other way around.
We can’t synchronize child’s data with parent’s, with what we know so far. So, we’ll take a break and study Vue’s Custom Events, before we go any further.

Components
84
Code Examples
You can find the code examples of this chapter on GitHub1.
1https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter7
Components 85
7.7 Homework
Create an array of planets. Each planet must have a name and a number of visits.
You can choose to travel to any planet, but you are limited to 3 visits per planet due to shortage of
fuel.
You should have a Planet component with the appropriate methods/computed properties. When rendered, each planet should display:
• its name
• the number of visits
• a Visit button (if max number of visits has not been reached) • an icon to indicate if planet has been visited at least once
Example Output
Potential Solution
You can find a potential solution to this exercise here2. 2https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter7.html

1 2 3 4 5 6 7 8 9
10 11 12 13 14
8. Custom Events
Some times it is needed to fire a custom event. To do so, we can use Vue Instance methods. Every Vue instance implements the Events interface1.
This means that it can:
• Listen to an event using $on(event). • Trigger an event using$emit(event). It can also:
• Listen to an event, but only once, using $once(event). • Remove event listeners using$off().
8.1 Emit and Listen
The following codeblock represents a page with a counter and a Vote button. When the button is clicked, it emits an event, named ‘voted’. There is also an Event Listener for the event, which increases the number of votes when the event is triggered.

Emit and Listen

1http://vuejs.org/api/#Instance- Methods- Events

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

Custom Events 87
Custom Events
88
Example Output
We register the event listener within the created Lifecycle Hook. this is bound to the Vue Instance within vote method and created hook. So, we can access $on and$emit functions using this.$on and this.$emit.
8.1.1 Lifecycle Hooks
Lifecycle Hooks are functions who execute when Vue related events happen. In Vue 2, these hooks are:
Custom Events
89
Hook
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
activated
deactivated
beforeDestroy
destroyed
Called
After the instance has just been initialized, before data observation and event/watcher setup.
After the instance is created.
Right before the mounting begins.
After the instance has just been mounted to the DOM.
When the data changes, before the virtual DOM is re-rendered
and patched.
After a data change causes the virtual DOM to be re-rendered and patched.
When a kept-alive component is activated.
When a kept-alive component is deactivated.
Right before a Vue instance is destroyed.
After a Vue instance has been destroyed.
You don’t have to know all these, but it is good to be aware of their existence. If you want to learn more about Lifecycle Hooks check Vue’s API2.
8.2 Parent-Child Communication
Things are getting a bit different when a parent component needs to listen to an event of a child component. We can’t use this.$on/this.$emit since this will be bound to different instances.
Remember the v-on (@) event listener? A parent component can listen to the events emitted from a child component using v-on directly in the template, where the child component is used.
Following the previous example, I’ll create a food component which will have a name property. In its template, it will show a button displaying its name. When the button is clicked, we want to emit the vote event.
Food Component
Vue.component(‘food’, {
template: ‘#food’,
props: [‘name’],
methods: {
vote: function () { this.$emit(‘voted’) } }, }) 2http://vuejs.org/api/#Options- Lifecycle- Hooks Custom Events 90 Food Component’s Template In the parent instance, the Custom Events 93 Multiple Component Instances Nothing new so far. To make the App more fancy, we can add a Vote Log. The log will get updated every time a food is voted. We have to update the child component, to pass the food’s name when emitting the voted event. Info The$emit function, along with the event name argument, it passes any additional arguments to listener’s callback function. For example: vm.$emit(‘voted’, ‘Alex’, ‘Sunday’, ‘Bob Ross’) We have two options to access food’s name. One is obviously from component’s name property. The second one, is to access the element which triggered the event and find its text content. We’ll go with the second one. We can log the event variable to the console, within Food’s vote method, to find out how we can access the clicked element. Custom Events 94 Food Component Vue.component(‘food’, { methods: { vote: function (event) { } } }) console.log(event) this.votes++ this.$emit(‘voted’)
event.srcElement
If you are following along, you will see that we have access to the clicked element within event.srcElement attribute. The name can be found in both event.srcElement.outerText and event.srcElement.textContent.
So, lets pass one of these to the $emit function. Custom Events 95 Food Component Vue.component(‘food’, { methods: { vote: function () { } }) this.votes++ this.$emit(‘voted’, event.srcElement.textContent) }
Within the parent, we will push the incoming vote to a log array.
new Vue({
el: ‘.container’, data: {
log: [] },
methods: {
countVote: function (food) {
this.log.push(food + ‘ received a vote.’)
} }
})
To display the log, we can add a list to the HTML template.

# Log:

• {{ vote }}

Custom Events
96
Pretty easy, huh?
8.4 Non Parent-Child Communication
We’ll take the previous example a step further, by adding a Reset button. When it’s clicked, it will reset all vote counters. As you can imagine, the reset button will emit an event that should be handled by all components. But how can we catch this event within the child components?
If we go with the way, we can listen for the voted event, but we don’t have a way to emit events to the child components.
To make all components able to communicate with each other, we will use an empty Vue instance as a central event bus. Then, within the components created hook, we will register the event listeners using bus.$on instead of this.$on. Accordingly, we will use bus.$emit to fire all events. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 HTML # Food Battle {{ votes.count }} # Log: • {{ vote }} JavaScript 10 11 12 13 14 15 }, methods: { vote: function (event) { // instead of using this.name // we can access event’s element’s text var food = event.srcElement.textContent; Custom Events 97 1 2 3 4 5 6 7 8 9} var bus = new Vue() Vue.component(‘food’, { template: ‘#food’, props: [‘name’], data: function () { return { votes: 0 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 this.votes++ bus.$emit(‘voted’, food)
},
reset: function () { this.votes = 0
} },
created () {
bus.$on(‘reset’, this.reset) } }) new Vue({ el: ‘.container’, data: { votes: { count: 0, log: [] } }, methods: { countVote: function (food) { this.votes.count++ this.votes.log.push(food + ‘ received a vote.’) }, reset: function () { this.votes = { count: 0, log: [] } bus.$emit(‘reset’)
}
},
created () {
bus.$on(‘voted’, this.countVote) } }) Custom Events 98 Custom Events 99 Warning Non Parent-Child Communication Notice here that we are using: bus.$on(‘voted’, this.countVote) If instead we were trying to use it like:
bus.$on(‘voted’, function(){ this.vote(food) }) it would have thrown an error, since this would be bounded to the bus instance instead of the current component’s instance. 8.5 Removing Event Listeners To remove one or more event listeners, we can use$off. The $off([event, callback]) method can be used in several ways. Custom Events 100 1.$off(), with no arguments, removes all event listeners.
2. $off([event]), removes all event listeners for the specified event. 3.$off([event, callback]) removes event’s listener for the specific callback.
To see it in action, we’ll add a Stop button to stop the votes from being counted/logged. We’ll place a stop method in the Vue instance.
new Vue({ …
methods: {
} })

stop: function () {
bus.$off([‘voted’]) } If we go with bus.$off([‘voted’]), you see that after Stop button is clicked, the upcoming votes are not added to the total count. Also, they don’t show up in the log. Though, if you hit the Reset Votes button, the Food components’ votes are being reseted to 0.
To disable the reset listener too, we can remove all event listeners using bus.$off(). 8.6 Back to stories Remember the Stories example of the previous chapters? We were about to synchronize component’s data with parent’s data. The solution seems obvious now. We’ll use Story component like this: Within Story component, we’ll emit the update event when a story is marked as favorite. The story being favorite, should be passed as an argument on emit. Custom Events 101 Story Component Vue.component(‘story’, { methods:{ updateFavorite: function(){ // ‘update’ is just the name of the custom event // it could be anything. ex: fav-update this.$emit(‘update’, this.story)
} }
… });
In the parent instance, we’ll add a favorite variable to the data. Also, we’ll create a new method, which will update favorite variable when called.
Parent Instance
new Vue({ …
data: { …
favorite: {}
},
methods: {
updateFavorite: function(story) {
this.favorite = story; }
}, })

Custom Events
102
Favorite only one story
Now, the desired result is achieved and the user is able to choose only one story to be his favorite, while he can vote as many stories as he wants.
Info
In Vue 2, bindings are always one-way. To keep data in sync between Parent-Child you have to use Events.
Code Examples
You can find the code examples of this chapter on GitHub3.
3https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter8

Custom Events 103
8.7 Homework
This is the most difficult exercise so far, so make sure to put in use everything you have learned in this book. Create an array of 4 horse-drawn chariots. Each chariot has a “name” and a number of “horses” (from 1 to 4). Create a component named “chariot”. The “chariot” component should display the name of the chariot and the number of the horses it has. It must also have an action button. The button’s text depends on the currently selected chariot.
More specifically, button’s text should be:
• ‘Pick Chariot’, before the user has chosen any chariot
• ‘Dismiss Horses’, when the chariot has less horses than the selected chariot
• ‘Hire Horses’, when the chariot has more horses than the selected chariot
• ‘Riding!’, when the chariot is the selected chariot (this button has to be disabled)
The user should be able to pick a chariot and then choose between any chariot he wants to.
Example Scenario: User has chosen a chariot with 2 horses and its button says ‘Riding!’. A chariot with 3 horses has one more horse, so its button says ‘Hire Horses’. A chariot with 1 horse has one less horse than user’s chariot, so its button says ‘Dismiss Horses’. I think you got the idea..
Hint
You need to keep in sync child’s and parent’s currentChariot property.
Hint
To disable a button use disabled=”true” attribute. You have to figure out how to apply it conditionally.

Custom Events
104
Example Output
Potential Solution
You can find a potential solution to this exercise here4. 4https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter8.html

1. Class and Style Bindings
9.1 Class binding 9.1.1 Object Syntax
A common need for data binding is to manipulate an element’s class and its styles. For such cases, you can use v-bind:class. This can be used to apply classes conditionally, toggle them and/or apply many of them using one binded object et al.
The v-bind:class directive takes an object with the following format as an argument {
‘classA’: true, ‘classB’: false, ‘classC’: true
}
and applies all classes with true value to the element. For example, the classes of the following element, will be classA and classC.

data: {
elClasses:
}
{
‘classA’: true,
‘classB’: false,
‘classC’: true }
To demonstrate how v-bind is used with class attributes, we are going to make an example of class toggling. Using v-bind:class directive, we are going to dynamically toggle the class of div elements.
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Hello Vue

53
Toggle boxes’ color
Class and Style Bindings
108
Toggle boxes’ color
We have applied a class of box to each div, for our convenience. What this code actually does, is “flipping” the color of the boxes with a hit of the button. When pressing it, it invokes the flipColor function, that reverses the value of color originally set to true. Then the v-bind:class is going to toggle the class name from ‘red’ to ‘blue’ or from ‘purple’ to ‘green’ conditionally, depending on the truthfulness of color value. That given, the style is going to apply on each class and give us the desired output.
Info
The v-bind:class directive can co-exist with the plain class attribute.
So, in our example, divs always have the box class and conditionally one of red, blue,
purple or green.
9.1.2 Array Syntax
We can also apply a list of classes to an element, using an array of classnames.

Applying conditionally a class, can also be achieved with the use of inline if inside the array.

1 2 3 4 5 6 7 8 9
Info
Inline if is commonly referred to as the ternary operator, conditional operator, or ternary if.
The conditional (ternary) operator is the only JavaScript operator that takes three operands.
The syntax of ternary operator is condition ? expression1 : expression2. If condi- tion is true, the operator returns the value of expression1, otherwise, it returns the value of expression2.
Using inline if, the flipping colors example will look like:

10 } 11 });
this.color = !this.color;
Tip
To actually use a class name instead of a variable inside classes array, use single quotes.
v-bind:class=”[ variable, ‘classname’]”
Class and Style Bindings 109
1
2
3
4
5
6
7
8 9}
new Vue({
el: ‘.container’, data: {
color: true },
methods: {
flipColor: function() {

Class and Style Bindings 110
9.2 Style binding 9.2.1 Object Syntax
The Object syntax for v-bind:style is pretty straightforward; it looks almost like CSS, except it’s a JavaScript object. We are going to use the shorthand that Vue.js provides for the previously used directive, v-bind(:).
1
2

1 data: {
2 niceStyle:
3{
4 color: ‘blue’,
5 fontSize: ’20px’
6} 7}
We can also declare the style properties inside an object :style=”…“ inline.

We can even reference variables inside style object:

Class and Style Bindings
111
Style object binding
It is often a good idea to use a style object and bind it, so the template is cleaner.
9.2.2 Array Syntax
Using inline array syntax for v-bind:style, we are able to apply multiple style objects to the same element, meaning here that every list item is going to have the color and font-size of niceStyle and the font style of badStyle.
1
2

1 data: {
2 niceStyle:
3{
4 color: ‘blue’,
5 fontSize: ’20px’
6}
8{
9 fontStyle: ‘italic’
10 } 11 }
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Info for Intermediates
When you use a CSS property that requires vendor prefixes in v-bind:style, for example transform, Vue.js will automatically detect and add the appropriate prefixes to the applied styles.
9.3 Bindings in Action

Hello Vue

The above example has an array of objects called tasks and a styleObject which contains only one property. With the use of v-for, a list of tasks is rendered and each task has a done property with a boolean value. Depending on the value of done, a class is applied conditionally as before. If a task has been completed, then css style applies, and the task gains a text-decoration of line-through. Each task is accompanied by a button, listening for the click event, which triggers a method, altering the completion status of the task. The style attribute is bound to styleObject, resulting in the change of font-size of all tasks. As you can see, the completeTasks method takes in the parameter task.
Class and Style Bindings 113
Class and Style Bindings
114
Code Examples
You can find the code examples of this chapter on GitHub2.
2https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter9
Class and Style Bindings 115
9.4 Homework
A fun and maybe tricky exercise for this chapter. Create an input where the user can choose a color. When a color is chosen, apply it to an element of your choice. Thats it, let’s paint!! 🙂
Hint
You could use input type=”color” for your ease (supported in most browsers).
Example Output
Potential Solution
You can find a potential solution to this exercise here3. 3https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/chapter9.html

II Consuming an API
10. Preface
In this chapter, we are going a little deeper and demonstrate how we can use Vue.js to consume an API.
Following the story examples of previous chapters, we are now going to use some real data, coming from an external source.
In order to use real data, we need to make use of a database. Assuming that you already know how to create a database, it won’t be covered in this book. To work along with the book’s examples, we got you covered, we have already created one to be put to use.
10.1 CRUD
Presume we have a database, we need to perform CRUD operations (Create, Read, Update, Delete). More particularly, we want to
• Create new stories in the database
• Update existing story’s details (such as ‘upvotes’) • Delete stories that we don’t like
Since Vue.js is a Front-end JavaScript framework, it cannot connect to a database directly. To access a database we need a layer between Vue.js and the database. This layer is the API (Application Program Interface).
10.2 API
Because this book is about Vue.js and not about designing APIs, we will provide you a demo API built with Laravel1. Laravel is one of most powerful PHP frameworks along with Symfony2, Nette, CodeIgniter, and Yii2. You are free to create your API using any language or framework you like. I use Laravel because it is simple, it has a great community, and it is awesome! 🙂
Therefore, we strongly recommend you to use the demo API that we have built exclusively for the examples of this book.
1https://laravel.com/

Preface 118
To use our API, you have to download the book’s code and start a server. To do so, follow the instructions below.
1. Open your terminal and create a directory (we will create ‘∼/themajestyofvuejs2’) mkdir ∼/themajestyofvuejs2
cd ∼/themajestyofvuejs2
git clone https://github.com/hootlex/the-majesty-of-vuejs-2 .
Alternatively, you can visit the repository on github2 and download the zip file. Then, extract its contents under the created directory.
1. Navigate to the current chapter under ‘apis’ of the newly created directory. cd ∼/themajestyofvuejs2/apis/stories
1. Run the installation script sh setup.sh
If you want to customize the server (host, port, etc), you can make the setup manually. Below is the source code of our script.
2https://github.com/hootlex/the- majesty- of- vuejs- 2

Preface 119
Installation Script: setup.sh

# navigate to chapter directory

$cd ~/themajestyofvuejs2/apis/stories # install dependencies$ composer install

$php artisan db:seed; # Start server$ php artisan serve –port=3000 –host localhost;
Great! You now have a fully functional API and a database filled with nice stories.
Note
If you are using Vagrant, you have to run the server on host ‘0.0.0.0’. Then, you will be able to access your server on Vagrant’s box ip.
If, for example, Vagrant’s box ip is 192.168.10.10 and you run $php artisan serve –port=3000 –host 0.0.0.0; you can browse your website on 192.168.10.10:3000. If you have downloaded our demo API, you can continue to the next section. If you chose to create you own API, you have to create a database table to store the stories. The following columns must be present. Column Name id plot writer upvotes Type Integer, Auto Increment String String Integer, Unsigned Don’t forget to seed some fake data to follow up with the next examples. Preface 120 10.2.2 API Endpoints An endpoint is simply a URL. When you go to http://example.com/foo/bar it is an endpoint and you simply need to call it /foo/bar because the domain will be the same for all the endpoints. To manage the Story resource we need 5 endpoints. Each endpoint corresponds to a specific action. HTTP Method GET/HEAD GET/HEAD POST PUT/PATCH DELETE URI api/stories api/stories/{id} api/stories api/stories/{id} api/stories/{id} Action Fetches all stories Fetches specified story Creates a new story Updates an existing story Deletes specified story As indicated in the above table, to get a listing with all the ‘stories’ we have to make an HTTP GET or HEAD request to api/stories. To update an existing story we have to make an HTTP PUT or PATCH request to api/stories/{storyID} providing the data we want to update, and replacing {storyID} with the id of the story we want to update. The same logic applies to all endpoints. I think you get the idea. Assuming your server is running on http://localhost:3000, you can view a listing of all stories in JSON format by visiting http://localhost:3000/api/stories on your web browser. Preface 121 Tip JSON response Reading raw JSON data on browser can be painful. It is always easier to read a well formatted JSON. Chrome has some great extensions that could format raw JSON data into tree view format that can be easily read. I use JSONFormatter3 because it supports syntax highlighting and displays JSON in tree view, where the nodes on the tree can be collapsed or expanded by clicking the triangle icon on the left of each node. It also provides a button for switching to original (raw) data. You can choose whichever extension you like but you should definitely use one! 3https://chrome.google.com/webstore/detail/json- formatter/bcjindcccaagfpapjjmafapmmgkkhgoa 1. Working with real data It is time to actually put in use our database and perform the operations we have mentioned (CRUD). We will utilize the last example from the Components chapter, but this time, of course, our data will come from an external source. To exchange data with the server we need to perform asynchronous HTTP (Ajax) requests. Info AJAX is a technique that allows web pages to be updated asynchronously by exchanging small amounts of data with the server behind the scenes. 11.1 Get Data Asynchronous Take a moment to have a look at the last example from the Components chapter. As you can see we hardcode stories array, inside the data object of Vue instance. Stories array hardcoded new Vue({ data: { stories: [ { plot: ‘My horse is amazing.’, writer: ‘Mr. Weebl’, }, { plot: ‘Narwhals invented Shish Kebab.’, writer: ‘Mr. Weebl’, }, … ] This time, we want to fetch the existing stories from the server. } }) Working with real data 123 To do so , we’ll perform a HTTP GET request, using jQuery at first. Later on this chapter, we will migrate to vue-resource1 to see the differences between the two of them. To make the AJAX call we are going to use$.get(), a jQuery function that loads data from the
server using a HTTP GET request. Full documentation for $.get() can be found here2. Info vue-resource is a plugin for Vue.js that provides services for making web requests and handling responses. The$.get() method’s syntax is
$.get( url, success ); which is actually a shorthand for$.ajax({
url: url,
success: success
});
So, what we do now? We want to get the stories from the server, using $.get(‘/api/stories’), and store the response data into stories array. There is a common catch here, we have to make the call after the Instance is ready. Do you remember Vue’s Lifecycle Hooks? There is a hook, called mounted, which is called just after the instance has been mounted. Warning The mounted hook is not equivalent to jQuery’s$( document ).ready(). When using mounted, there’s no guarantee to be in-document. If you need to execute something once the page Document Object Model (DOM) is ready, you can use:
mounted: function () { this.$nextTick(function () { // code that assumes this.$el is in-document
}) }
1https://github.com/vuejs/vue- resource 2https://api.jquery.com/jquery.get/

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
Working with real data 124 Lets see this in action.

# Let’s hear some stories!

{{ $data }} • {{ story.writer }} said “{{ story.plot }}” {{story.upvotes}} • We start by pulling in the jQuery from the cdnjs3. Then, within mounted hook, we perform the GET 3https://cdnjs.com/libraries/jquery/ Working with real data 125 request. After the request is successfully finished, we store the response data (inside the callback) into stories array. Get stories Notice here, that inside the callback we are referring to stories variable using vm.stories instead of this.stories. We do so because variable this is not bound to the Vue instance inside the callback. So, we save the whole Vue instance to a variable called vm, in order to have access to it from anywhere within our code. To learn more about this, have a look at: documentation4. 11.2 Refactoring Having large amounts of code in our text editor can be confusing if not displayed properly, as well as in the browser. For that reason, we are going to refactor our example code, to render the list of stories using a < table> element instead of the < ul>. 4https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Operators/this 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # Plot Writer Upvotes Actions {{story.id}} {{story.plot}} {{story.writer}} {{story.upvotes}} Here’s a list of all your stories. {{$data }}

But there is an issue.
Working with real data 126
Working with real data
127
Rendering issues
Our table does not render properly, but why?5
Some HTML elements, for example

<

table>, have restrictions on what elements can appear inside them. Custom elements that are not in the whitelist will be hoisted out and thus not render properly. In such cases you should use the is special attribute to indicate a custom element.
Therefore, to solve this issue we have to use Vue’s special attribute is.

So our example will become

5http://goo.gl/Xr9RoQ

Working with real data
128
Well, this looks better!
11.3 Update Data
Table renders properly
We used to have a function that allowed the user to vote any story he wanted to. But now we want something more. We want the server to be informed every time a story is voted, ensuring that story’s votes are updated in the database as well.
To update an existing story, we have to make an HTTP PATCH or PUT request to api/sto- ries/{storyID}.
Inside the upvoteStory function, which is to be created, we are going to make an HTTP call after we have increased story’s upvotes. We will pass the newly updated story variable in the Request Payload, in order to update the data in our server.
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8 9
10 11 12 13 14

Vue.component(‘story’,{
template: ‘#template-story-raw’,
props: [‘story’],
methods: {
$.ajax({ url: ‘/api/stories/’+story.id, type: ‘PATCH’, data: story, }); } }, }) Working with real data 129 We brought back the upvote method and placed it inside our story component. Making a PATCH request now, providing the new data, the server updates the upvotes count. 1 2 3 4 5 6 7 8 9 10 Let us proceed to another piece of functionality, our stories list should have: Deleting a story we don’t like. To remove a story from the array and the DOM, we have to search for it and remove it from stories array. We append a Delete button to the actions column, bound to a method to delete the story. The deleteStory method will be: Working with real data 130 11.4 Delete Data Upvote stories 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Vue.component(‘story’,{ methods: { deleteStory: function(story){ // find story var index = vm.stories.indexOf(story); // delete it vm.stories.splice(index, 1) } } … }) Working with real data 131 But of course, this way, we will only remove the story temporary. In order to delete the story from the database, we have to perform an AJAX DELETE request. Vue.component(‘story’,{ methods: { deleteStory: function(story){ // find story var index = vm.stories.indexOf(story); // delete it vm.stories.splice(index, 1) // make DELETE request$.ajax({
url: ‘/api/stories/’+story.id,
type: ‘DELETE’
}); },
}
… })
We are passing in the URL, as we did before. The type here should be equal to DELETE. Our method is now ready and we can delete the story from our database as well as the DOM.
Working with real data
132
Upvote and Delete stories
That’s it for now. We will continue our example in the next chapter, by enhancing the functionality with Creating new stories, Editing current stories, and more. But first of all, we will replace jQuery with vue-resource.
12. Integrating vue-resource 12.1 Overview
Vue-resourse is a resource plugin for Vue.js.
This plugin provides services for making web requests and handles responses using an XMLHttpRe- quest or JSONP.
We are going to make again all the web requests we made above, using this plugin instead. This way you can see the differences and decide which one is better for your needs. jQuery is nice, but if you are using it only to perform AJAX calls, you may consider removing it.
Here you can find installation instructions and documentation about vue-resource1. As usual, we are going to “pull it in” from a cdn2 page.
To fetch data from a server, we can use vue-resource’s $http method with the following syntax: mounted: function() { // GET request this.$http({url: ‘/someUrl’, method: ‘GET’}) .then(function (response) {
// success callback
}, function (response) { // error callback
}); }
Info
A Vue instance provides the this.$http(options) function which takes an options object for generating an HTTP request and returns a promise. Also the Vue instance will be automatically bound to this in all function callbacks. Instead of passing the method option, there are shorthand methods available for all the requested types. 1https://github.com/vuejs/vue- resource 2https://cdnjs.com/libraries/vue- resource Integrating vue-resource 134 Request shorthands 1 this.$http.get(url, [data], [options]).then(successCallback, errorCallback);
2 this.$http.post(url, [data], [options]).then(successCallback, errorCallback); 3 this.$http.put(url, [data], [options]).then(successCallback, errorCallback);
4 this.$http.patch(url, [data], [options]).then(successCallback, errorCallback); 5 this.$http.delete(url, [data], [options]).then(successCallback, errorCallback);
12.2 Migration
It is time to use vue-resource in our example. First of all, we have to include it. We will add this line to our HTML file.
1
To fetch the stories, we will make a GET request in the corresponding form:
mounted: function() {
// GET request
this.$http({url: ‘/api/stories’, method: ‘GET’}) .then(function (response) { Vue.set(vm, ‘stories’, response.data) // Or we as we did before // vm.stories = response.data }) } Our list of stories comes without any problems, when using the syntax above. Let’s move on now, with the DELETE and PATCH requests, using the shorthand methods. PATCH request upvoteStory: function(story){ story.upvotes++; this.$http.patch(‘/api/stories/’+story.id , story) }

Integrating vue-resource 135
DELETE request
deleteStory: function(story){ this.$parent.stories.indexOf(story) this.$parent.stories.splice(index, 1) this.$http.delete(‘/api/stories/’+story.id ) } We have replaced the AJAX methods with these ones, in no time! Info Since, the story component doesn’t have access to the stories array, we access the array using this.$parent.stories. We could also use vm.stories or emit an event to update the array within the parent Vue instance.
12.3 Enhancing Functionality
We should add a couple more features, to make our list of stories neat. We can give the user the ability to change the plot of a story, its writer, and also create new stories.
12.3.1 Edit Stories
Let’s start with the first task and give the user some inputs to manipulate the story’s attributes. Two bound inputs should do the job, but we should display them only when the user is editing a story. It seems like the kind of work we did in previous chapters.
To define if a story is in editing state we will use a property, editing, which will become true when the user hits the Edit button.
1

2
3
4
5
6
7 {{story.plot}}
8

9

10

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

{{story.writer}}

9 10
… })
Integrating vue-resource 136
Vue.component(‘story’,{

methods: {

editStory: function(story){ story.editing=true;
},
1
2
3
4
5
6
7 8}
This is our updated table, with two new inputs and a button. We use the editStory function, to set story.editing to true , so v-if will bring up the inputs to edit the story and hide the Upvote and Delete buttons. Though, this approach won’t work. It seems that the DOM isn’t updating after setting story.editing to true. But why does this happen?
Integrating vue-resource 137
It turns out, according to this post from Vue.js blog3, that when you are adding a new property that wasn’t present when the data was observed, the DOM won’t update. The best practice is to always declare properties that need to be reactive upfront. In cases where you absolutely need to add or delete properties at runtime, use the global Vue.set or Vue.delete methods.
For this reason, we have to initialize the story.editing attribute to false on each story, right after receiving the stories from the server.
To do this, we are going to use Javascript’s .map() method, within the success callback of the GET request.
mounted: function() { var vm = this;
// GET request
this.$http({url: ‘/api/stories’, method: ‘GET’}) .then(function (response) { var storiesReady = response.data.map(function(story){ story.editing = false; return story }) Vue.set(vm, ‘stories’, storiesReady) }) } This function adds the editing attribute to each story object and then returns the updated story. The new variable, storiesReady, is an array that contains our updated array with the new attribute on. When the story is under editing, we will give the user two options: to update the story with new values or to cancel the edit. 3http://vuejs.org/2016/02/06/common- gotchas/ 4https://msdn.microsoft.com/en- us/library/ff679976(v=vs.94).aspx Info The .map() method calls a defined callback function on each element of an array and returns an array that contains the results. You can find more information about the .map() method and its syntax here4. Integrating vue-resource 138 1 2 3 4 5 6 7 8 9 10 11 12 Form inputs for story editing So, lets move on and add two buttons, that should be displayed only when the user is editing a story. Additionally, a new method called updateStory will be created. It is going to update the current editing story, after the Update Story button is pressed. Vue.component(‘story’,{ methods: { updateStory: function(story){ this.$http.patch(‘/api/stories/’+story.id , story)
//Set editing to false to show actions again and hide the inputs
story.editing = false;
}, }
… })
Integrating vue-resource
139
1 2 3 4 5 6 7 8 9
10 11 12 13
1 2 3 4 5
Updating story actions
Here it is, up and running. After the PATCH request is finished successfully, we have to set story.editing back to false, in order to hide the inputs and bring back the action buttons.
12.3.2 Create New Stories
Now for a bit trickier task, we are going to give the user the ability to create a new story and save it to our server. First, we must provide inputs, so the new story can be typed in. To make this happen, we will create an empty story and we’ll append it to the stories array using the push() javascript method. We will initialize all the story’s attributes to null, except from editing. Since, we want to immediately manipulate the new story, the editing property will be set to true.
varvm=newVue({ …
methods: {
createStory: function(){
} })
var newStory={ “plot”: “”,
“editing”: true };
this.stories.push(newStory); },

Here’s a list of all your stories.

1 2 3 4 5 6 7 8 9
10 11 12
1 2 3 4 5 6 7 8 9
10 11
Info
The push() method adds new items to the end of an array, and returns the new length. You can find more information about the push() method and its syntax here5.
We named the new function createStory and we placed it in our Vue instance.
Right bellow our list, we have added a button. When the button is clicked, createStory method gets invoked. Since the newStory.editing is set to true, the binded inputs for plot and writer along with the Edit action buttons, are being rendered instantly.
Also, the new story object must be sent to the server in order to be stored in the database. We are going to perform a POST request inside a method called storeStory.
Vue.component(‘story’,{

methods: {

storeStory: function(story){ this.$http.post(‘/api/stories/’, story).then(function() { story.editing = false; }); }, } … }) Integrating vue-resource 140 We’ve used the shorthand method to perform the POST request. In the success callback function, we have set editing to false in order to show the action buttons again and hide the form’s inputs and editing buttons. Below, we are going to update the button groups, in accordance to the new method. 5https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/Array/push Integrating vue-resource 141 12 13 14 17 20 23 24 We observe a small mistake in this block of code. When we are in editing mode (v-else block) we see that the buttons for update and store are being shown together, but we only need one for each story, since each story will be Stored or Updated. It can’t do both. So, if the story is an old one and the user is about to edit it, we need the update button. On the other hand, if the story is new, we need the store button. A small mistake To bypass this issue, we are going to restructure our buttons. The Update button will only be Integrating vue-resource 142 displayed when the story is old. Accordingly the Save new Story button will be displayed when the story is a new one. You may have noticed that all stories fetched from the server have an id attribute. We are going to use this observation to define if a story is new or not. 1 2 4 7 8 11 12 15 Tip If the story is taken from the database, it will have an id. Integrating vue-resource 143 1 2 3 4 5 6 7 8 9 10 11 Adding new story There we have it. It wasn’t that hard, right? After finishing this part, testing our app brings up another error. After creating, saving, and trying to edit a new story, we see that the button says “Save new Story” instead of “Update Story”! Thats because we are not fetching the newly created story from the server, after we send it, and it does not have an id yet. To solve this problem, we can again fetch the stories from the server, just after we store a new one into the database. Since I don’t like to repeat my code, I will extract the fetching procedure to a method called fetchStories(). After that, I can use this method to fetch the stories anytime. The fetchStories method varvm=newVue({ el: ‘#v-app’, data : { stories: [], }, mounted: function(){ this.fetchStories() }, methods: { createStory: function(){ var newStory={ 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 “plot”: “”, “upvotes”: 0, “editing”: true }; this.stories.push(newStory); }, fetchStories: function () { this.$http.get(‘/api/stories’)
.then(function (response) {
Integrating vue-resource 144
} });
}); },
story.editing = false
return story })
In our situation, we’ll call fetchStories() inside the success callback of the POST request.
Vue.component(‘story’,{

methods: {

storeStory: function(story){ this.$http.post(‘/api/stories/’, story).then(function() { story.editing = false; vm.fetchStories(); }); }, } … }) That’s it! We can now create and edit any story we want. 12.3.3 Store & Update Unit A better way to fix the previous issue, is to fetch only the newly created story from the database, instead of fetching and overwriting all the stories. If you check the server response, for the POST 1 2 3 4 5 6 7 8 9 10 11 12 13 Integrating vue-resource 145 request, you will see that it returns the created story along with its id. Server response after creating new story The only thing we have to do, is to update our story to match the server’s one. So, we will set the id of the response data, to story’s id attribute. We will do this inside the POST’s success callback. Vue.component(‘story’,{ methods: { storeStory: function(story){ this.$http.post(‘/api/stories/’, story).then(function(response) {
Vue.set(story, ‘id’, response.data.id);
story.editing = false });
}, }
… })
I use Vue.set(story, ‘id’, response.data.id) instead of story.id = response.data.id be- cause inside our table we display the id of each story. Since the new story had no id, when it is pushed to the stories array, the DOM won’t be updated when the id changes, so we will not be able to see the new id.
Integrating vue-resource 146
Tip
When you are adding a new property that wasn’t present when the data was observed, Vue.js cannot detect the property’s addition. So, if you need to add or remove properties at runtime, use the global Vue.set or Vue.delete methods.
12.4 JavaScript File
As you may have noticed, our code is starting to become big. As our project grows, it is getting harder to maintain. For starters, we’ll separate the JavaScript code from the HTML. I’ll create a file called app.js and I’ll save it under js directory.
All the JavaScript code should live inside that file from now on. To include the newly created script to any HTML page you simply have to add this tag
and you are ready to go!
12.5 Source Code
Below is the whole source code of the previous Managing Stories example. If you have downloaded our repo, I suggest you to open your local files with your favorite text editor, because the code is quite big. The files are located at ∼/themajestyofvuejs2/apis/stories/public.
If you haven’t downloaded the repository you can still view the stories.html6 and app.js7 files on github.
stories.html
1
2
3 Stories
4
6
7
8
9

6https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/apis/stories/public/stories.html 7https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/apis/stories/public/js/app.js

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# Stories

Here’s a list of all your stories.

{{ $data }} {{story.id}} {{story.plot}} Integrating vue-resource 147 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 {{story.writer}} {{story.upvotes}} Integrating vue-resource 148 94 95 96 97 98 99 100 101 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Vue.component(‘story’, { template: ‘#template-story-raw’, props: [‘story’], methods: { deleteStory: function (story) { var index = this.$parent.stories.indexOf(story);
this.$parent.stories.splice(index, 1) this.$http.delete(‘/api/stories/’ + story.id)
},
this.$http.patch(‘/api/stories/’ + story.id, story) }, editStory: function (story) { story.editing = true; }, updateStory: function (story) { this.$http.patch(‘/api/stories/’ + story.id, story)
//Set editing to false to show actions again and hide the inputs
story.editing = false;
},
storeStory: function (story) {
this.$http.post(‘/api/stories/’, story) .then(function (response) { // After the the new story is stored in the database // fetch again all stories with // vm.fetchStories(); // Or Better, update the id of the created story Vue.set(story, ‘id’, response.data.id); //Set editing to false to show actions again and hide the inputs story.editing = false; Integrating vue-resource 149 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 }); }, Integrating vue-resource 150 } }) new Vue({ el: ‘#v-app’, data: { stories: [], story: {} }, mounted: function () { this.fetchStories() }, methods: { createStory: function () { var newStory = { plot: “”, upvotes: 0, editing: true }; this.stories.push(newStory); }, fetchStories: function () { console.log(‘fetsi’) var vm = this; this.$http.get(‘/api/stories’)
});
}, }
.then(function (response) {
// set data on vm
var storiesReady = response.data.map(function (story) {
story.editing = false;
return story
})
});
Integrating vue-resource 151
Code Examples
You can find the code examples of this chapter on GitHub8
12.6 Homework
To get comfortable with making web requests and handling responses, you should replicate what we did in this chapter.
What you have to do is to consume an API in order to:
• create a table and display existing movies • modify existing movies
• store new movies in the database
• delete movies from the database
I have prepared the database and the API for you. You only have to write HTML and JavaScript. 12.6.1 Preface
If you have followed the instructions from Chapter 10, open your terminal and run: cd ∼/themajestyofvuejs/apis/movies
sh setup.sh
If you haven’t, you should run this:
mkdir ∼/themajestyofvuejs
cd ∼/themajestyofvuejs
git clone https://github.com/hootlex/the-majesty-of-vuejs . cd ∼/themajestyofvuejs/apis/movies
sh setup.sh
You now have a database filled with great movies along with a fully functional server running on http://localhost:3000!
To ensure that everything is working fine, browse to http://localhost:3000/api/movies and you should see an array of movies in JSON format.
8https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter12

Integrating vue-resource
152
12.6.2 API Endpoints
The API Endpoints you are going to need are:
HTTP Method
URI
api/movies api/movies/{id} api/movies api/movies/{id} api/movies/{id}
Action
Fetches all movies
Fetches specified movie Creates a new movie Updates an existing movie Deletes specified movie
Put your HTML code inside ∼/themajestyofvuejs2/apis/movies/public/movies.html file we have created. You can place your JavaScript code there too, or inside js/app.js.
To check your work visit http://localhost:3000/movies.html on your browser. I hope you will enjoy this one. Good luck!
Example Output

Integrating vue-resource 153
Potential Solution
You can find a potential solution to this exercise here9. 9https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/homework/Chapter12

1. Pagination
In the previous chapter we managed to fetch all the records from the database and display them inside a table. That implementation is fine for a bunch of records, but in the real world, when you have to work with thousands or millions of records, you cannot simply fetch them and place them inside an array. If you do so, your browser will not be happy to load such an amount of data, but even if it manages to do that, then I assure you that no user likes dealing with a table containing 100,000 rows.
Info
Pagination is used in some form in almost every web application to divide returned data and display it on multiple pages. Pagination also includes the logic of preparing and displaying the links to the various pages, and it can be handled client-side or server-side. Server-side pagination is more common.
In situations like this, the developers who designed the API will (hopefully) divide the returned data in pages.
The HTTP response will contain some simple meta-data next to the main data, to inform you about total items, per page items, etc. To help you traverse through the pages, it will provide information such as the current page, the next page, and the previous page.
Example Response with Paginated data
{
“total”: 10000,
“per_page”: 50,
“current_page”: 15,
“last_page”: 200,
“next_page_url”: “/api/stories?page=16”, “prev_page_url”: “/api/stories?page=14”, “from”: 751,
“to”: 800,
“data”: […] }
The pagination’s meta-data could also be inside an object next to data, or anywhere the API developers have decided.

Pagination 155
Example Response with Paginated data
{
“data”: […], “pagination”: {
“total”: 10000,
“per_page”: 50,
“current_page”: 15,
“last_page”: 200,
“next_page_url”: “/api/stories?page=16”, “prev_page_url”: “/api/stories?page=14”, “from”: 751,
“to”: 800, }
}
13.1 Implementation
We are going to continue working with the story examples from the previous chapter, using the slightly improved paginated API. So, we are going to modify the code, to be able to access and use these data.
If you take a look at the code from the previous example, you will see that our fetchStories method is similar to this:
new Vue({ …
methods: {

fetchStories: function () { var vm = this;
this.$http.get(‘/api/stories’) .then(function (response) { }, … } }); var storiesReady = response.data.map(function (story) { story.editing = false; return story }) Vue.set(vm, ‘stories’, storiesReady) }); Pagination 156 If we open our HTML file on the browser, as you may have already guessed, our table doesn’t render properly. Stories’ aren’t displaying This happens because the stories are now returned inside an array named data. To fix this, we have to change response.data to response.data.data (I know this is kinda weird, but…). Except from the stories array, we also want to save the pagination’s data inside an object in order to easily implement the pagination functionality. To find out how we can access those data, let’s have a look at the server’s response. Pagination 157 Server’s response For starters, we don’t need all those data. So, we will stick with current_page, last_page, next_- page_url, and prev_page_url. Our pagination object will be something like this: pagination: { “current_page”: 15, “last_page”: 200, “next_page_url”: “/api/stories?page=16”, “prev_page_url”: “/api/stories?page=14” } Let’s modify our fetchStories method to update pagination object, each time it fetches stories from the database. Pagination 158 new Vue({ … methods: { fetchStories: function () { var vm = this; this.$http.get(‘/api/stories’) .then(function (response) {
var storiesReady = response.data.data.map(function (story) { story.editing = false;
return story
})
//here we use response.data
var pagination = {
current_page: response.data.current_page, last_page: response.data.last_page, next_page_url: response.data.next_page_url, prev_page_url: response.data.prev_page_url
}
Vue.set(vm, ‘pagination’, pagination)
}); },
… }
});
By now, we have our pagination object but we always fetch the first page of stories since we are making a GET HTTP request to api/stories. We have to change the requested page, based on user interaction (next page, previous page).
First we’ll update the fetchStories method to accept an argument with the desired page. If no argument is passed, it will fetch the first page. I’ll also create a new method, makePagination, to make the code cleaner.
Pagination 159
new Vue({ …
methods: {

fetchStories: function (page_url) { var vm = this;
page_url = page_url || ‘/api/stories’ this.$http.get(page_url) .then(function (response) { var storiesReady = response.data.data.map(function (story) { story.editing = false; return story }) vm.makePagination(response.data) Vue.set(vm, ‘stories’, storiesReady) }); }, makePagination: function (data){ //here we use response.data var pagination = { current_page: data.current_page, last_page: data.last_page, next_page_url: data.next_page_url, prev_page_url: data.prev_page_url } Vue.set(vm, ‘pagination’, pagination) } … } } Now that our method is ready, we need a way to call it properly. We will add 2 buttons, one for next and one for previous page, on the top of our #app div. Each button will call fetchStories method when clicked, passing the corresponding page url. 6 7 8 9 10 11 12 13 1 2 3 4 5 6 7 8 Pomp! If you try to click the buttons you’ll see that they work as expected. We’ve got our pagination in the blink of an eye. It will be useful though to inform the user about which page he is currently looking at, and the total number of pages. Also, we can disable the previous button when the user is on the first page, and the next on the last, accordingly. Pagination 160 1 2 3 4> 5 Pagination 161 Disabled previous button Code Examples You can find the code examples of this chapter on GitHub1. 13.3 Homework There is nothing particular to do for homework in this chapter. If you actually want to work on this example, I will provide you the paginated API. If you have solved the previous chapter’s homework (downloaded the code and started a server), you are just a few clicks away. If you haven’t, just follow these instructions. The paginated API lives inside ’∼/themajestyofvuejs2/apis/pagination/stories’ directory. The HTML file is in ’∼/themajestyofvuejs2/apis/pagination/stories/public’ directory. If you just want to view the final code, you can take a look at the files on GitHub2. 1https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter13 2https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/apis/pagination/stories/public III Building Large-Scale Applications 14. ECMAScript 6 Before we take things a step further and see how we can build Large-Scale Applications, I would like to familiarize you with ECMAScript 6. Info ECMAScript is a client-side scripting language’s specification, that is the basis of several programming languages including JavaScript, ActionScript, and JScript. ECMAScript 6 (ES6), also known as ES2015, is the latest version of the ECMAScript standard. The ES6 specification was finalized in June 2015. It is a significant update to the language, and the first major update since ES5 was standardized in 2009. Implementation of ES6 features in major JavaScript engines is underway now1. 14.1 Introduction ES6 has a lot of new features. We are going to review those that we will use in the next chapters. If you are interested in learning more about what is new in ES6, I highly recommend you the book “Understanding ECMAScript 6” by Nicholas C. Zakas which is available on leanpub2. There is also an online version3 of the book, which you can read for free. Also, there are other useful resources and tutorials, like the one on Babel4, an article on tutsplus5, a blog post6 by Nicholas C. Zakas, and a ton of stuff around the web! 1http://kangax.github.io/compat- table/es6/ 2https://leanpub.com/understandinges6/ 3https://leanpub.com/understandinges6/read 4https://babeljs.io/docs/learn- es2015/ 5http://code.tutsplus.com/articles/use- ecmascript- 6- today- – net- 31582 6https://www.nczonline.net/blog/2013/09/10/understanding- ecmascript- 6- arrow- functions/ ECMAScript 6 164 14.1.1 Compatibility Unsurprisingly, support varies wildly from engine to engine, with Mozilla tending to lead the way. ES6 compatibility table7 is a useful start for establishing what ECMAScript 6 features your browser does and doesn’t support. Note If you’re using Chrome most of the ES6 features are hidden behind a feature toggle. Browse to chrome://flags, find the section titled “Enable Experimental JavaScript” and enable it to turn on support From now on we will develop our examples using ES6 features. 14.2 Variable Declarations 14.2.1 Let Declarations let is the new var. You can basically replace var with let to declare a variable, but limit the variable’s scope only to the current code block. Since let declarations are not hoisted to the top of the enclosing block, you better always place let declarations first in the block, so that they are available to the entire block. For example: Let inside if 1 2 3 4 5} 6 //adult isn’t accessible here 7 console.log(adult); 8 //ERROR: Uncaught ReferenceError: adult is not defined letage=22 if(age>=18){ let adult = true; console.log(adult); //outputs true 7https://kangax.github.io/compat- table/es6/ ECMAScript 6 165 Let on top 1 2 3 4 5 6} 7 //now adult is accessible here 8 console.log(adult); //outputs true 14.2.2 Constant Declarations Constants, like let declarations, are block-level declarations. There is one big difference between let and const. Once you declare a variable using const, it is defined as a constant, which means that you can’t change its value. 1 const name = “Alex” 2 3 name = “Kostas” //throws error Info Much like constants in other languages, their value cannot change later on. However, unlike constants in other languages, the value a constant holds may be modified if it is an object. 14.3 Arrow Functions One of the most interesting new parts of ECMAScript 6 is the arrow functions. Arrow functions are functions defined with a new syntax that uses an “arrow” (⇒). They support both expression and statement bodies. Unlike functions, arrows share the same lexical this as their surrounding code. For example, the following arrow function takes a single argument and returns its value increased by 1: letage=22 let adult if(age>=18){ adult = true; console.log(adult); //outputs true ECMAScript 6 166 var increment = value => value + 1; increment(5) //returns 6 // equivalent to: var increment = function(value) { return value + 1; }; Another example with an arrow function which takes 2 arguments and returns their sum: var sum = (a, b) => a + b; sum(5, 10) //returns 15 // equivalent to: var sum = function(a, b) { return a + b; }; An example arrow function which takes no arguments and uses a statement. var sayHiAndBye = () => { console.log(‘Hi!’); console.log(‘Bye!’); }; sayHiAndBye() // equivalent to: var sayHiAndBye = function() { console.log(‘Hi!’); console.log(‘Bye!’); }; 14.4 Modules This is, to me, the biggest improvement of the language. ES6 now supports exporting and importing modules across different files. The simplest example is to create a .js file with a variable, and use it inside another file like this: ECMAScript 6 167 module.js 1 export name = ‘Alex’ main.js 1 import {name} from ‘./module’ 2 console.log(‘Hello’, name) 3 //outputs “Hello Alex” You can also export variables along with functions one by one module.js 1 export var name = ‘Alex’ 2 export function getAge(){ 3 4} return 22; main.js 1 import {name, getAge} from ‘./module’ 2 console.log(name, ‘is’, getAge()) Or inside an object: module.js 1 var name = ‘Alex’ 2 function getAge(){ 3 4} 5 export default {name, age} main.js 1 import person from ‘./module’ 2 console.log( person.name, ‘is’, person.getAge() ) 3 //outputs “Alex is 22” 14.5 Classes JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript’s existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance. return 22; ECMAScript 6 168 Class Example //parent class class Rectangle { constructor(height, width) { this.height = height; this.width = width; } calcArea() { return this.height * this.width; } //To create a getter, use the keyword get. get area() { return this.calcArea(); } //To create a setter, you do the same, using the keyword set. } //child class class Square extends Rectangle{ constructor(side) { //call parent’s constructor super(side, side) } } var square = new Square(5); console.log(square.area); //outputs 25 14.6 Default Parameter Values With ES6 you can define default parameter values. ECMAScript 6 169 function divide(x, y = 2){ return x/y; } // equivalent to: function divide(x, y){ y = y == undefined ? 2 : y; return x/y; } 14.7 Template literals Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them. They were called “template strings” in prior editions of the ES2015/ES6 specification. Template literals are enclosed by the back-tick () character instead of double or single quotes. Within the back-ticks you can use${expression}, where expression can be a function, variable or an actual expression. Template literals - Using Variables let name = 'Alex' console.log(Hello ${name}) // equivalent to: console.log('Hello '+ name ) Template literals - Using Functions add = (a, b) => a + b let [a, b] = [10, 2] console.log(If you have${a} eggs and you buy ${b} more you’ll have${add(a,b)} eggs!)
// equivalent to:
console.log(‘If you have ‘ + a +’ eggs and you buy ‘ + b +
‘\nmore you\’ll have ‘+ add(a,b) + ‘ eggs!’)

ECMAScript 6 170
Template literals – Using Expressions
let [a, b] = [10, 2]
console.log(If you have ${a} eggs and you buy${b} more you'll have ${a + b} eggs!) // equivalent to: console.log(‘If you have ‘ + a +’ eggs and you buy ‘ + b + ‘\nmore you\’ll have ‘+ (a + b)*1 + ‘ eggs!’) It is also possible to split the message in multiple lines using ‘\n’. 1. Advanced Workflow All these ES6 features (and many more) may get you excited, but there is a catch here. As we mentioned before, not all browsers fully support ES6/ES2015 features. In order to be able to write this new JavaScript syntax today, we need to have a middleman which will take our code and transpile it into Vanilla JS1, which every browser understands. This procedure is really important in production, even though you might not think so. Let me tell you a story. A few years ago, a co-worker of mine began using some cool JS features that weren’t fully supported by all browsers. A few days later our users started complaining about some pages of our website not showing properly, but we couldn’t figure out why. We tested it on different PCs, Android phones, iPhones, etc, and it was 100% functional in all our browsers. Later, he found out that older versions of mobile Safari didn’t support his code. Don’t be that guy! Some times it’s really hard to know if the code you write is going to work well on all browsers, including Facebook’s mobile browser, which is my worst fear. 15.1 Compiling ES6 with Babel Babel will be our middleman. Babel is a source-to-source JavaScript compiler, which lets us use next generation JavaScript, today. Info A source-to-source compiler, transcompiler or transpiler, is a type of compiler that takes the source code of a program written in one programming language, as its input, and produces the equivalent source code in another programming language. 1http://vanilla- js.com/ Advanced Workflow 172 Babehttps://shop.nets.eu/nb/web/partners/test-cardsl Before installing Babel, you have to install Node.js. To do so, head to Node’s website2 and hit the download button for the the Latest Stable Version. It is going to give you a .pkg file (or .msi if you are on Windows). When the download is finished, open the file and follow the instructions. Then, do the required restart, and you are done! 2https://nodejs.org/en/ Advanced Workflow 173 15.1.1 Installation Node.js Create a new directory and place a file named package.json inside, containing an empty JSON object ({}). You can do it manually, or by running the following commands in your terminal. mkdir babel-example echo {} > package.json Then run this to install Babel: npm install babel-cli –save-dev Advanced Workflow 174 Terminal output When it’s done, your package.json file should be something like this: package.js { } } “devDependencies”: { “babel-cli”: “^6.18.0” What is package.json? A package.json file contains meta data about your app or module. Most importantly, it includes the list of dependencies to install from npm when running npm install. If you’re familiar with Composer, it’s similar to the composer.json file. To learn more about package.json have a look at npm docs3. Your project’s directory should look like this: 3https://docs.npmjs.com/files/package.json Advanced Workflow 175 15.1.2 Configuration Project directory Now that we have babel installed, we need to explicitly tell it what transformations to run on build. Since we want to transform ES2015 code, we will install the ES2015-Preset4. We’ll also create a config file (.babelrc) to enable our preset. npm install babel-preset-es2015 –save-dev echo { “presets”: [ [“es2015”] ]} > .babelrc Tip If the second command fails, enclose file contents inside quotes like this: echo ‘{ “presets”: [ [“es2015”] ]}’ > .babelrc 4https://babeljs.io/docs/plugins/preset- es2015/ Advanced Workflow 176 15.1.3 Build alias Instead of running Babel directly from the command line, we’re going to put our commands within npm scripts. We’ll add a scripts field to our package.json file, and register the babel command there, as build. Our package.json will look like this: package.js { } This works like an alias. Meaning that when we run npm run build we are actually running babel src -d assets/js. This command tells Babel to transpile the code from the src directory to assets/js directory. Before we run the build command we have to do a few more things. For starters, go on and create the above-mentioned dirs (src and assets/js). 15.1.4 Usage Lets move on and put some files inside src folder. I will create a file with a simple sum function and call it sum.js. src/sum.js const sum = (a, b) => a + b; console.log(sum(5,3)); Thats was it. We can now run: npm run build When you run it, you can see in your terminal that the src\sum.js file has been compiled to assets\js\sum.js and looks like this: “scripts”: { “build”: “babel src -d assets/js” }, “devDependencies”: { “babel-cli”: “^6.8.0”, “babel-preset-es2015”: “^6.18.0” } Advanced Workflow 177 assets/js/sum.js “use strict”; var sum = function sum(a, b) { return a + b; }; console.log(sum(5, 3)); From now on, whenever you want to compile your ES6 code you can do it by running the build command. Pretty neat, huh? It’s time to see the resulted sum.js file in the browser. I will create sum.html and include our js. sum.html Babel Example # Babel Example Advanced Workflow 178 Browser output As you can see, the result of the sum function is successfully printed to the console. Info When you want to test a .js file, but you don’t want to get in the process of serving it to the browser, you can run it with Node.js. In the sum.js example there is a console.log(sum(5,3)) line, so if you type in your terminal node sum.js you’ll see the result (8) pop right up! 15.1.5 Homework This homework exercise aims to help you remember what you’ve learned by reproducing the example we’ve built. Instead of the sum.js go on and use ES6 Classes to create a Ninja.js file which will contain a Ninja class. A Ninja should have a property name and a method announce, which will alert the presence of a Ninja. Advanced Workflow 179 For example new Ninja(‘Leonardo’).announce() //alerts “Ninja Leonardo is here!” Don’t forget to compile your js using Babel before including it in your HTML. Hint You can find an example for building classes on the previous chapter. Hint 2 Don’t forget to run npm run build each time you make a change in your js file, otherwise it won’t update! Potential Solution You can find a potential solution to this exercise here5. 5https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/homework/Chapter15/chapter15.1 Advanced Workflow 180 15.2 Workflow Automation with Gulp 15.2.1 Task Runners If you devoted some time to develop the app from the previous Homework section, you have probably found out that it is kinda annoying having to run npm run build every time you make a change in your code. This is where Task Runners like Gulp6 or Grunt7 come in handy. Task runners let you automate and enhance your workflow. Gulp Why use a task runner? In one word: Automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes. 6http://gulpjs.com/ 7http://gruntjs.com/ Advanced Workflow 181 Gulp vs Grunt Grunt, like Gulp, is a tool for defining and running tasks. The major difference between Grunt and Gulp is that Grunt defines tasks using configuration objects while Gulp defines tasks as JavaScript functions. Since Gulp runs Javascript, it provides more flexibility in writing your tasks. Both have a massive plugin library, where you may find one which implements a task you need. 15.2.2 Installation I will show you an example of how you can use Gulp to watch for changes in your js files and automatically run the build command. First we have to install Gulp globally: npm install gulp-cli –global Then we’ll install Gulp in our project’s devDependencies: npm install gulp –save-dev Now that we have gulp installed we will create a gulpfile.js at the root of our project: gulpfile.js const gulp = require(‘gulp’); gulp.task(‘default’, function() { // place the code for your default task here }); 15.2.3 Usage When we now run gulp in our console, it starts, but does nothing yet. We have to setup a default task. In order to run babel directly, I’ll install an npm plugin called gulp-babel8. 8https://www.npmjs.com/package/gulp- babel Advanced Workflow 182 npm install gulp-babel –save-dev I’ll add a new gulp task named babel and set it as the default task. My gulpfile will look like this: gulpfile.js const gulp = require(‘gulp’); const babel = require(‘gulp-babel’); gulp.task(‘default’, [‘babel’]); //basic babel task gulp.task(‘babel’, function() { return gulp.src(‘src/.js’) .pipe(babel({ presets: [‘es2015’] })) .pipe(gulp.dest(‘assets/js/’)) }) This task basically tells babel to transform all js files under src directory using the es2015 preset and put them inside assets/js directory. 15.2.4 Watch Currently running gulp on your console has the same effect with npm run build. What we want to achieve here is to run this task every time a js file has changed. To do so, we will set up a watcher inside our gulpfile like this: gulpfile.js const gulp = require(‘gulp’); const babel = require(‘gulp-babel’); gulp.task(‘default’, [‘watch’]); //basic babel task gulp.task(‘babel’, function() { return gulp.src(‘src/ .js’) .pipe(babel({ presets: [‘es2015’] })) Advanced Workflow 183 .pipe(gulp.dest(‘assets/js/’)) }) //the watch task gulp.task(‘watch’, function() { gulp.watch(‘src/*.js’, [‘babel’]); }) When we run gulp watch on our console, gulp is watching for changes in all our .js files under the specified directory. Every time we make a change, gulp runs our babel task and the files under assets/js are being updated. How awesome is that? 15.2.5 Homework This homework exercise is following the previous one. If you haven’t done the previous one, it’s never too late to begin! Since this part of the chapter is dedicated to Task Runners, you have to to setup a watcher with Gulp and compile your code with Babel, when a change is detected. Note You may already have noticed that when running Gulp it prints messages in terminal (“Starting” – “Finished”), so don’t be so hasty and wait for the changes to be applied. Potential Solution You can find a potential solution to this exercise here9. 9https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/homework/Chapter15/chapter15.2 Advanced Workflow 184 Gulp is watching! 15.3 Module Bundling with Webpack 15.3.1 Module Bundlers Our workflow is fine with the current code of sum.js. We will extend its features, to calculate the cost of a pizza and a beer and let the client know. src/sum.js const pizza = 10 const beer = 5 const sum = (a, b) => a + b + ‘$’;
console.log( Alex, you have to pay ${sum(pizza, beer)}) This code looks good, but assuming that not everyone is called Alex we’ll create a new file, client.js, which will provide the client’s name. Advanced Workflow 185 src/client.js export const name = ‘Alex’ We’ll import the name from there. src/sum.js import { name } from ‘./client’ const pizza = 10 const beer = 5 const sum = (a, b) => a + b + ‘$’;
console.log(${name} you have to pay${sum(pizza, beer)}`) Great! If we run node assets/js/sum.js we get the expected output.
Output of sum.js
You would expect here that the same behavior will apply when we open the html file in the browser, but it doesn’t! We get an error instead.

186
Require is not defined
Check the node assets/js/sum.js and notice the var _client = require(‘./client’); on top. The reason we get the error in the browser is because require() does not exist in the browser/- client-side JavaScript. What we have to do is to bundle the modules in one file so it can be included within a

Working with Single File Components 202

Inside the

It has the same structure as the Hello.vue file we saw above. By default, there is a components object which contains the Hello component. Inside this object, we will import any new components. In the template, there is the tag, and therefore the template of the Hello component will be displayed.

Working with Single File Components
204
16.2.5 main.js
Project’s Homepage
The main.js file within src, as you imagine, is our main script. src/main.js
import Vue from 'vue' import App from './App'
/* eslint-disable no-new */
new Vue({
el: '#app', template: '', components: { App }
})
It imports Vue as a module from node_modules and App component from the src directory as well. Below is our Vue instance and in the components object, there is the App one.
Whenever you need to import a script or a component globally, you can put it within main.js.

Working with Single File Components 205
Note
The template: '' option represents the template output of the component, which is to be injected to the index.hmtl we’ve mentioned earlier.
has the same output with and .
Info
documentation12
16.3 Forming .vue Files
We have seen how a single file component looks like and how it is used in a project. It is time to create a few, in a real-life scenario. Assume we want to create some kind of social network or forum, where users post their stories and experiences. To create our startup, we are going to need 2 forms, one for registration and login, and one page to display users’ stories.
Before we begin, we’ll include Bootstrap globally in order to be able to use its styles within all our components. To do so, we have to update our index.html.
index.html

stories-classic-project

Let’s create the login form, in a new, Login.vue file. 12http://vuejs- templates.github.io/webpack/structure.html

Working with Single File Components 206

And there it is. In order to view the file in the browser we have to include our Login component
somewhere. So, we’ll import it into the main App component and append it to its components object. src/App.vue

Working with Single File Components 207

If you hit reload in your browser you won’t see the Login component yet, because we need to reference it. Place it under the and you will have a nice login form!
src/App.vue

...
...

Working with Single File Components
208
If you open the browser’s console, you should see the login message we are logging when the component is created. If you are using vue-devtools, which is highly recommended, you should also see it in the components tree view.
Working with Single File Components
209
Tree View
Let’s create another component, this time for registration.
src/components/Register.vue

## Register Form

Then we can import it in the App.vue file.
src/App.vue

...
...

...
...
The Register component’s template appears, when we check the browser.

Working with Single File Components
211
Note
Register Component
The other components are commented out because we don’t want to display them one under the other. The Hello component is there by default, but we are not going to use it in any further examples, so we will remove it.
We said that we are working on a social network (or something relevant), so we want a place to display the stories. Thus, we are going to create a Stories component which when is rendered, it will bring all the stories told by the users.
Working with Single File Components 212
src/components/Stories.vue

• {{ story.writer }} said "{{ story.plot }}"

This is the Stories.vue file. We can use it in our main App.vue file. At this point the stories are hard-coded for simplicity. Time to import it just like the other components.
src/App.vue

Working with Single File Components
214
Stories Component
Great! Now we have a page to display all the listings.
16.3.1 Nested Components
We would like to be able to display the most “famous” stories, at any place we want to. So after the creation of the Famous component, we should be able to use it anywhere.
src/components/Famous.vue

## Trending stories({{famous.length}})

• {{ story.writer }} said "{{ story.plot }}".
• Working with Single File Components 215

This is the whole Famous.vue file. We have filtered the stories array using computed properties,
as we saw in previous chapters, and created a template to display them. Note
stories array is hard coded again here and data are the same as before. This is a bad practice, we will find a way later to define stories array once and share it between all components.
But where could we use this component? An idea is to have it within the registration page, so the user could read the most trending stories and be intrigued. This means - in the current project - that we need to have the Famous component within the Register one. Well, this can be done the same way we did it inside App.vue.
So, open Register.vue, import it there, and reference it within the template.
src/components/Register.vue

## Register Form

...

Working with Single File Components
217
Registration page with top stories
Pay attention to the import file path. Now that the two files are in the same directory, you have to use ./Famous instead of the full path. This is an easy mistake to make, especially if you’re not familiar with it!
Working with Single File Components 218
Code Examples
You can find the code examples of this chapter on GitHub13. 13https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter16/16.3

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
17. Eliminating Duplicate State
In the previous examples we hardcoded the data -the array of stories- within each component. This is not a proper way to work with data.
When more than one component uses the same data, it’s a good practice to create/fetch the array once, and then find a way to share it between application’s components.
Stories.vue and Famous.vue are using the same stories array. We will review two ways of sharing the data:
1. Using component properties. 2. Using a global store.
17.1 Sharing with Properties
The first thing we are going to do is to move the stories array to App component. src/App.vue

Eliminating Duplicate State 220
The next step is to remove the data() from Stories and Famous components, and declare stories property.
Let’s do it for the first component.
src/components/Stories.vue
1
2
3
4}
5

src/components/Famous.vue

This implementation works, but is not efficient, because Famous component is not independent. This means that we cannot use it wherever we want, unless we pass down the data from root component, App.vue.
In a scenario where a not independent component is deeply nested, you will have to pass a useless property, from component to component, just to be able to use it. In our case, if we wanted to use Famous inside Register’s sidebar’s widgets, we would have to carry the stories array all the way long.
App -> Register -> Sidebar -> WidgetX -> Famous
17.2 Global Store
The “props way” seemed nice at first, but as seen in the Famous component, as a project gets bigger and components get nested into others, data management and sharing between them gets really hard to track.
So, let’s make the data of our examples a bit easier to handle. We can extract the stories data to a .js file, store them to a constant and later import them at the desirable locations.
I’ll name our js file store.js and put it under the /src directory.
src/store.js
export const store = { stories: [
plot: 'My horse is amazing.', writer: 'Mr. Weebl', upvotes: 28,
voted: false
},
plot: 'Narwhals invented Shish Kebab.', writer: 'Mr. Weebl',
voted: false
}, {
plot: 'The dark side of the Force is stronger.',
Eliminating Duplicate State 224
10
11
12
13
14
15
16
17
1
2 3{ 4
5
6
7
8 9{
18
19
20
21
22
23
24
25
26
27
28
voted: false },
{
plot: 'One does not simply walk into Mordor', writer: 'Boromir',
voted: false
10
11
12
13
14
15
},
created () {
console.log('stories')
}
}

Because we are importing the store object we have to change the component’s template as well.
Eliminating Duplicate State 225
} ]
}
Warning
The stories prop must be removed from all files, because we have changed the way of data storage and there can be conflicts, which can break our build.
After we have stored all data within store.js we can import it within Stories.vue using the ES6 modules syntax.
src/components/Stories.vue

The same thing applies for Famous.vue.
src/components/Famous.vue
{store} from '../store.js'
default {
1
2
3
4
5
6
7 8} 9 },
10 computed: { 11 famous () {

If we hadn’t bind to stories, famous() computed property would have to be updated to filter
this.store.stories.
Once you get used to work with global objects I believe you are going to love it! 🙂
Code Examples
You can find the code examples of this chapter on GitHub1. 1https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter17
Eliminating Duplicate State 227

18. Swapping Components
Using Single File Components is the simplest way to build a Single Page Application with Vue. We’ve seen so far how to setup a fresh project, create .vue files and manage duplicate state. Now it’s time to review a way to swap view-specific components.
For reference, in the previous examples, we had 3 components inside App.vue and some others nested within. We need to find a way to swap components dynamically, so they won’t be rendered on the page simultaneously.
18.1 Dynamic Components 18.1.1 The is special attribute
We can use the reserved element and use the same mount point to dynamically switch between multiple components, using the is special attribute.
src/App.vue

This is very useful...

Swapping Components 229
We’ve created a fresh project and modified the Hello.vue file. We have the exact same result as before, but here we are using the element. The Hello component is bound to the is attribute. To see how this would work dynamically, check the next example where we are changing between 2 different components by clicking on their links.
Firstly, create a similar component with a different message, named Greet.vue.
src/Greet.vue

# {{ msg }}

We made Greet to display a message to manifest its presence! Let’s import it into App and give the
user the ability to swap between the 2 components.
src/App.vue

This is very useful...
Show Hello
Show Greet

Swapping Components 230

Swapping Components
231
Greet.vue
Well, as you can see, we are binding the special attribute is to currentComponent, so when its value changes, the displaying component will also change. To swap the view, the user just have to click on either link to change the value of currentComponent.
This dynamic way of switching between multiple components can prove handy.
In the previous examples , we used .vue files to simulate a social network, where we had components like Login, Registration, etc. Now we can use a tab system to navigate between these components with style.
We are going to have Stories.vue in one tab, Register.vue in another, and Login.vue in a third. Don’t forget that Register contains the Famous component, which returns the most trending stories.
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
src/App.vue

# Welcome to dynamic Components!

Swapping Components 233

Swapping Components
234
Let’s break it down.
A page for each component
The array named pages contains the components which we would like to render. We are using the v-for directive to create a tab for each one.
Swapping Components 235 To navigate between the tabs, we’ve created a method called setPage.
The activePage property is initially set to 'stories'. When a tab is clicked, activePage changes in order to reflect the name of the component we wish to display.
To determine which tab must be active, an in-line if is applied, which sets the class active whether the current activePage property matches the current component’s name.
To make the first letter of each tab capitalize, we’ve created a Vue.filter(), named capitalize, which is used within text interpolations.
With these few and simple lines of code, we have accomplished a simple navigation system, swapping between our components.
Code Examples
You can find the code examples of this chapter on GitHub1. 1https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter18

19. Vue Router
Routing, in general, refers to determining how your application responds to a client request. A web browser request wouldn’t be directed to your application without some form of routing. Router helps a web server to fetch an appropriate and exact information for the user. It is like a station master at a railway station, who informs the train operator when to change tracks.
The way of swapping views we just studied, paves the way for routing. The official router for Vue.js is called vue-router. It is deeply integrated with Vue.js core to make building Single Page Applications a breeze. This plugin is relatively easy to understand, install, and use.
The main features are:
• Nested route/view mapping
• Modular, component-based router configuration
• Route params, query, wildcards
• Links with automatic active CSS classes
• HTML5 history mode or hash mode, with auto-fallback in IE9 • Restore scroll position when going back in history mode
Info
These are just some of the provided features, you can see more on Github1. Also, here is the official documentation2.
19.1 Installation
There are the usual ways to install the plugin; using cdn, NPM, and Bower. We are going to use the terminal to install it via NPM.
npm install vue-router
Type this command in your terminal to have it installed in your node_modules folder inside your project’s directory. After it is complete, go to your main.js file and add the following lines.
1https://github.com/vuejs/vue- router 2http://router.vuejs.org/en/index.html

Vue Router 237
src/main.js
import Vue from 'vue'
import VueRouter from 'vue-router' Vue.use(VueRouter)
You can install a Vue.js plugin using Vue.use() as shown. For more information about Vue.use check the guide3.
19.2 Usage
The first step is to create a router instance, where we will pass extra options later on, but let’s keep it simple for now.
src/main.js
...
const router = new VueRouter({
routes // short for routes: routes })
Now, we have to define some routes. Each route should map to a component, which means that we are going to create routes for the *.vue files we’ve been using all along in our examples.
The main method to define route mappings for the router, is to create a routes array where we can pass route objects.
src/main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{ path: '/', component: Hello },
]
3http://vuejs.org/api/#Vue- use
Vue Router 238 Inside the array we define 2 routes.
1. When http://localhost:3000/ is met (or any port you might use), the default Hello.vue will be rendered
We are using { path: '/login', component: Login } because the Login component is imported on top of the code. Alternatively you could use require() function like this: { path: '/login', component: require('Login') }
The next step is to create an outlet for the router. The guide states that the router needs a root component to render. In our case, we will use the App.vue component as root.
Let’s create and mount the root instance. Keep in mind that we have to inject the router with the router option to make the whole app router-aware.
src/main.js
...
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '', components: {
App }
})
We set the template to so Vue will replace the div with the id of app with App.vue
component’s template. Since we are already importing App in our main.js file, we are set here.
If you neglect to include the comment /* eslint-disable no-new */ you will get an error from
eslint stating:
http://eslint.org/docs/rules/no-new Do not use ‘new’ for side effects What we have to do here is to disable that rule.
Tip
Every time you find yourself struggling with an eslint rule, you can disable it by adding a comment like the above. For example /* eslint-disable eqeqeq */. You can find a complete list with the rules here4.
4http://eslint.org/docs/rules/

Vue Router 239
Now, we need to define some links for the navigation. Head to App.vue, to make the changes required.
src/App.vue

# Welcome to Routing!

The is the place where all the magic happens while components are being rendered for us.
router-link is the component that lets users navigate in a router-enabled app. The to property defines the target location, matching a route defined in main.js. By default, router-link will render an tag with the href attribute set to the desired URI. It can take more arguments for more complex navigations, which we will review soon.
Note
If you are using the vue-cli template with router included, you have to define your routes array within router/index.js.
19.3 Named Routes
While the options we reviewed for routing serve our needs in a small project, like our example, presumably you will need more options as your project grows. For instance, if we decide later to change /login url to /signin, we will have to update all the links directing to the login page. To prevent this from happening, we can give each route a name.
To name a route we have to alter the route configuration.

Vue Router 240
src/main.js
...
const routes = [ {
path: '/',
name: 'home',
component: Hello
}, {
} ]
We can give a name to a route by adding a name property and use it as identifier to link it afterwards. src/App.vue

...

Notice here that instead of using a string to define the destination of the link (to="/home"), we are using an object (:to="{ name: 'home'}"). We will elaborate on that later.
19.4 History mode
Before moving on, I would like to point out something regarding the browser URL, when using vue- router. As you have seen, when a route changes, a ‘#’ symbol is appended to the URL. For instance, the URL is /#/login, when we navigate to the login page.
This is caused by the default mode for vue-router, hash mode, which uses the URL hash to simulate a full URL so that the page won’t be reloaded when the URL changes. To get rid of the hash, we can change to history mode. Also we will set another option, base, which defines a root path for all

Vue Router 241
router navigations. Changing this to anything from its initial value, that equals default, will result in paths which will always include the new value in the actual browser URL.
For example if we set base to /vuejs the login page will be /vuejs/login. You can set anything to be the base, here let’s just put /.
src/main.js
const router = new VueRouter({ mode: 'history',
base: '/',
routes
})
This relieves us from ‘#’ in the URLs.

Vue Router
242
Info
Neat URLs
Check the detailed list of available options on vue-router’s documentation5
19.5 Nested routes
Nested routes, are routes that live within other routes. Mapping nested routes to components is a common need, and it’s also very simple with vue-router.
To demonstrate it, we are going to add a page to display the stories with 2 sub-pages. 5https://router.vuejs.org/en/api/options.html

Vue Router 243 • One to display all stories (StoriesAll.vue)
• One to display famous stories (StoriesFamous.vue)
We will create the above-mentioned components, plus one, which will be the wrapper, similar to
App.vue.
Let’s start by registering the new routes. All we have to do is to use the children option in the
routes array and add a nested within our wrapper view, StoriesPage.vue. src/main.js
import Vue from 'vue' import App from './App'
import Hello from './components/Hello.vue'
import StoriesPage from './components/StoriesPage.vue' import StoriesAll from './components/StoriesAll.vue' import StoriesFamous from './components/StoriesFamous.vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [ {
path: '/',
component: Hello
},
{
}, {
}, {
path: '/stories',
component: StoriesPage,
children: [
{
path: '',
name: 'stories.all',
component: StoriesAll
path: 'famous',
Vue Router 244
name: 'stories.famous',
component: StoriesFamous
}
] }
]
Notice here that the path for StoriesAll is set to ''. This means that it is the default child route
and will be rendered when /stories is matched. You can also use '/' to define a default route. The contents of StoriesFamous will be rendered when /stories/famous is matched.
At this point, there is no need to show what’s inside these components. They both just display an array of stories.
Our wrapper component, StoriesPage, contains 2 links and the tag, to render its child components’ contents.
src/StoriesPage.vue

## Stories

All Trending

19.6 Auto-CSS active class
Wouldn’t be nice if we highlighted the link that directs to the active page? Vue Router is smart enough to append a css class to the active link. This class is vue-router-active.
All we have to do is to add a rule to style it in our css. I will add it to App.vue component.

Vue Router 245
src/App.vue
...

So, now, every time we visit a page, the corresponding link turns green..
If you try this out in the browser, you will notice that the Home link is always green. This happens because Home’s path is /, so when you visit for example /login, Home remains active. To get rid of this behavior we can add the exact prop to this specific link.
src/App.vue

...

We have append exact to the first link within StoriesPage.vue too.
The nice part is that the active link is highlighted in the secondary navigation as well.

Vue Router
246
19.6.1 Custom Active Class
Active Class
You can change the name of the active class (router-link-active) for a specific link, using the active-class property or globally by using the linkActiveClass router constructor option.
Vue Router 247
Local custom active class
Home

Global custom active class
const router = new VueRouter({
mode: 'history',
base: '/',
})
19.7 Route Object
All information of a parsed route will be accessible on the exposed Route Context Object, also called route object.
The route object will be injected into every component in a router-enabled app and will be accessible as this.$route. It will be updated whenever a route transition is performed. Below is a list with$route object’s properties. Property Description
path
params
query
hash
fullPath
matched
name
A string that equals the path of the current route, always resolved as an absolute path. e.g. /foo/bar.
An object that contains key/value pairs of dynamic segments and star segments. More details below.
An object that contains key/value pairs of the query string. For example, for a path /foo?user=1, we get $route.query.user == 1. The hash of the current route (without #), if it has one. If no hash is present the value will be an empty string. The full resolved URL including query and hash. An Array containing route records for all nested path segments of the current route. Route records are the copies of the objects in the routes configuration Array (and in children Arrays). The name of the current route, if it has one. Vue Router 248 19.8 Dynamic Segments Vue Router provides the ability to form paths using dynamic segments. Dynamic segments are segments with a leading colon. They are called dynamic because their value is changeable. Info URL segments are the parts of a URL or path delimited by slashes. If you had the path /user/:id/posts, then user, :id, and posts would each be a segment. In this path, the :id is the dynamic segment and will match any provided value, for instance /user/11/posts, /user/37/posts, etc. When a path containing a dynamic segment is matched, the dynamic segments will be available inside$route.params.
In our example, we can use a dynamic segment to access a certain story by its id, in order to create a view where we can edit it.
src/main.js
1
2 3{
4 path: ':id/edit',
5 name: 'stories.edit',
6 component: StoriesEdit
7} 8 ... 9]
Now, we need a way to link StoriesAll.vue with StoriesEdit.vue. Let’s manipulate the file. Note
I have created the StoriesEdit.vue file in the background. You will see it soon, after the routing for it is complete.
const routes = [ // other routes

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
src/component/StoriesAll.vue

### All Stories ({{stories.length}})

• #### {{ story.writer }} said "{{ story.plot }}" {{ story.upvotes }}

Note
Our example files are almost the same as before, with minor alterations, mostly styles, which do not affect their functionality. A noteworthy change is the addition of an id to each story within store.js.

1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
Vue Router 250 We’ve added a button to link the stories.edit route. Well, this is not enough, because we also need
to pass story’s id to the route.
To do so, we are going to edit the to prop and make the :id turn into the corresponding id of each
story.
src/component/StoriesAll.vue

### All Stories ({{stories.length}})

• #### {{ story.writer }} said "{{ story.plot }}"{{ story.upvotes }}

Here we want to render a

We are using story’s id, to pick the desired story out of stories array with JavaScript’s find
method6.
The chosen story is ready for editing. Notice that the id of the story is shown in the URL.
6https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Vue Router 251

Vue Router
252
Editing selected story
This works fine if you visit the StoriesEdit page using the button but if you try to type the URL directly into the browser’s address bar, e.g. /stories/2/edit, you’ll get an error and the component won’t render.
The reason behind this is that we used strict equality7 (===) to find the right active story. When visiting the page directly, id is passed as string (not number). So, isTheOne always returns false.
7https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity

Vue Router 253
src/components/StoriesEdit.vue
isTheOne (story) {
// returns false when this.id is not a number
return story.id === this.id }
The easy-fix is to convert the id to number, using the Number JavaScript object wrapper.
src/components/StoriesEdit.vue
export default { ...
mounted () {
this.id = Number(this.$route.params.id) } } If you check this in the browser you will find out that the story comes up as if we clicked the edit button. Though, there is a much better solution which helps us decouple the StoriesEdit component from$route. We can cast the id parameter to number within route’s configuration and use it as a prop in the StoriesEdit component.
src/main.js
const routes = [ ...
{
path: ':id/edit',
props: (route) => ({ id: Number(route.params.id) }),
name: 'stories.edit',
component: StoriesEdit
},
... ]
Now, when stories/:id/edit is met, router will pass the numeric value of :id as a prop- erty to the StoriesEdit component. What’s missing is to define that property and remove this.$route.params.id. So, our component will look like Vue Router 254 src/components/StoriesEdit.vue That’s it. Now the StoriesEdit component is decoupled from the route and we can use it anywhere like this: . You can have a look at the attributes of the$route object, using Vue Devtools.

Vue Router
255
19.9 Route Alias
Inside $route When we define new routes, we usually try to make them clear and representative. Sometimes though, we might end up with long paths or complex ones, which can be difficult to handle later. When we want to view the famous routes through the browser, we have to visit '/stories/famous'. We can make this shorter by defining a global alias for this route, where a shorter URL would lead us to the same place. Vue Router 256 src/main.js { path: 'famous', name: 'stories.famous', // match '/famous' as if it is '/stories/famous' alias: '/famous', component: StoriesFamous } Using this configuration, we can just use the alias instead of the path. Using route alias Vue Router 257 19.10 Programmatic Navigation At some point, we want to navigate to a route not through links, but programmatically. To navigate to a route, we can use router.push(path). The path can be either a string or an object. If it is a string, the path must be in the form of plain path, meaning that it can not contain dynamic segments. For example router.push('/stories/11/edit'). If it is an object, you can pass any needed arguments. Programmatic navigation router.push({ path: '/stories/11/edit' }) router.push({ name: 'stories.edit', params: {id: '11'} }) We can use router.push to navigate to the stories’ listing page after editing is complete. src/components/StoriesEdit.vue 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 }, methods: { saveChanges (story) { console.log('Saved!') this.$router.push('/stories')
},
isTheOne (story) {
return story.id === this.id }
},
mounted () {
this.story = store.stories.find(this.isTheOne) }
}

<

script>
import {store} from '../store.js'
export default { props: ['id'], data () {
return { story: {}
1
2
3
4
5
6
7
8 9}

1 2 3 4 5 6 7 8 9
10
Vue Router 258 We have updated the saveChanges method. When called, it logs a message to the console and using
this.$router.push(), navigates back to /stories. If you want to direct the user to the URL he visited previously, instead of a specific URL, you can use router.back(). In our case we can add button and call the router.back function, instead of router.push, and this time the user will be able to navigate to the previous page (whichever this is, for example https://google.com). src/components/StoriesEdit.vue ... methods: { ... goBack () { this.$router.back()
},
... }
Another way to do this is to use the router.go(n) method, which takes a single integer as parameter that indicates by how many steps to go forwards or go backwards in the history stack8.
goBack () { this.$router.go(-1) } Warning By using$router.back(), or any other router’s method, we are coupling the component to the router. If you want to keep it uncoupled, you can use window.history.back().
19.11 Transitions 19.11.1 Introduction
Each time we navigate to another page of our application nothing fancy happens. We can change that by using a transition to animate the component that enters the page and also the one that leaves.
8http://router.vuejs.org/en/essentials/history- mode.html

Vue Router 259
Vue provides a variety of ways to apply transition effects when items are inserted, updated, or removed from the DOM. This includes tools to:
• automatically apply classes for CSS transitions and animations
• integrate 3rd-party CSS or JavaScript animation libraries, such as Animate.css9, Velocity.js10,
etc
• use JavaScript to directly manipulate the DOM during transition hooks
At this point, we’ll only cover entering and leaving using CSS classes. If you are interested in learning more about transitions check the guide11.
To use a transition, we have to wrap the corresponding element within the transition component. In our case, router-view component.
Vue will append v-enter CSS class to the element before it’s inserted and v-enter-active during the entering phase. v-enter-to is the last class to be appended before the transition is complete. This is actually the ending state for enter.
Accordingly, when the element is being removed from the DOM, v-leave, v-leave-active, and v-leave-to will be applied.
Transition Classes
If a name for the transition is defined, all the above mentioned classes will contain the name instead of ‘v’. For example fade-enter, fade-leave-to, etc.
9https://daneden.github.io/animate.css/ 10http://velocityjs.org/ 11https://vuejs.org/v2/guide/transitions.html

Vue Router 260
19.11.2 Usage
Lets use a transition, named fade, for our route outlet. src/App.vue

...

Take a look at the CSS classes. The transition will start with opacity 0 which will be gradually increasing for 1 second. .fade-enter-to is not necessary, defining it like this will create a blunt pop, from 0.8 to 1, just before the transition is finished.
To create the reverse animation when a component leaves, we have to alter our CSS like this:

Vue Router 261
19.11.3 3rd-party CSS animations
Creating animations from scratch, and designing in general, is a hard task for me. I always prefer to rely on 3rd party libraries. Fortunately for me (and you maybe), using Vue transitions is pretty easy to integrate 3rd-party CSS and JS animation libraries.
In this example I will use Animate.css12.
Animate.css
According to the documentation, to use Animate.css you have to:
1. Include the stylesheet on your document’s . 12https://daneden.github.io/animate.css/

Vue Router 262
2. Add the class animated to the element you want to animate. You may also want to include the class infinite for an infinite loop.
3. Finally you need to add one of the available classes, such as bounce, rollIn, fadeIn, etc. You can find a list with all available classes here13.
Lets start by importing it from CDNJS, in the header of our index.html file.
Instead of relying on the name prop of the transition component, this time we will use a custom
class for v-enter-active.
src/App.vue

...

We can also apply an animation when the component leaves the DOM by appending a custom leave-active-class, like this: leave-active-class="animated rollOut".
Vue Router provides a convenient mechanism for filtering transitions. To filter a transition you can use router.beforeEach() which is triggered before each transition, and router.afterEach(), which is triggered after, however afterEach() cannot affect the navigation.
router.beforeEach() can be handy in a scenario regarding authorization. For example if a user does not have permission to access a page of your app, he should be directed to the login page.
Let’s see how we can accomplish that in a short example.
13https://github.com/daneden/animate.css
Vue Router 263
src/main.js
1
2
3 4} 5
// create a dummy user object
6 router.beforeEach((to, from, next) => {
10 } else {
11 // if authorized, proceed
12 next()
13 }
14 })
Here we apply a rule so router won’t let users proceed to any page except login. Make sure to always call the next() function, otherwise the hook will never be resolved.
Code Examples
You can find the code examples of this chapter on GitHub14.
14https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/codes/chapter19
Vue Router 264
19.13 Homework
Throughout this chapter we analyzed a lot of things and it’s been a while since we’ve assigned you some homework!
The app you have to build is a mini Pokédex.
The home page will show a list of Pokémon categories, such as Fire, Water, etc. From there, the user will be able to browse a category, view its Pokémon, and add new ones.
Your routes could be something like these:
Route
/
/category/:name /category/:name/pokemons/new
Description
List categories.
Show category’s Pokémons. Add new Pokémon to category.
Each transition must be logged to the console. For example, when the user decides to browse category Fire, a message has to be logged, informing the user that he is going to visit /category/Fire.
We have created the Pokédex object to help you get started. You can find it here15.
Info
The /category/:name/pokemons/new route is a subroute of /category/:name.
When the user visits /category/:name/pokemons/new s/he should see a form to add a new
Pokémon along with the listing of the category’s Pokémons.
Hint 1
To access Pokémon of a specific category, consider using JavaScript’s find method16.
Hint 2
To log messages to the console before each transition use router.beforeEach().
15https://github.com/hootlex/the- majesty- of- vuejs- 2/blob/master/homework/Chapter19/pokedex.js 16https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

Vue Router
265
Example output
Vue Router
266
Example output
You can find a potential solution to this exercise here17
17https://github.com/hootlex/the- majesty- of- vuejs- 2/tree/master/homework/Chapter19