Fetching Data in Navigation with Vue Router
Tags: webdev, programming, javascript, vue
Published: 2021-01-25 23:20:33

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

Vue Router is a URL router that maps URLs to components.

In this article, we’ll look at how to fetch data while navigating between routes.

Data Fetching
=============

We sometimes have to fetch data from the server when a route is activated. For example, we may want to fetch user data when a route is loaded.

With Vue Router, we can fetch data after navigation or before.

When we fetch data after navigation, we perform the navigation first, then fetch data in the component’s lifecycle hook and display a loading state while data is being fetched.

When we fetch data before navigation, we fetch the data as the route enters a navigation guard, then perform the navigation after data is fetched.

Fetching After Navigation
=========================

We can fetch data after navigation by navigation and render the component first, the fetch the data in the component’s `created` hook.

This gives us the opportunity to display a loading state while the data is being fetched and we can also handle loading differently in each view.

For example, we load data after navigation as follows:

`src/index.js` :

```
const User = {  
  data() {  
    return {  
      user: {}  
    };  
  },  
  created() {  
    this.fetchData();  
  },  
  watch: {  
    $route: "fetchData"  
  },  
  methods: {  
    async fetchData() {  
      this.user = await Promise.resolve({ id: this.$route.params.id });  
    }  
  },  
  template: `      
    <div>user: {{user.id}}</div>      
  `  
};

const routes = [  
  {  
    path: "/:id",  
    component: User  
  }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>
```

In the code above, we have the `User` component that has the `fetchData` to fetch data by resolving a promise and setting the result to `this.user` . This is done each time the `$route` object changes.

So when we go to `/#/1` , we get `user: 1` , and when we go to `/#/2` , we get `user: 2` .

We can add a `loading` state to `fetchData` as follows:

```
const User = {  
  data() {  
    return {  
      user: {},  
      loading: true  
    };  
  },  
  created() {  
    this.fetchData();  
  },  
  watch: {  
    $route: "fetchData"  
  },  
  methods: {  
    async fetchData() {  
      this.loading = true;  
      this.user = await Promise.resolve({ id: this.$route.params.id });  
      this.loading = false;  
    }  
  },  
  template: `    
    <div>    
      <div v-if='!loading'>user: {{user.id}}</div>      
      <div v-if='loading'>Loading</div>  
    </div>  
  `  
};

const routes = [  
  {  
    path: "/:id",  
    component: User  
  }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});
```

Then when the data loads in `fetchData` , we have `this.loading` set to `true` . Otherwise, it’s set to `false` .

Fetching Before Navigation
==========================

we can fetch data before navigating to a new route by using the `beforeRouteEnter` hook.

Then in the `next` hook, we can pass in a callback that calls a method in our component to set the data.

For example, we can write the following code to do that:

`src/index.js` :

```
const User = {  
  data() {  
    return {  
      user: {},  
      loading: true  
    };  
  },  
  async beforeRouteEnter(to, from, next) {  
    const user = await Promise.resolve({ id: to.params.id });  
    next(vm => vm.setUser(user));  
  },  
  async beforeRouteUpdate(to, from, next) {  
    const user = await Promise.resolve({ id: to.params.id });  
    this.setUser(user);  
    next();  
  },  
  methods: {  
    setUser(user) {  
      this.loading = true;  
      this.user = user;  
      this.loading = false;  
    }  
  },  
  template: `    
    <div>    
      <div v-if='!loading'>user: {{user.id}}</div>      
      <div v-if='loading'>Loading</div>  
    </div>  
  `  
};

const routes = [  
  {  
    path: "/:id",  
    component: User  
  }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>
```

In the code above, the `beforeRouteEnter` hook loads the data before the component is fully loaded, so we have to call `next(vm => vm.setUser(user));` to set `data.user` . `vm` is the component instance, so the `setUser` method is a property of `vm` .

When the route parameter updates, `beforeRouteUpdate` will be called. We can get the data and call `this.setUser` directly since the component is already loaded.

Then when we go to `/#/1` , we get `user: 1` , and when we go to `/#/2` , we get `user: 2` .

It’s also a good idea to display a progress bar or some kind of indicator while data is loading.

Conclusion
==========

We can load data before or after route navigation is done. To do it after, we can load the data in the `created` hook and watch the `$route` object for updates.

If we want to load data before route navigation is done, we can load the data in the `beforeRouteEnter` hook and the call `next` with a callback that has the component instance as the parameter to call its methods and set the data.

Then when the parameter or query string updates, we can run code in the `beforeRouteUpdate` hook to load data there.