Vue.js Router – Named Routes and Views

Vue Router is pretty easy to use once you get the hang of it.

I’ll do my best to demonstrate how all the pieces fit together.

I hope this simple use case can help you understand the router better.

The Task

Use Vue Router to display a list of users and load their respective posts.

Resources

Getting Started

  • Download / clone / fork the repo, run NPM install. GitHub Repo.
  • Rename env.example.js to env.js
  • Run ‘npm install json-server -g’
  • Run ‘json-server generate.js’ from your terminal of choice.
    • if you get a network error, try a different port by running ‘json-server generate.js –port 5555’ replacing ‘5555’ with any port number you want. Make sure to update your env.js accordingly.
  • Run ‘npm run dev’ from the root directory of the project. The project should open up automatically in your browser.
  • main.js is where the action starts. Open it up and examine the code.

Step 1: Routes – main.js

index.html has our main router-view component. We mount our application to the div#app and then our router directs everything through this main router-view component.

index.html

<div id="app">
    <router-view></router-view>
</div>

main.js

new Vue({
  router
}).$mount('#app')

Layout Component

I decided to create a Layout component to give a basic structure that would be used on any page.

Layout.vue contains two named router-view components – ‘sidebar’ and ‘content.’

Any route now has to wire up a component to one or both of those named router views components.

{
    path: 'user/:id/posts',
    name: 'userPosts',
    components: {
        sidebar: UserList,
        content: Posts
    }
}

Named Views vs Unnamed Views

Documentation

When working with named router-views, your routes object needs to define a ‘components’ (plural) property on each route; whereas, with unnamed router view components, you just wire up a single component on a ‘component’ (singular) property. Use the ‘default’ key for any unnamed views when you have both named and unnamed views on the page.

Named Routes

Documentation

Step 2: Layout.vue

I use Layout.vue to give the rest of the app structure. It is loaded on the root route and all child routes find their way into the two named router views on this component.

If this were a larger app with many different contexts, I could set up this Layout for the User Posts only and use a different Layout for other pages. We’ll tackle that in a part 2.

Step 3: UserList.vue

This component is rather simple. We’ll just grab the list of users from our ‘server’ once the component is created.

To future-proof the setup a little, I’ve delegated the rendering of each User to their own component. See nested lists for an example of when this is necessary.

With simple cases like this, and a logical point of separation is obvious, I feel it’s good practice to just separate components right away. For more complex UI, I would wait until it became clear that I had to divide a component up like this; otherwise, you could waste a lot of time optimizing the UI prematurely.

Step 4: User.vue

Here is where things get interesting.

The User data object comes in via props from the UserList.vue component.

I make use of Vue’s Type Checking. Learn more about props here – Vue Props API – Type Checking, Custom Validation, and Default Values.

This is a great feature, and there’s no reason not to use it.

Two Ways to Change Route:

  1. using router-link
  2. programmatically using this.$router.push

As explained above, for the router-link, I decided to go with a named route:

<router-link :to="{name: 'userPosts', params: {id: user.id}}">Get Posts 1</router-link>

Expessed programmatically, there are two parts: the link / button in the template, and the function to change route. I can also use the syntax for named-route navigation here.

<button @click="viewPosts">Get Posts 2</button>
function viewPosts() {
        this.$router.push({ name: 'userPosts', params: { id: this.user.id } })
}

If I had to perform some operations before changing route, obviously I would use the this.$router.push() syntax.

If all I wanted to do was change a route, I would just use router-link

Route Params

On changing route, I send through the user.id as a route param. This is picked up by the router, sent to the Posts.vue component and used to GET the users posts.

Step 5: Posts.vue

In this component we GET the posts in two ways:

  1. When the component is created.
  2. When the route changes.

In this component we have to ‘watch’ the route change. On a route change, the component isn’t recreated, it is kept alive. For this reason, the created() method isn’t called again and we have to instead watch for a route change to trigger a re-GETing of the posts data.

Each time the route change a new user.id is passed in as a param, and we have it available on this.$route.params object.

Challenges

  1. Setup different top-level routes and use a different Layout for each. Each Layout should have named router-view components that child routes can access.
  2. Using an Alias change the user/:id/posts route to something else. Is it possible to use the user’s name in the route?
  3. Try applying some interesting Transitions to the route change.
  4. Look into Navigation Guards and see if you can just log out a simple message before a route change. See what happens when you neglect to call the next() function. These remind me of Express.js middleware.
  5. When selecting a User from way down on the UserList, we have to scroll up manually to see the posts. Fix this using scroll behavior settings.

More Homework

  1. Get to know the Constructor Options.
  2. Understand the issues with browser history and 404s.