Part 5: Div​ing into the Vue.js SPA

Chris Berry JavaScript, Node, Single-Page Application, Tutorial, Vue Leave a Comment

Part of the Solid Foundations Learning Series

This is an in-depth learning series focused on a specific application: a JavaScript-based suite of single-page applications optimized for use in a microservice environment. We focus on telling the story of “why” and “how” it was built. 

In Part 4 of this series, we learned the why and how of adding single-page applications to our server-rendered application.

In this blog, we take a small step to the side and talk about our Vue.js app that is added to the reference application. We will specifically focus on how the Vue.js components are added and how the routing is completed within the SPA.

First Thing’s First

Let’s get started by looking at the Handlebars page for the Vue.js application:

{{> header }}
{{> navigation }}


<link rel="stylesheet" href="../app_vue_router/css/style.css" />
{{!-- cloudflare's vue.min is a prodution build CDN link --}}
{{!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script> --}}
{{!-- jsdelivr is using a non minified version and allows Vue Dev tools to work with it  --}}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.5.3/vue-router.min.js"></script>

<div id="app">
 <h2><i class="fa fa-map-signs"></i> VueJS Router Demo</h2>
<hr />
 <p>
   This is an example displaying serveral ways to navigatite and pass
   parameters to different VueJS componets.
 </p>
 <nav class="nav nav-pills">
   <!-- use router-link component for navigation. -->
   <!-- specify the link by passing an object to the `to` prop. -->
   <router-link :to="{ name: 'userList'}" class="nav-item nav-link" active-class="active"><i class="fa fa-users"></i>
     User List Demo</router-link>
   <router-link :to="{ name: 'named'}" class="nav-item nav-link" active-class="active"><i class="fa fa-users"></i>
     Named Views Demo</router-link>
   <a class="nav-link disabled" href="#" tabindex="-1">Access User Component Directly <i class="fa fa-arrow-right"></i></a>
   <router-link :to="{ name: 'user', params: { userId: 852 } }" class="nav-item nav-link" active-class="active"><i
       class="fa fa-user"></i> User 852</router-link>
   <router-link :to="{ name: 'user', params: { userId: 987, userData: { age: 28, name: 'Danny Enzio'} } }"
     class="nav-item nav-link" active-class="active"><i class="fa fa-user"></i> User 987</router-link>
 </nav>
 <hr />
 <section class="mainBody">
   <router-view></router-view>
 </section>
</div>


<script src="../app_vue_router/js/master.js"></script>

{{> footer }}

Vue.js applications are slightly different from our Part 4 Angular.js example in that we can actually have quite a bit of markup in this page with fewer scripts overall. I will go over this page from top to bottom describing a handful of the highlights found here.

If you read through Part 4, you will notice that again we are starting the page off with our Handlebars partial components of header and navigation.

Next, we are actually linking to a stylesheet for the application found in our server-side public folder and to several Content Delivery Network provided scripts: one for Vue.js itself, the Vue.js router script, and a third one called http-vue-loader.

The Vue Loader script was found to be a great addition to the overall development of the application due to the fact it allows the developer to use Vue’s Single File Component model when creating components for the application.

After the calls to the external scripts, we now get into some HTML markup. Right off the bat, we see a div element with an id of app. This is going to be the entry point of the Vue.js application. Lower down, we now have some Vue Router link-specific markup where we’re using router-link to create dynamic hyperlinks in place of HTML hyperlinks.

Notice there are times where we’re passing a simple object for the destination of the link, such as:

:to="{ name: 'userList'}"

And there are other times where we’re passing not only the destination but also a series of parameters along with the link.

:to="{ name: 'user', params: { userId: 987, userData: { age: 28, name: 'Danny Enzio'} } }"

Using the colon in from the word to is a Vue.js shortcut for designating the route. Objects are used inside of the route destination and parameters are named as are additional props for the route.

Inside the section called mainBody, you will see another Vue Router specific element called router-view. This element is the target for all view changes for the router links found above.

Lastly, you will see that there is only one script tag linking back into the Vue.js application. If you noticed from Part 4‘s Angular.js application example, this greatly simplifies the references needed.

See Also:  Part 1: JavaScript Application Introduction

Now let’s take a look at what’s inside the master.js file.

Taking A View of Vue

Inside the folder public/app_vue_router, you will find two subfolders. One for the CSS of the application and one called JS which has our scripts and components files. We’re going to focus on the JS folder.

Here is the master.js file listed out:

"use strict";
const User = httpVueLoader("../app_vue_router/js/user.vue");
const Sidebar = httpVueLoader("../app_vue_router/js/sidebar.vue");
const UserList = httpVueLoader("../app_vue_router/js/user-list.vue");
const NamedWrapper = httpVueLoader("../app_vue_router/js/named.vue");

/* Router and App setup: */
const routes = [
 {
   path: "/users",
   name: "userList",
   component: UserList
 },
 {
   path: "/named",
   name: "named",
   component: NamedWrapper,
   children: [
     {
       path: "user/:userId",
       name: "named_id",
       components: { user_details: User, sidebar: Sidebar },
       props: { user_details: true, sidebar: false }
     }
   ]
 },
 {
   path: "/user/:userId",
   name: "user",
   component: User,
   props: true
 }
];

const router = new VueRouter({
 routes: routes
});

const app = new Vue({
 router: router
}).$mount("#app");

Right off the bat, take note that we’re loading up all of our Vue.js components using the earlier mentioned httpVueLoader module. We have referenced all of our components into this one JavaScript file and have basically done the exact same thing that Vue-Cli’s webpack build would be doing by combining all necessary files into one main file for deployment.

Next, we’re setting up the routes for the application. Routes can be accessed either by a path such as /users or by name such as userList. Both have advantages when used in certain scenarios. When using path-based routes, parameters are defined with a colon and the word used for the parameter.

For example:

path: "user/:userId" 

When optional props are going to be passed to components, we need to tell the routes to watch for them. We do that in this fashion:

props: true

And for child components of a parent, we need to register the components and let Vue.js know which component may have additional props passed in:

components: { user_details: User, sidebar: Sidebar },
props: { user_details: true, sidebar: false }

The last parts of the main.js file are telling Vue.js to use the router and mounting the application to the HTML ID of app found in the Handlebars page.

Single File Components – Vue’s Simplicity At Work

When you use the Vue.js command line tool to generate an application, you are given the ability to work with Single File Components (SFC’s). Using SFC’s during development greatly enhances a developers experience. You are no longer tied to just JavaScript and string templates. You have access to the full range of syntax highlighting, scoped CSS blocks and the ability to have the JavaScript compiled as CommonJS modules. All of which are used in modern JavaScript development.

Next we will list out the four components that are used within this Vue.js application and then talk about some of the highlights of each file.

user-list.vue

<template>
<div class="userList">
     <div class="left">
       <h3>Passing id's to click handlers:</h3>
      <p> <code>Selected user Id: {{selectedUser}} </code></p>
       <p>What is happening here is that you have two click events both passing an 'id' as
         a parameter. Code within this component determines which user to display. There is
         no navigtion happening, but the display is still using the 'user component'.
       <ul>
         <li><span class="userLink" @click="loadUserData(852)">User 852</span></li>
         <li><span class="userLink" @click="loadUserData(987)">User 987</span></li>
       </ul>
     </div>
     <div class="right" v-if="selectedUser.userId">
       <user :user-id="selectedUser.userId" :user-data="selectedUser.userData"></user>
     </div>
   </div>
</template>

<script>
const User = httpVueLoader('./user.vue')
module.exports = {
 data: function data() {
   return {
     selectedUser: {} // selected user data. Place this here to make sure it's reactive.
   };
 },
 methods: {
   loadUserData: function loadUserData(id) {
     this.selectedUser = id === 852 ? { userId: 852 } : { userId: 987, userData: { age: 28, name: 'Danny Enzio' } };
   }
 },

   components: {
     user: User
   }
 }
 </script>

 <style>
 .hello {
   background-color: #ffe;
 }
 </style>

named.vue

<template>
 <div>
   <h3>Passing users into named views:</h3>
   <p>So what happens here is that you have two 'real' hyperlinks which both will pass
     a user to the router. The route that is followed will be prefixed by '/named' and
     will have 'user/:userId' appended to it. In this fashion you have a parent route, and
     child routes all combined into one unit. The display still makes use of the user component.
   </p>
   <ul>
     <li>
       <router-link :to="{ name: 'named_id', params: { userId: 852 } }">User 852</router-link>
     </li>
     <li>
       <router-link :to="{ name: 'named_id', params: { userId: 987, userData: { age: 28, name: 'Danny Enzio'} } }">User 987</router-link>
     </li>
     </ul>
     <div class="userList">
       <router-view class="float-left" name="user_details"></router-view>
       <router-view class="float-right" name="sidebar"></router-view>
     </div>
   </div>
 </template>

 <script>
 module.exports = {
   name: "named",
   methods: {}
 }
 </script>

 <style>
 .hello {
   background-color: #ffe;
 }
 </style>

user.vue

<template>
     <div>
     <h4>User ID: ({{userId}})</h4>
     <div v-if="userData">
       <p>This route makes use of some option data that was passed in:</p>
       <p>{{userData.name}} ({{userData.age}} yrs old)</p>
     </div>
   </div>
</template>

<script>
module.exports = {
 name: "user",
 props: ['userId', 'userData'],
 methods: {}
}
</script>

<style>
.hello {
   background-color: #ffe;
}
</style>

sidebar.vue

<template>
<div class="col-6">
 <h3>Sidebar</h3>
 <p>This side bar only shows when it's accessed from the named view demo.</p>
</div>
</template>

<script>
module.exports = {
 methods: {}
}
</script>

<style>
.hello {
   background-color: #ffe;
}
</style>

Each file follows the standard Vue.js SFC style of creating the template first, having the script second, and the local styles last. Notice that inside the template areas, you have a container and then the rest of the markup. This follows the standard way of creating web components in HTML. Vue.js uses the double curly braces to designate where data binding will occur along with using directives prefixed with v- for special handling of data.

See Also:  JavaScript Bake-Off: Angular, React, and Vue

For example:

<div v-if="userData">

For handling of events within the views, we’ve used Vue.js’ shortcuts of using the at sign for click events. An example of this is:

@click="loadUserData(852)"

Notice that we’re calling a JavaScript function here, and are passing a parameter into it as you normally would when using a function anywhere else.

When a component is expecting props to be passed into it through the markup, we use the convention of using a colon and the name of the prop, along with the assignment of the data being passed in. Here is an example of this:

 
<user :user-id="selectedUser.userId" :user-data="selectedUser.userData"></user> 

As we drop down into the script’s area of the SFC, there are several properties which are defined for the Vue.js component. These can consist of:

  • The name of the component.
  • The methods which the component will have accessible.
  • Any props which the component expects to receive from another component.
  • The data property which contains all of the local data variables; it should be bound to the template.
  • A components property which allows you to register child components for use within the template.
  • Lifecycle hooks for when the component renders through Vue.js.

Here is an example of registering a child component to the script:

const User = httpVueLoader('./user.vue')

module.exports = {
components: {
     user: User
   }

Notice that once again we use the httpVueLoader to load our component from the file system and to bring it into our application.

Wrapping It Up

As you can see, it was easy to mimic using Vue.js’ command line tool by using the httpVueLoader module. Using the module let us have a full experience while developing the application, and it easily allowed us to add modules to a different section of the application.

The routing provided by Vue.js’ routing module was simple, full-featured, and allowed us to develop routes and name routes for deep navigation throughout the application. Overall I was impressed with the experience and will be using Vue.js in future Single-Page Application development scenarios.

Stay tuned for part six!

Series Quick Links

Part 1: JavaScript Application Introduction
Part 2: Navigation Setup with Node + Express
Part 3: Using Node and Markdown + the Package Showdown
Part 4: Adding Smaller SPAs to An Existing Application
Part 5: This Post –> Diving into the Vue.js SPA
Part 6: Node + Express for a Simple Security Model

What Do You Think?