Paulund

VueJS - Vue Router Reacting To Route Changes

The the last example we learnt how to create dynamic routes in VueJs. This allows you to have the same component be reused on multiple page, for example if we create a Blog post component that needs to search for the blog information from an API it will something like this.

const Blog = {
  template: '...',

  data: {
    blogPost: []
  },

  created: function () {
    this.fetchData()
  },

  methods: {
    fetchData: (function () {            
      return this.$http.get('/api/blogByName/' + this.$route.params.postname).then((response) => {
        this.blogPost = response.body;
      }, (response) => {
        // error callback
      });
    })
  },
}

In Vue this component will be loaded once when the route creates the component. The problem is when you navigate to a new URL the component will still be populated with the old blog post data. We need to tell Vue to react to any route changes and update the data. This is done by using Vue watchers, these will watch for any data changes and perform an event when it happens. In this example we're going to watch the $route object and call the fetch data method to get the information on the blog post URL change.

const Blog = {
  template: '...',

  data: {
    blogPost: []
  },

  created: function () {
    this.fetchData()
  },

  methods: {
    fetchData: (function () {            
      return this.$http.get('/api/blogByName/' + this.$route.params.postname).then((response) => {
        this.blogPost = response.body;
      }, (response) => {
        // error callback
      });
    })
  },

  watch: {
    '$route': 'fetchData'
  }
}

Now whenever the route data changes then this component will run the fetchData method and update the blogPost data property. The problem you will have with this is that the fetchData method will run whenever the route changes even if this component isn't on display or being used on the page. So you'll be making a HTTP request to this API and the component won't even be used, this is obviously a waste of server resources and an unnecessary HTTP request. What you should be using is the beforeEach() on the component, as this will be called whenever the component is loaded by the route.

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // called before the component is confirmed
    
  },
}

The problem you will get now is that this function can not access the this object as it's called before the component is instantiated therefore you'll need to with the next() function with a callback to get access to the component, so let's change the above API call to fetch new data before the component is rendered.

const Blog = {
  template: '...',

  data: {
    blogPost: []
  },

  created: function () {
    this.fetchData()
  },

  methods: {
    fetchData: (function () {            
      return this.$http.get('/api/blogByName/' + this.$route.params.postname).then((response) => {
        this.blogPost = response.body;
      }, (response) => {
        // error callback
      });
    })
  },

  beforeRouteEnter (to, from, next) {
    next(vm => {
      vm.fetchData()
    })
  }
}

Using the next() function with a callback function we're able to access the current component using vm.fetchData()