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

🌲 This is the last part of my summary of Vue JS Tutorial by Net Ninja on YouTube.
31) HTTP Requests
32) GET Requests
33) Custom Directives
34) Filters
35) Custom Search Filter
36) Registering Things Locally
37) Mixins
38) Setting Up Routing
39) Routing : Hash vs History
40) Adding Router Links
41) Route Parameters
42) Posting to Firebase
43) Retrieving Posts from FirebaseIf you haven’t set up a development server for Vue, please refer to “16) The Vue CLI” chapter in the previous post for details of running a development server by using Vue CLI3.
If you already did that, just type npm run dev in the terminal for Mac OS and the command line for Windows to run a development server.
31) HTTP Requests (YouTube)
In order to store data in a database, we need to makeHypertext Transfer Protocol (HTTP) requests.

v-if='!submitted'is used to hide the form after “Add Blog” button is pressed.- By using
v-if='submitted', display “Thanks for adding your post!” message. title: '', content: ''allows us to display a title & a content that a user enters.
[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'
import axios from 'axios'Vue.prototype.$http = axios;new Vue({
el: '#app',
render: h => h(App)
});
-----------------------------------[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 v-if='!submitted'>
<label>Blog Title: </label>
<input type='text' v-model.lazy='blog.title' required />
<label>Blog Content: </label>
<textarea v-model.lazy='blog.content'></textarea>
<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>
<label>Author: </label>
<select v-model='blog.author'>
<option v-for='author in authors' :key='author.id'>{{ author }}</option>
</select> <button @click.prevent='post'>Add Blog</button>
</form> <div v-if='submitted'>
<h3>Thanks for adding your post</h3>
</div><div id='preview'>
<p>Blog Title: {{ blog.title }}</p>
<p>Blog Content: </p>
<p>{{ blog.content }}</p>
<p>Blog Categories</p>
<ul>
<li v-for='category in blog.categories' :key='category.id'>{{ category }}</li>
</ul>
<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'],
submitted: false
}
},
methods:{
post: function() {
const axios = require('axios'); axios.post('https://jsonplaceholder.typicode.com/posts', {
title: this.blog.title,
body: this.blog.content,
userID: 1
}).then(response => {
console.log(response);
this.submitted = true;
});
}
}
}
</script>
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.
- Net Ninja used “JSON: Placeholder”, fake online REST API for testing and prototyping.
A RESTful API is an Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. A RESTful API — also referred to as a RESTful web service — is based on REpresentational State Transfer (REST) technology, an architectural style and approach to communications often used in web services development. (Margaret Rouse)
On Nov 3, 2016, Even You, creator of Vue.js, wrote an article called “Retiring vue-resource”. The article says, “As Vue users, many of you may have used vue-resource for handling ajax requests in your Vue applications. For a long time it’s been thought of as the “official” ajax library for Vue, but today we are retiring it from official recommendation status” (blog).
So, I decided to use axios instead of vue-resource. Axios is “Promise based HTTP client for the browser and node.js” (GitHub)
Since Net Ninja used vue-resource in his VueJS2 tutorial, I made a few adjustments.
- Use
import axios from ‘axios’instead ofimport VueResource from 'vue-resource’in main.js file. - Enter
Vue.prototype.$http = axiosinstead ofVue.use(VueResource). - Inside of
post: function(), includeconst axios = require(‘axios'). - Type
axios.postinstead ofthis.$http.post. responseinstead ofdatain thethen() method: will explain the reason in the next chapter.- Use
=>instead offunction().
32) GET Requests (YouTube)
We use GET requests to retrieve data. An example of sample objects in “JSON: Placeholder” is the following:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
[src > app.vue]
<template>
<div>
<show-blogs></show-blogs>
</div>
</template>
<script>
import showBlogs from './components/showBlogs.vue';
export default{
components{
'show-blogs': showBlogs
}
}
</script>
-----------------------------------[src > components > showBlogs.vue]
<template>
<div id='show-blogs'>
<h1>All Blog Articles</h1>
<div v-for='blog in blogs' class='single-blog'>
<h2>{{ blog.title }}</h2>
<article>{{ blog.body }}</article>
</div>
</div>
</template>
<script>
export default{
data(){
blogs: []
},
created(){
const axios = require('axios');
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
console.log(response);
this.blogs = response.data.slice(0,10);
});
}
}
</script>
<style scoped>
#show-blogs{
max-width: 800px;
margin: 0 auto;
}
.single-blog{
padding: 20px;
margin: 20px 0;
box-sizing: border-box;
background: #eee;
}
</style>
Available to see the full code with syntax highlighting on GitHubGist.
As I explained in the previous chapter, I made a few adjustments, because I used axios instead of vue-resource. The reason for the 5th difference(using response instead of data in the then() method) is the following:
[Net Ninja’s code]
.then(function(data) {
console.log(data);
this.blogs = data.body.slice(0,10);[My code]
.then(response => {
console.log(response);
this.blogs = response.data.slice(0,10);> In order to access sample objects provided by JSON Placeholder, I needed to use data instead of body. So, not to be confused with that, I passed down response instead of data.
33) Custom Directives (YouTube)


- Created a custom directive called “
v-rainbow”. It generates a random color for each title. - Created another custom directive called “
v-theme” to provide options : narrow & wide. - Attached an argument called “column” to the
v-themecustom directive to link a background option
[src > components > showBlogs.vue]
<template>
<div v-theme:column="'narrow'" id='show-blogs'>
<h1>All Blog Articles</h1>
<div v-for='blog in blogs' class='single-blog'>
<h2 v-rainbow>{{ blog.title }}</h2>
<article>{{ blog.body }}</article>
</div>
</div>
</template>
<script>
// same as above
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > main.js]
// same as aboveVue.directive('rainbow', {
bind(el, binding, vnode){
el.style.color = '#' + Math.random().toString().slice(2,8);
}
});
Vue.directive('theme', {
bind(el, binding, vnode){
if(binding.value == 'wide'){
el.style.maxWidth = '1200px';
} else if(binding.value == 'narrow'){
el.style.maxWidth = '600px';
}
if(binding.arg == 'column'){
el.style.background = '#ddd';
el.style.padding = '20px';
}
}
});new Vue({
// same as above
});
Available to see the full code with syntax highlighting on GitHubGist.
- In
v-theme:column, used single quotation marks to wrap “narrow” inside a double quotation marks. Without the single quotation marks, the following error message appears in the console: Property or method “narrow” is not defined on the instance but referenced during render. “narrow” is not a property or method. It’s just a value. In order to specify that it’s a string, we need to wrap the word with single quotation marks. - Added
'#'beforeMath.random()function, because a hexadecimal color starts with “#” (e.g., #00fefe). - “The
Math.random()function returns a floating-point, pseudo-random number in the range 0–1 (inclusive of 0, but not 1) with approximately uniform distribution over that range — which you can then scale to your desired range” (MDN). The output ofconsole.log(Math.random())is 0.XXXXX… Therefore, the first number inslice()function is “2”.
34) Filters (YouTube)
“Vue.js allows you to define filters that can be used to apply common text formatting (official website)”.

- Made titles uppercase
- Cut bodies down to show the maximum of 100 characters.
[src > components > showBlogs.vue]
<template>
<div id='show-blogs'>
<h1>All Blog Articles</h1>
<div v-for='blog in blogs' class='single-blog'>
<h2>{{ blog.title | to-uppercase }}</h2>
<article>{{ blog.body | snippet }}</article>
</div>
</div>
</template><script>
// same as above
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > main.js]
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.prototype.$http = axios;Vue.filter('to-uppercase', function(value){
return value.toUpperCase()
});
Vue.filter('snippet', function(value){
return value.slice(0,100) + '...'
});
new Vue({
el: '#app',
render: h => h(App)
});
Available to see the full code with syntax highlighting on GitHubGist.
| <name of a filter>in showBlogs.vue : appended to the end of a JavaScript expression, denoted by the “pipe” symbol.Vue.filter('<name of a filter>', function(){})in main.js : define a filter globally before creating aVueinstance.
35) Custom Search Filter (YouTube)

When a user type word(s) on a search bar, only blogs with the word(s) remains on the screen.
[src > components > showBlogs.vue]
<template>
<div id='show-blogs'>
<h1>All Blog Articles</h1>
<div v-for='blog in filteredBlogs' class='single-blog'>
<h2>{{ blog.title | to-uppercase }}</h2>
<article>{{ blog.body | snippet }}</article>
</div>
</div>
</template><script>
export default{
data(){
blogs: [],
search: ''
},
created(){
// same as above
},
computed:{
filteredBlogs: function(){
return this.blogs.filter((blog) => {
return blog.title.match(this.search) || blog.body.match(this.search);
});
}
}
}
</script>
<style scoped>
/* same as above */
</style>
Available to see the full code with syntax highlighting on GitHubGist.
A || Bmeans “A” or “B”. In this case, “filteredBlogs” filter searches all blog posts, check each blog post to see if the blog post contains word(s) typed in the search bar in its title or body, and returns only matched blog post(s) on the screen.
36) Registering Things Locally (YouTube)
In the previous chapters, we used “to-uppercase” & “snippet” filters registered globally.
[src > main.js]
Vue.filter('to-uppercase', function(value){
return value.toUpperCase()
});
Vue.filter('snippet', function(value){
return value.slice(0,100) + '...'
});In this chapter, we’ll register the filters locally in showBlogs.vue. Also, we’ll add v-rainbow directive locally.
[src > components > showBlogs.vue]
<template>
<div id='show-blogs'>
<h1>All Blog Articles</h1>
<div v-for='blog in filteredBlogs' class='single-blog'>
<h2 v-rainbow>{{ blog.title | to-uppercase }}</h2>
<article>{{ blog.body | snippet }}</article>
</div>
</div>
</template><script>
export default{
data(){
blogs: [],
search: ''
},
created(){
// same as above
},
computed:{
// same as above
},
filters:{
'to-uppercase': function(value){
return value.toUpperCase();
},
snippet(value){
return value.slice(0,100) + '...';
}
},
directives:{
'rainbow':{
bind(el, binding, vnode){
el.style.color = '#' + Math.random().toString().slice(2,8);
}
}
}
}
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > main.js]
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.prototype.$http = axios;
new Vue({
el: '#app',
render: h => h(App)
});
Available to see the full code with syntax highlighting on GitHubGist.
> 'to-uppercase': function(value) can be written as toUppercase(value).
37) Mixins (YouTube)
“Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options (official website)”.

[src > App.vue]
<template>
<div>
<show-blogs></show-blogs>
<list-blogs></list-blogs>
</div>
</template>
<script>
import showBlogs from './components/showBlogs.vue';
import listBlogs from './components/listBlogs.vue';
export default{
components{
'show-blogs': showBlogs,
'list-blogs': listBlogs
}
}
</script>
-----------------------------------[src > components > showBlogs.vue]
<template>
<!-- same as above -->
</template><script>
import searchMixin from '../mixins/searchMixin';export default{
data(){
blogs: [],
search: ''
},
created(){
// same as above
},
filters:{
// same as above
},
directives:{
// same as above
}
}
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > components > listBlogs.vue]
<template>
<!-- same as above -->
</template><script>
import searchMixin from '../mixins/searchMixin';export default{
data(){
blogs: [],
search: ''
},
created(){
// same as showBlogs.vue
},
filters:{
// same as showBlogs.vue
},
directives:{
// same as showBlogs.vue
}
}
</script>
<style scoped>
/* same as above */
</style>
-----------------------------------[src > mixins > searchMixin.js]
export default{
computed:{
filteredBlogs: function(){
return this.blogs.filter((blog) => {
return blog.title.match(this.search) || blog.body.match(this.search);
});
}
}
}
Available to see the full code with syntax highlighting on GitHubGist.
38) Setting Up Routing (YouTube)
“Vue Router is the official router for Vue.js. It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze (official website)”.
Install vue-router with Node Package Manager(NPM) : npm install vue-router --save.
[src > main.js]
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
import VueRouter from 'vue-router'
import Routes from './routes'
Vue.prototype.$http = axios;
Vue.use(VueRouter);
const router = new VueRouter({
routes : Routes
});
new Vue({
el: '#app',
render: h => h(App),
router: router
});
-----------------------------------[src > routes.js]
import showBlogs from './components/showBlogs.vue';
import addBlog from './components/addBlogs.vue';
export default[
{ path: '/', component: showBlogs },
{ path: '/add', component: addBlog }
]
-----------------------------------[src > App.vue]
<template>
<div>
<router-view></router-view>
</div>
</template>
-----------------------------------[src > components > showBlogs.vue]
<template>
<!-- same as above -->
</template><script>
export default{
data(){
blogs: [],
search: ''
},
created(){
// same as above
},
computed:{
filteredBlogs: function(){
return this.blogs.filter((blog) => {
return blog.title.match(this.search) || blog.body.match(this.search);
});
}
},
filters:{
// same as above
},
directives:{
// same as above
}
}
</script>
<style scoped>
/* same as above */
</style>
Available to see the full code with syntax highlighting on GitHubGist.
- “The
<router-view>component is a functional component that renders the matched component for the given path. Components rendered in<router-view>can also contain its own<router-view>, which will render components for nested paths (official website)”. - “The default mode for
vue-routeris hash mode - it uses the URL hash to simulate a full URL so that the page won't be reloaded when the URL changes. (official website)”. - Links can be accessed with “#” (e.g., “localhost:8080/#/” & “localhost:8080/#/add”).
39) Routing : Hash vs History (YouTube)
In the hash mode, a request to the server is not made. The hash mode just takes users to a different part of the website by using hash.
To get rid of the hash, we can use the router’s history mode.
[src > main.js]
// same as above
const router = new VueRouter({
routes : Routes,
mode: 'history'
});
new Vue({
// same as above
});Available to see the full code with syntax highlighting on GitHubGist.
> Now, “localhost:8080/” & “localhost:8080/add” work! (No more need to include a hash tag in URL)
40) Adding Router Links (YouTube)
Let’s add a header at the top with a navigation bar with two links that switch the view between showBlogs.js and addBlog.js.

[src > App.vue]
<template>
<div>
<add-header></add-header>
<router-view></router-view>
</div>
</template>
<script>
import header from './components/header.vue'
export default{
components{
'add-header': header
}
}
</script>
-----------------------------------[src > components > header.vue]
<template>
<nav>
<ul>
<li><router-link to='/' exact>Blog</router-link></li>
<li><router-link to='/add' exact>Add a New Blog</router-link></li>
</ul>
</nav>
</template><style scoped>
ul{
list-style-type: none;
text-align: center;
margin: 0;
}
li{
display: inline-block;
margin: 0 10px;
}
a{
color: #fff;
text-decoration: none;
padding: 6px 8px;
border-radius: 10px;
}
nav{
background: #444;
padding: 14px 0;
margin-bottom: 40px;
}
.router-link-active{
background: #eee;
color: #444;
}
</style>
Available to see the full code with syntax highlighting on GitHubGist.
- Instead of
<a href='/'>,router-linkis used.router-linkintercepts the click event. So, we don’t need to re-load the page, and that’s quicker. - A
router-linkhas a class ofrouter-link-active. Stylingrouter-link-activeallows us to differentiate between an inactiverouter-linkand an activerouter-link. - When
exactproperty is not included in therouter-linktag,<router-link to='/'>has a style ofrouter-link-activeeven after<router-link to='/add'>becomes active and<router-link to='/'>becomes inactive. The reason is that'/add'contains'/'. ***Exactproperty makes arouter-linkactive only when the URL exactly matches the link included in therouter-linktag.
41) Route Parameters (YouTube)
“Very often we will need to map routes with the given pattern to the same component. For example, we may have a User component which should be rendered for all users but with different user IDs. In vue-router, we can use a dynamic segment in the path to achieve that.
A dynamic segment is denoted by a colon :. When a route is matched, the value of the dynamic segments will be exposed as this.$route.params in every component. Therefore, we can render the current user ID by updating User's template to this: (official website)”
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/user/:id', component: User }
]
})
In other words, we can detect route parameters and handle them in a component by making a HTTP request for correct resource.
[src > routes.js]
import showBlogs from './components/showBlogs.vue';
import addBlog from './components/addBlogs.vue';
import singleBlog from './components/singleBlog.vue';export default[
{ path: '/', component: showBlogs },
{ path: '/add', component: addBlog },
{ path: '/blog/:id', component: singleBlog }
]
-----------------------------------[src > components > singleBlog.vue]
<template>
<div id='single-blog'>
<h1>{{ blog.title }}</h1>
<article>{{ blog.body }}</article>
</div>
</template><script>
export default{
data(){
return{
id: this.$route.params.id,
blog: {}
}
},
created(){
const axios = require('axios'); axios.get('https://jsonplaceholder.typicode.com/posts/' + this.id
).then(response => {
console.log(response);
this.blog = response.data;
});
}
}
</script><style scoped>
#single-blog{
max-width: 960px;
margin: 0 auto;
color: gray;
}
</style>
There are a few differences between my code and Net Ninja’s code, since I used axios and Net Ninja used vue-resource.
- Use
import axios from ‘axios’instead ofimport VueResource from 'vue-resource’in main.js file. - Enter
Vue.prototype.$http = axiosinstead ofVue.use(VueResource). - Inside of
post: function(), includeconst axios = require(‘axios'). - Type
axios.postinstead ofthis.$http.post. - Use
=>instead offunction().
[Net Ninja’s code]
.then(function(data) {
console.log(data);
this.blog = data.body;[My code]
.then(response => {
console.log(response);
this.blog = response.data;> In order to access sample objects provided by JSON Placeholder, I needed to use data instead of body. So, not to be confused with that, I passed down response instead of data.
/blog/<a number> displays a different object provided by JSON Placeholder. (e.g., localhost:8080/blog/30)
But, users will not type URL like that to access a webpage. Users will read blog articles and click one of them to access specific blog article.
So, replaced <h2 v-rainbow>{{ blog.title | to-uppercase }}</h2> in showBlogs.vue with the following:
<router-link v-bind:to="'/blog' + blog.id"><h2 v-rainbow>{{ blog.title | to-uppercase }}</h2></router-link>Available to see the full code with syntax highlighting on GitHubGist.
42) Posting to Firebase (YouTube)
So far, we’ve used fake online REST API provided by JSON Placeholder. We can add our own content, store it in a database, and retrieve it by using a tool called “Firebase”. Firebase lets us store data in a NoSQL(Structured Query Language) database, which means we’re storing data as JavaScript objects.
After you create a project, you will be directed to the project page. On the project page, click the followings in order: “database” icon on the left, “Realtime Database” in the middle > “Rules” tab.
If the default setting doesn’t look like the following, change the setting like the following:
{
"rules": {
".read": true,
".write": true
}
}> ".read" stands for whether data is allowed to be read by users or not.
> ".write" stands for whether data is allowed to be written or not.
*** After you put your app into production, you’d want to set up the security for your app. Please visit here to learn more about firebase security rules.
Under the “data” tab, you can see the link of Firebase database for your project. we’d use it instead of the JSON Placeholder link : https://jsonplaceholder.typicode.com/posts/.
[src > components > addBlog.vue]
<template>
<!-- same as above -->
</template><script>
export default{
data(){
// same as above
},
methods:{
post: function(){
const axios = require('axios'); axios.post('https://<name of your project>.firebaseio.com/posts.json', this.blog
).then(response => {
console.log(response);
this.submitted = true;
});
}
}
}
</script><style scoped>
/* same as above */
</style>
Available to see code with syntax highlighting on GitHubGist.
> There are a few differences between my code and Net Ninja’s code, since I used axios and Net Ninja used vue-resource. If you like to see the details of that again, please check out “31) HTTP Requests” chapter at the beginning of this blog post.
Before moving on, add a few blog posts under the “Add a New Blog” tab to retrieve data from Firebase database for the next chapter’s exercise.

43) Retrieving Posts from Firebase (YouTube)
We still see sample data from JSON Placeholder. In order to see blog posts we added to firebase, we need to take some steps.
[STEP 1] : Check data we get from Firebase on console (in showBlogs.vue)
created():{
post: function(){
const axios = require('axios'); axios.get('https://<name of your project>.firebaseio.com/posts.json')
.then(response => {
console.log('1. response', response);
})
}
}

[STEP 2] : Add id property (in showBlogs.vue)
In Chapter 41(Adding Router Links), we added a link to each individual blog post : <router-link v-bind:to=”’/blog’ + blog.id”>.
The id property existed in the sample data. But, it doesn’t exist in our data from Firebase. However, we have a unique key for each object which refers to a blog post.
[STEP 2–1] : Return promise objects (in showBlogs.vue)
created():{
post: function(){
const axios = require('axios'); axios.get('https://<name of your project>.firebaseio.com/posts.json')
.then(response => {
return response.data;
});
}
}
> “The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value” (MDN). Because we’re dealing with promise objects, the objects are asynchronous and we need to use .then method.
[Net Ninja’s code]
.then(function(data) {
return data.json();[My code]
.then(response => {
return response.data;Since Net Ninja used vue-resource, he used .json() to convert data into JSON data. But, I used axios. So I didn’t need to do that.
> In axios, we no longer need to convert data into JSON data. One of features of axios is “automatic transforms for JSON data” (GitHub).
[STEP 2–2] : Fire a call-back function (in showBlogs.vue)
created():{
post: function(){
const axios = require('axios'); axios.get('https://<name of your project>.firebaseio.com/posts.json')
.then(response => {
console.log('2. response.data', response.data);
return response.data;
}).then(response => {
console.log('3. response / callback function', response);
});
}
}
We can now use .then method, which fires a call-back function after response.data is executed.

[STEP 2–3] : Cycle through objects (in showBlogs.vue)
created():{
post: function(){
const axios = require('axios'); axios.get('https://<name of your project>.firebaseio.com/posts.json')
.then(response => {
return response.data;
}).then(response => {
let blogsArray = [];
for(let key in response){
console.log('4. key', key);
console.log('5. response[key]', response[key]);
}
});
}
}

[STEP 2–4] : Push the objects into an empty array (in showBlogs.vue)
- Add a property called “
id” to each object, which would be equal to thekey. - Push all objects to the empty array we created :
blogsArray.
created(){
axios.get('https://<name of your project>.firebaseio.com/posts.json')
.then(response => {
return response.data;
}).then(response => {
let blogsArray = [];
for(let key in response){
response[key].id = key;
blogsArray.push(response[key]);
}
});
}
}[STEP 2–5] : Display the objects on the screen (in showBlogs.vue)
- After pushing all objects into the
blogsArray, assign the array tothis.blogsarray. (This.blogsarray is what’s shown on the screen.) - There’d be still an error, because we’ve used
blog.bodyto retrieve contents in sample data from JSON Placeholder. To retrieve contents in our data fromFirebase, we need to changeblog.bodytoblog.contentin searchMixins.js, showBlogs.vue, and singleBlog.vue.
data(){
return { blogs: [], search: '' }
},
created(){
axios.get('https://<name of your project>.firebaseio.com/posts.json')
.then(response => {
return response.data;
}).then(response => {
let blogsArray = [];
for(let key in response){
response[key].id = key;
blogsArray.push(response[key]);
}
console.log('6. this.blogs', this.blogs);
console.log('7. blogsArray', blogsArray);
this.blogs = blogsArray;
});
}
}

[STEP 3] : Display a single blog post in a new page when clicked (in singleBlog.vue)
data(){
return {
id: this.$route.params.id,
blog: {}
}
},
created(){
axios.get('https://<name of your project>.firebaseio.com/posts/' + this.id + '.json')
.then(response => {
return response.data;
}).then(response => {
this.blog = response;
});
}
}Since we created id by using key in Step 2, we can use that to open a single blog post in a new page when clicked.

Available to see the full code with syntax highlighting on GitHubGist.
Thanks for reading! 💕 If you like this blog post, please clap👏
