Summary of Vue JS2 Tutorial by Net Ninja (Part 2 of 3)

JL’s Self-teaching Story #3

Lim
Lim
Aug 31, 2018 · 13 min read

[JL’s Self-teaching Story] Series

[Net Ninja] JS Regular Expressions
[Net Ninja] Vue JS2 (Part1, Part2(current), Part3)
[Net Ninja] Vuex
[Net Ninja] Python3 (Part1, Part2, Part3)
[Net Ninja] Django (Part1, Part2, Part3)
[Net Ninja] Sass (Part1, Part2)
[Sean Larkin] Webpack4 (Part1, Part2, Part3, Part4)

🌲 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 Binding

16) 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.

  1. Used npm install -g @vue/cli instead of npm install -g vue-cli .
  2. Run vue create <project name> and manually select options by pressing the space bar
  3. vue init webpack-simple <project-name> : install webpack-simple template
  4. To get started, the following needs to be entered in order: cd <project-name>, npm install(install all dependencies needed for webpack-simple), and npm run dev(run a development server).
  5. 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]

  • import statement in main.js file
  • data() instead of data: function() in app.vue file.

18) Nesting Components (YouTube)

[Root Component]
1) Header Component : Links Component, Login Component
2) Article Component
3) Footer Component

There 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`
COPYRIGHT © 2017 Net Ninja

> 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)

COPYRIGHT © 2017 Net Ninja
  • 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

COPYRIGHT © 2017 Net Ninja

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

COPYRIGHT © 2017 Net Ninja

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 above
export 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)

COPYRIGHT © 2017 Net Ninja

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

COPYRIGHT © 2017 Net Ninja
[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.


25) Life-cycle Hooks (YouTube)

COPYRIGHT © 2018 Vue.js
  • Created : good for fetching data
  • Mounted : good for manipulating DOM once it’s been mounted
  • Updated : good for manipulating DOM once it’s been updated

26) Slots (YouTube)

A way to create a form easily based on a template => provide a general outline/structure

COPYRIGHT © 2017 Net Ninja
[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>
  1. is attribute is used to switch between components. (Official Website)
  2. “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)
  3. 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>
  1. To define “title” & “content”, we need to add title: '', content: '' in data() property.
  2. Input modifier lazy prohibits 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-for with a component, a key is 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.


If you’d like to read the previous part of my summary of Vue JS2 Tutorial by Net Ninja, please visit here.

If you’d like to read the next part of my summary of Vue JS2 Tutorial by Net Ninja, please visit here.


Thanks for reading! 💕 If you like this blog post, please clap👏

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade