Summary of Vue JS2 Tutorial by Net Ninja (Part 2 of 3)
JL’s Self-teaching Story #3

🌲 This is the second part of my summary of Vue JS Tutorial by Net Ninja on YouTube.
16) The Vue CLI
17) Vue Files & The Root Components
18) Nesting Components
19) Component CSS
20) Nesting Components Examples
21) Props
22) Reference vs Primitive Types
23) Events (Child to Parent)
24) The Event Bus (Child to Child)
25) Life-cycle Hooks
26) Slots
27) Dynamic Components
28) Input Binding
29) Checkbox Binding
30) Select Box Binding16) The Vue CLI (YouTube)
CLI stands for “Command Line Interface”. Vue CLI is a standard tooling for Vue.js development (official website).
It allows us to create a dev environment workflow with webpack. Benefits of using webpack are the following:
- Use ES6 features
- Compile & minify our JS into one file
- Use single-file templates
- Compile everything on our machine, not in a browser
- Run live reload dev server
In the video, Vue CLI2 was used. However, since the video had been released on April 30, 2017, Vue CLI3 came out. So, I used Vue CLI3 instead.
If you have the previous vue-cli (1.x or 2.x) package installed globally, you need to uninstall it first with npm uninstall vue-cli -g.
- Used
npm install -g @vue/cliinstead ofnpm install -g vue-cli. - Run
vue create <project name>and manually select options by pressing the space bar vue init webpack-simple <project-name>: installwebpack-simpletemplate- To get started, the following needs to be entered in order:
cd <project-name>,npm install(install all dependencies needed forwebpack-simple), andnpm run dev(run a development server). - For some reasons, I kept getting an error. I found a solution after searching the internet for a while. The following solution worked for me!
[Step 1] npm install webpack-dev-server@2.9.7 --save -dev (version 2.9.1 was automatically installed.)
[Step 2]
[package.json]
"scripts" : {
"dev" : "cross-env NODE_ENV=development webpack-dev-server --port 8080 --hot --host 0.0.0.0"}
}17) Vue Files & The Root Component (YouTube)
[ES6 Syntax]
importstatement in main.js filedata()instead ofdata: function()in app.vue file.
18) Nesting Components (YouTube)
[Root Component]
1) Header Component : Links Component, Login Component
2) Article Component
3) Footer ComponentThere are two ways to register components : globally & locally.
[index.html]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>vuejs-playist</title>
</head>
<body>
<div id="app"></div>
<script src="/dist/build.js"></script>
</body>
</html>
-----------------------------------[src > ninjas.vue]
<template>
<ul>
<li v-for="ninja in ninjas">{{ ninja }}</li>
</ul>
</template><script>
export default{
data(){
return{ ninjas: ['A', 'B', 'C'] }
}
}
</script>
1) Globally
[src > main.js]
import ninjas from './ninjas.vue'
Vue.component('ninjas', ninjas)
-----------------------------------[src > App.vue]
<template>
<div>
<h1>{{ title }}</h1>
<ninjas></ninjas>
</div>
</template><script>
export default{
data(){
return{ title : 'Ninja App' }
}
}
</script>
2) Locally
[src > main.js]
// delete what we entered for registering components globally
-----------------------------------[src > App.vue]
<template>
<!-- same as above -->
</template><script>
import ninjas from './ninjas.vue'export default{
components : { 'ninjas' : ninjas }
// same as above
}
</script>
19) Component CSS (YouTube)
[Part 1] : Component template should contain exactly one root element. => The solution is to wrap elements in a div.
[src > ninjas.vue]
<template>
<div>
<h1>List of Ninjas</h1>
<ul>
<li v-for="ninja in ninjas">{{ ninja }}</li>
</ul>
</div>
</template>[Part 2] If we would like to style differently for each vue file, we need to use <style scoped> instead of <style>.
20) Nesting Components Examples (YouTube)
[Root Component]
1) Header Component : Title
2) Ninja Component : List of ninjas
3) Footer Component : Copyright notice`
> When we click a div, “speciality” is toggled. :)
[index.html]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>vuejs-playist</title>
</head>
<body>
<div id="app"></div>
<script src="/dist/build.js"></script>
</body>
</html>
-----------------------------------[src > main.js]
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
});
-----------------------------------[src > App.vue]
<template>
<div>
<add-header></add-header>
<add-ninjas></add-ninjas>
<add-footer></add-footer>
</div>
</template>
<script>
import header from './components/header.vue';
import people from './components/ninjas.vue';
import footer from './components/footer.vue';
export default{
components:{
'app-header': header,
'app-ninjas': people,
'app-footer': footer
}
}
</script>
-----------------------------------[src > components > header.vue]
<template>
<header>
<h1>{{ title }}</h1>
</header>
</template><script>
export default{
data(){
return{ title: 'Vue Ninjas' }
}
}
</script>
<style scoped>
header{
background: lightgreen;
padding: 10px;
}
h1{
color: black;
text-align: center;
}
</style>
-----------------------------------[src > components > ninjas.vue]
<template>
<div id='people'>
<ul>
<li v-for='ninja in ninjas' @click='ninja.show = !ninja.show'>
<h2>{{ ninja.name }}</h2>
<h3 v-show='ninja.show'>{{ ninja.speciality }}</h3>
</li>
</ul>
</div>
</template>
<script>
export default{
data(){
return{
ninjas: [
{name: 'Ryu', speciality: 'Vue Components', show: false},
{name: 'Crystal', speciality: 'HTML Wizardry', show: false},
{name: 'Hitoshi', speciality: 'Click Events', show: false},
{name: 'Tango', speciality: 'Conditionals', show: false},
{name: 'Kami', speciality: 'Webpack', show: false},
{name: 'Yoshi', speciality: 'Data Diggin', show: false}
]
}
}
}
</script>
<style scoped>
#people{
width: 100%;
max-width: 1200px;
margin: 40px auto;
padding: 0 20px;
box-sizing: border-box;
}
ul{
display: flex;
flex-wrap: wrap;
list-style-type: none;
padding: 0;
}
li{
flex-grow: 1;
flex-basis: 300px;
text-align: center;
padding: 30px;
border: 1px solid #222;
margin: 10px;
}
</style>
-----------------------------------[src > components > footer.vue]
<template>
<header>
<h1>{{ copyright }}</h1>
</header>
</template>
<script>
export default{
data(){
return{ copyright: 'copyright 2017 Vue Ninjas' }
}
}
</script>
<style scoped>
footer{
background: black;
padding: 6px;
}
p{
color: lightgreen;
text-align: center;
}
</style>
Available to see code with syntax highlighting on GitHubGist.
21) Props (YouTube)

- Defined “ninjas” in the root component (app.vue) instead of defining “ninjas” in a child component (ninjas.vue).
ninjas : { type : Array, required: true }was used for a validation purpose.
[src > App.vue]
<template>
<div>
<add-header></add-header>
<add-ninjas v-bind:ninjas='ninjas'></add-ninjas>
<add-footer></add-footer>
</div>
</template>
<script>
// same as above
export default{
components:{
// same as above
},
data(){
return{
ninjas: [
{name: 'Ryu', speciality: 'Vue Components', show: false},
{name: 'Crystal', speciality: 'HTML Wizardry', show: false},
{name: 'Hitoshi', speciality: 'Click Events', show: false},
{name: 'Tango', speciality: 'Conditionals', show: false},
{name: 'Kami', speciality: 'Webpack', show: false},
{name: 'Yoshi', speciality: 'Data Diggin', show: false}
]
}
}
}
</script>
-----------------------------------[src > components > ninjas.vue]
<template>
<!-- same as above -->
</template>
<script>
export default{
props:{
ninjas:{
type: Array,
required: true
}
}
}
</script>
<style scoped>
/* same as above */
</style>
Available to see the full code with syntax highlighting on GitHubGist.
22) Reference vs Primitives Types (YouTube)
[Examples]
- Reference : Objects, Arrays
- Primitive : Strings, Booleans, Numbers
[Characteristics]
- Reference : Modifying a shared element in a file affect the shared element in another file.
- Primitive : Modifying a shared element in a file doesn’t affect the shared element in another file.
1) Reference Type

When pressing “Delete ninja” button, shared data in both instances gets affected.
[src > App.vue]
<template>
<div>
<add-header></add-header>
<add-ninjas v-bind:ninjas='ninjas'></add-ninjas>
<hr />
<add-ninjas v-bind:ninjas='ninjas'></add-ninjas>
<add-footer></add-footer>
</div>
</template>
<script>
components:{
// same as above
},
data(){
return{
ninjas : [
// same as above
]
}
}
</script>
-----------------------------------[src > components > ninjas.vue]
<template>
<div id='people'>
<ul>
<!-- same as above -->
</ul>
<button @click='deleteNinja'>Delete Ninja</button>
</div>
</template>
<script>
export default{
props:{
ninjas:{
type: Array,
required: true
}
},
methods:{
deleteNinja: function(){
this.ninjas.pop();
}
}
}
</script>
<style scoped>
/* same as above */
</style>
Available to see the full code with syntax highlighting on GitHubGist.
2) Primitive Type

When clicking “Vue Ninja” tag in header.vue, shared data in footer.vue doesn’t get changed.
[src > App.vue]
<template>
<div>
<add-header v-bind:title='title'></add-header>
<add-ninjas v-bind:ninjas='ninjas'></add-ninjas>
<add-footer v-bind:title='title'></add-footer>
</div>
</template>
<script>
// same as aboveexport default{
components:{
// same as above
},
data(){
return{
ninjas : [
// same as above
],
title: 'Vue Ninjas'
}
}
}
</script>
-----------------------------------[src > components > header.vue]
<template>
<header>
<h1 @click='changeTitle'>{{ title }}</h1>
</header>
</template>
<script>
export default{
props:{
title:{
type: String
}
},
methods:{
changeTitle: function(){
this.title = 'Vue Wizards'
}
}
}
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > components > footer.vue]
<template>
<header>
<h1>{{ copyright }} {{ title }}</h1>
</header>
</template>
<script>
export default{
props:{
title:{
type: String
}
},
data(){
return{ copyright: 'copyright 2017' }
}
}
</script>
<style scoped>
/* same as above */
</style>
Available to see the full code with syntax highlighting on GitHubGist.
23) Events (Child to Parent) (YouTube)

When clicking “Vue Ninja” tag in header.vue, shared data in footer.vue gets changed.

[src > components > header.vue]
<template>
<header>
<h1 @click='changeTitle'>{{ title }}</h1>
</header>
</template>
<script>
export default{
props:{
title:{
type: String
}
},
methods:{
changeTitle: function(){
this.$emit('changeTitle', 'Vue Wizards')
}
}
}
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > App.vue]
<template>
<div>
<add-header v-bind:title='title' @changeTitle='updateTitle($event)'></add-header>
<add-ninjas v-bind:ninjas='ninjas'></add-ninjas>
<add-footer v-bind:title='title'></add-footer>
</div>
</template>
<script>
// same as above
export default {
components: {
// same as above
},
data(){
return{
ninjas: [
// same as above
],
title: 'Vue Ninjas'
}
},
methods:{
updateTitle: function(newTitle){
this.title = newTitle;
}
}
</script>
Available to see the full code with syntax highlighting on GitHubGist.
24) The Event Bus (Child to Child) (YouTube)
The Event Bus allows us to communicate among children without interacting with parents.
[src > main.js]
// same as above
export const bus = new Vue();
new Vue({
// same as above
});
-----------------------------------[src > components > header.vue]
<template>
<header>
<h1 @click='changeTitle'>{{ title }}</h1>
</header>
</template>
<script>
import { bus } from '../main';
export default{
props:{
// same as above
},
methods: {
changeTitle: function(){
this.title = 'Vue Hello';
bus.$emit('titleChanged', 'Vue Hello');
}
}
}
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > components > footer.vue]
<template>
<header>
<h1>{{ copyright }} {{ title }}</h1>
</header>
</template>
<script>
import { bus } from '../main';
export default{
props:{
// same as above
},
data(){
return { copyright: 'copyright 2018' }
},
created(){
bus.$on('titleChanged', (data) => {
this.title = data;
});
}
}
</script>
<style scoped>
/* same as above */
</style>
If we didn’t include this.title = 'Vue Hello', only the shared data in footer.vue gets changed when clicking “Vue Ninja” tag in header.vue.
Available to see the full code with syntax highlighting on GitHubGist.
26) Slots (YouTube)
A way to create a form easily based on a template => provide a general outline/structure

[src > main.js]
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
});
-----------------------------------[src > App.vue]
<template>
<div>
<form-helper>
<div slot='form-header'>
<h3>This is the title of the form.</h3>
<p>Information about the form.</p>
</div>
<div slot='form-fields'>
<input type='text' placeholder='name' required />
<input type='password' placeholder='password' required />
</div>
<div slot='form-controls'>
<button @click='handleSubmit'>Submit</button>
</div>
</form-helper>
</div>
</template>
<script>
import formHelper from './components/formHelper.vue'
export default{
components:{
'form-helper': formHelper
}
}
</script>
-----------------------------------[src > components > formHelper.vue]
<template>
<div>
<h1>Please fill out the form...</h1>
<form>
<div id='form-header'>
<slot name='form-header'></slot>
</div>
<div id='form-fields'>
<slot name='form-fields'></slot>
</div>
<div id='form-controls'>
<slot name='form-controls'></slot>
</div>
<div id='useful-links'>
<ul>
<li><a href='#'>Link 1</a></li>
<li><a href='#'>Link 2</a></li>
<li><a href='#'>Link 3</a></li>
<li><a href='#'>Link 4</a></li>
</ul>
</div>
</form>
</div>
</template>
<style scoped>
h1{
text-align: center;
}
form{
width: 100%;
max-width: 960px;
margin: 0 auto;
}
#useful-links ul{
padding: 0;
}
#useful-links li{
display: inline-block;
margin-right: 10px;
}
form > div{
padding: 20px;
background: #eee;
margin: 20px 0;
}
#form-header{
background: #ddd;
border: 1px solid #bbb;
}
</style>
Available to see the full code with syntax highlighting on GitHubGist.
27) Dynamic Components (YouTube)
A tool to dynamically change which component is shown at a particular place in the browser
[src > App.vue]
<template>
<div>
<keep-alive>
<component v-bind:is='component'></component>
</keep-alive>
<button @click='component="form-one"'>Show Form One</button>
<button @click='component="form-one"'>Show Form Two</button>
</div>
</template>
<script>
import formOne from './components/formOne.vue';
import formTwo from './components/formTwo.vue';
export default{
components{
'form-one': formOne,
'form-two': formTwo
},
data(){
return{
component: 'form-two'
}
}
}
</script>
-----------------------------------[src > components > formOne.vue]
<template>
<div>
<form-helper>
<div slot='form-header'>
<h3>Form One - Contact Us</h3>
<p>Fill in this form to contact us</p>
</div>
<div slot='form-fields'>
<input type='text' placeholder='name' required />
<label>Your Message:</label>
<textarea></textarea>
</div>
<div slot='form-controls'>
<button @click='handleSubmit'>Submit</button>
</div>
</form-helper>
</div>
</template>
<script>
import formHelper from './components/formHelper.vue'
export default{
components:{
'form-helper': formHelper
}
}
</script>
-----------------------------------[src > components > formTwo.vue]
<template>
<div>
<form-helper>
<div slot='form-header'>
<h3>Form Two - Log In</h3>
<p>Enter your details to log-in</p>
</div>
<div slot='form-fields'>
<input type='text' placeholder='name' required />
<input type='password' placeholder='password' required />
</div>
<!-- same as formOne.vue -->
</form-helper>
</div>
</template>
<script>
// same as formOne.vue
</script>
isattribute is used to switch between components. (Official Website)- “When wrapped around a dynamic component,
<keep-alive>caches the inactive component instances without destroying them.<keep-alive>is an abstract component: it doesn’t render a DOM element itself, and doesn’t show up in the component parent chain.” (Official Website) data() { return { component: 'form-one' } }displays the default form when the webpage is loaded.
Available to see the full code with syntax highlighting on GitHubGist.
28) Input Binding (YouTube)
It allows us to show a preview of a blog post not yet published.
[src > App.vue]
<template>
<div>
<add-blog></add-blog>
</div>
</template>
<script>
import addBlog from './components/addBlog.vue';
export default{
components{
'add-blog': addBlog
}
}
</script>
-----------------------------------[src > components > addBlog.vue]
<template>
<div id='add-blog'>
<h2>Add a New Blog Post</h2>
<form>
<label>Blog Title: </label>
<input type='text' v-model.lazy='blog.title' required />
<label>Blog Content: </label>
<textarea v-model.lazy='blog.content'></textarea>
</form>
<div id='preview'>
<h3>Preview Blog</h3>
<p>Blog Title: {{ blog.title }}</p>
<p>Blog Content: </p>
<p>{{ blog.content }}</p>
</div>
</div>
</template>
<script>
export default{
data(){
return{
blog:{
title: '',
content: ''
}
}
}
}
</script>
<style scoped>
#add-blog *{
box-sizing: border-box;
}
#add-blog{
margin: 20px auto;
max-width: 500px;
}
label{
display: block;
margin: 20px 0 10px;
}
input[type="text"], textarea{
display: block;
width: 100%;
padding: 8px;
}
#preview{
padding: 10px 20px;
border: 1px dotted red;
margin: 30px 0;
}
h3{
margin-top: 10px;
}
</style>
- To define “title” & “content”, we need to add
title: '', content: ''indata()property. - Input modifier
lazyprohibits data from being displayed until we finish typing data in a input field.
Available to see the full code with syntax highlighting on GitHubGist.
29) Checkbox Binding (YouTube)

When checkbox(es) are checked, value(s) in the checkbox(es) are added to the preview.
[src > components > addBlog.vue]
<template>
<div id='add-blog'>
<h2>Add a New Blog Post</h2>
<form>
<!-- same as above -->
<div id='checkboxes'>
<label>Ninjas</label>
<input type='checkbox' value='ninjas' v-model='blog.categories'/>
<label>Wizards</label>
<input type='checkbox' value='wizards' v-model='blog.categories'/>
<label>Mario</label>
<input type='checkbox' value='mario' v-model='blog.categories'/>
<label>Cheese</label>
<input type='checkbox' value='cheese' v-model='blog.categories'/>
</div>
</form>
<div id='preview'>
<!-- same as above -->
<p>Blog Categories</p>
<ul>
<li v-for='category in blog.categories' :key='category.id'>{{ category }}</li>
</ul>
</div>
</div>
</template>
<script>
export default{
data(){
return{
blog:{
title: '',
content: '',
categories: []
}
}
}
}
</script>
<style scoped>
/* same as above */
#checkboxes input{
display: inline-block;
margin-right: 10px;
}
#checkboxes label{
display: inline-block
}
</style>In 2.2.0+, when using
v-forwith a component, akeyis now required (Official Website).
If the key is not included, the following error message shows up:
[eslint-plugin-vue] [vue/require-v-for-key] Elements in iteration expect to have 'v-bind:key' directives.Available to see the full code with syntax highlighting on GitHubGist.
30) Select Box Binding (YouTube)

When data in a select box is checked, selected data is added to the preview.
[src > components > addBlog.vue]
<template>
<div id='add-blog'>
<h2>Add a New Blog Post</h2>
<form>
<!-- same as above -->
<div id='checkboxes'>
<!-- same as above -->
</div>
<label>Author: </label>
<select v-model='blog.author'>
<option v-for='author in authors'>{{ author }}</option>
</select>
</form>
<div id='preview'>
<!-- same as above -->
<p>Author: {{ blog.author }}</p>
</div>
</div>
</template>
<script>
export default{
data(){
return{
blog:{
title: '',
content: '',
categories: [],
author: ''
},
authors: ['The Net Ninja', 'The Angular Avenger', 'The Vue Vindicator']
}
}
}
</script>
<style scoped>
/* same as above */
</style>Available to see the full code with syntax highlighting on GitHubGist.
Thanks for reading! 💕 If you like this blog post, please clap👏

