Vue Blog Site - Part 2
Now that we've got the routes figured out for our blog site, we'll start to pull data from a 'mock' web service (we'll use JSON Server as the mock web service).
Add a file named blog-data.json inside the vue-blog-site folder and put this code in it (it should look familiar):
{
"pages":[
{
"id":1,
"url":"css/some-sample-css-page.html",
"title":"Sample CSS Post",
"category":"css",
"description":"Some post about CSS",
"published":"2023-06-21",
"metadata":{"author":"Lee Yuan", "authorId": 3},
"content":"<h3>Something about CSS</h3><p>Here is some content</p>"
},
{
"id":2,
"url":"html/some-sample-html-page.html",
"title":"Sample HTML Post",
"category":"html",
"description":"Some post about HTML",
"published":"2023-06-22",
"metadata":{"author":"Erin Robilla", "authorId": 2},
"content":"<h3>Something about HTML</h3><p>Here is some content</p>"
},
{
"id":3,
"url":"css/some-sample-js-page.html",
"title":"Sample JS Post",
"category":"javascript",
"description":"Some post about JavaScript",
"published":"2023-06-23",
"metadata":{"author":"Sue Green", "authorId": 1},
"content":"<h3>Something about JS</h3><p>Here is some content</p>"
}
],
"authors":[
{"id":1, "firstName":"Sue", "lastName":"Green", "email":"sg@sg.com"},
{"id":2, "firstName":"Erin", "lastName":"Robilla", "email":"er@er.com"},
{"id":3, "firstName":"Lee", "lastName":"Yuan", "email":"ly@ly.com"}
]
}
Check to see if you have installed the JSON Server package by running this command (it checks the version of the package that is installed):
npx json-server -v
If you need to install the JSON Server package run this command (reminder for Ubuntu users, you need to use sudo):
npm install -g json-server
To start the mock server, open a new terminal in the project folder (vue-blog-site), then enter this command:
npx json-server --watch ./blog-data.json
When the json server launches you'll see a URL appear in the terminal that looks something like this:
http://localhost:3000/posts
This is the (mock) web service that we'll use as our data store.
To make it easy to start the JSON server, we can add a script to the package.json file. Find the 'scripts' object in the package.json file and add a property named 'json' (make sure to put a comma after the last key/value pair in the scripts object). Then set the value of the 'json' property to npx json-server --watch ./blog-data.json. When you are done, the scripts object in the package.json file should look something like this:
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"json": "npx json-server --watch ./blog-data.json"
}
Now you can run the json server in the terminal by running this command (make sure you are in the vue-blog-site folder):
npm run json
Install Axios
We'll use the Axios ajax library to make requests to the server, to install it run this command (from the project folder)
npm install axios --save
Create a Blog Data Access Component
Create a file in the src folder named blog-data-access.js and put this code in it:
import axios from 'axios';
const baseURL = "http://localhost:3000/";
export function getAllPosts(){
return axios.get(baseURL + "pages/")
.then(resp => resp.data)
.catch(errorHandler);
}
export function getPostById(id){
return axios.get(baseURL + "pages/" + id)
.then(resp => resp.data)
.catch(errorHandler);
}
function errorHandler(err){
console.log("ERROR (in blog-data-access):", err.message);
throw new Error(err.message);
// here's a bug I had in my code,
// but it might be interesting to leave it there just deal with errors:
//throw new Error(msg);
}
This code should look familiar, because it is the same component that we created in a previous activity. One difference is that now were are using the module in an NPM project, so we import the axios library differently (when we originally wrote this code it was for a project that did not use NPM, and we used a SCRIPT element to link to the Axios library CDN).
Update the BlogList View To Pull Data From the Mock API
Update the code in BlogListView.vue to look like this:
<template>
<div>
<h1>This is the blog list page</h1>
<ul>
<li v-for="p in posts" :key="p.id">{{ p.title }}</li>
</ul>
</div>
</template>
<script>
import {getAllPosts} from '@/blog-data-access'
export default {
data(){
return {
posts:[]
}
},
mounted(){
getAllPosts()
.then(resp => {
this.posts = resp;
})
.catch(err => {
console.log(err)
})
}
}
</script>
There are a lot of important concepts wrapped up into this component. Note the following:
- We imported the getAllPosts() function from the blog-data-access module.
- When the component mounts, we call the getAllPosts() method and use the response to initialize the posts data member.
- The LI element in the template uses v-for to loop through the posts and display the title of each one.
Run the app in the browser (npm run serve), and when you visit the blog page, you should see the title's of 3 blog pages.
Update the BlogPost View to Pull Data from the Mock API
Update the code in BlogPostView.vue to look like this:
<template>
<div v-if="post">
<h1>{{ post.title }}</h1>
<h3>{{ post.published }}</h3>
<h3>{{ post.metadata.author }}</h3>
<div v-html="post.content"></div>
</div>
</template>
<script>
import { getPostById } from '@/blog-data-access';
export default {
props:["postId"],
data(){
return{
post: null
}
},
mounted(){
getPostById(this.postId)
.then(resp => this.post = resp)
.catch(err => console.log(err))
}
}
</script>
Before we discuss this code, run the app in the browser and navigate to this URL:
http://localhost:8080/blog/1
You should see that information for the first blog page is being pulled from the mock api and displayed in the component.
Here are some important points to note in the BlogPost component:
- When the component mounts, it uses the postId prop as a parameter to call getPostById() and assigns the response data to the post data member.
- The template displays information about the post data member.
- The template uses a v-if directive which will only display detail of the post if it is not null (falsy).
- We are using the v-html directive because the content of the post may contain HTML content. This is a potential security problem that we can discuss in class (please ask).
Add Hyper Links to the BlogListView Component
Update the template in BlogListView.vue to look like this:
<template>
<div>
<h1>This is the blog list page</h1>
<ul>
<li v-for="p in posts" :key="p.id">
<router-link :to="{name:'blog-post', params:{postId:p.id}}">
{{ p.title }}
</router-link>
</li>
</ul>
</div>
</template>
Note that the only change to the template is that we have put a router-link element inside the LI element. This router-link directs to the route with the name of blog-post and it passes the current post id (p.id) as the postId param. This will allow the post id to be passed into the BlogPostView as the postId prop.
Run the app in the browser and now you should be able to click the links in the blog list view.