A dog playing with a ball

Fetch data needed on component’s template in Vue before render

Let’s say in your Vue app you have a component that needs to fetch some data in order to display it on the template. This component needs this data on creation in order to be able to bind it on the template.

Let’s take the following example:

<template>
  <div class="container">
    <p>Name: {{name}}</p>
    <p>Age: {{age}}</p>
  </div>
</template>
<script>
data():{
  return{
    name: null
    age: null
  }
}

created():{
  fetchUser().
    then(res => {
     this.name = res.data.name
     this.age = res.data.age 
    }
    )
}
</script>

In this example, we have a Vue Component that needs to fetch the user name and age before being able to create the template and display them. We use the lifecycle hook created() to fetch the data in the component. This will finally work, because of the reactive nature of Vue, but we are going to have an error in the console telling us that name is undefined. This happens because when we navigate to the component and this one gets rendered, the value of the variables is not yet fetched, the component rendering first the template, then executing the script. So we get the error on the console, then the script is run, data is fetched and displayed.

Let’s see how to do that without having the error on the console:

<template>
 <div class="container" v-if="dataFetched">
   <p>Name: {{name}}</p>
   <p>Age: {{age}}</p>
 </div>
</template>
<script>
data: {
  return {
    name: null
    age: null
    dataFetched: false
  }
},

beforeRouteEnter(to, from, next) {
  fetchUser().
    then(res => {
      next(vm => vm.setUser(res.data)
    })
},

methods: {
  setUser(user){
    this.name = user.name
    this.age = user.age
    this.dataFetched = true
  }
}
</script>

To achieve this, we first use the beforeRouteEnter() guard of the Vue Router that is called before the Router navigates to our component route. This guard has three parameters: to, from and next. From is an object containing the information about the route we are navigating from, also the parameters passed by route, to is the information about the route we are navigating to, next() is the function we call after we executed all we need on the guard to pursue the navigation.

But moving the fetch logic from the lifecycle hook to the guard alone won’t solve our problem because fetch is an asynchronous operation that will start on the guard, then next is called, leading to template rendering before data being fetched and virtually the same error on console.

To achieve this, we use a trick. We declare a boolean in the component, let’s say dataFetched, that we instantiate to false and we use it to trigger with v-if the render of the container div only when the data is fetched. We set the variable to true in the setUser method in the component after data is fetched, this way preventing the render of the template and the error on the console.

Happy coding!!!

Leave a Reply

Your email address will not be published. Required fields are marked *