Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue Router beforeRouteLeave doesn't stop subcomponents

I have a simple question. I just want to cancel sub-component when route changed. Here is a sample. There is a home component which is the parent. And it has a subcomponent. I just want to stop interval function when route changes in the subcomponent mounted

import Home from "./components/Home.vue";
import Another from "./components/Another.vue";
const routes = [
    { path: '', component: Home },
    { path: '/another', component: Another }
];
const router = new VueRouter({
    routes
});
const app = new Vue({
    router

}).$mount('#app');

And this is home component. Home.vue

<template>
  <sub-component></sub-component>
</template>
<script type="text/babel">
    import SubComponent from "./components/Subcomponent.vue";
    export default {
       components:{
          'sub-component':SubComponent
       }
    }
</script>

And this is subcomponent. Subcomponent.vue

<template>
   <div> Sub component will run a interval </div>
</template>
<script type="text/babel">
    import SubComponent from "./components/Subcomponent.vue";
    export default {
       components:{
          'sub-component':SubComponent
       },
       mounted:function(){
           setInterval(function(){
             console.log("I should cancel when route changed");
           },1000)
       }
    }
</script>

I tried beforeRouteLeave method but it stops only Home.vue methods.

like image 823
Oğuz Can Sertel Avatar asked Feb 04 '17 20:02

Oğuz Can Sertel


People also ask

How do I remove the hashtag from my Vue router?

Setting the history option to the result of the createWebHistory() function from vue-router switches the router from hash history mode to HTML5 history mode. This removes the hash from the URLs. There is no hash character before the index route path.

How do I turn off Vue components?

Vue Button component can be enabled/disabled by giving disabled property. To disable Vue Button component, the disabled property can be set as true .

Should you use history mode for Vue router?

We can enable the Vue Router's history mode to eliminate the hash from our app's URLs. However, we have to redirect our app to index. html since we don't want users to see errors when they go the URLs by entering it or refreshing the page. We can do this with any web server.


1 Answers

As you are using a sub-component (inside the route component), you will not be able to use beforeRouteLeave directly.

Your sub-component is a child component of a route. Therefore you need to trigger the exit method of child component from your route component using Child Component Refs as explained in the guide page below:

https://vuejs.org/v2/guide/components.html#Child-Component-Refs

You can create a reference to your sub-component as follows:

<sub-component ref="mySubComponent"></sub-component>

And now in your route component, you can do the following:

beforeRouteLeave: function(to, from, next) {
    // Indicate to the SubComponent that we are leaving the route
    this.$refs.mySubComponent.prepareToExit();
    // Make sure to always call the next function, otherwise the hook will never be resolved
    // Ref: https://router.vuejs.org/en/advanced/navigation-guards.html
    next();
}

Note: Your route component calls a method in the child sub-component called prepareToExit() in this example, in which you can do the clean-up as follows:

methods: {
    prepareToExit: function() {
        console.log("Preparing to exit sub component, stopping 'twoSecondsTimerEvents'")
        clearInterval(this.twoSecondsTimerEvents)
    }
}

Here is a working example: https://jsfiddle.net/mani04/crwuxez3/ (all the details logged in console)

Please note: This example uses Vue 2.1.10 and Vue-Router 2.2.0 (latest versions as of today). There were some issues in the previous versions, around Navigation Guards functions, which are now fully resolved.

EDIT: Alternate method

After posting the above solution, I realized that there is a simpler way to do it. Your sub-component may not get route specific callbacks like beforeRouteLeave, but it is still a Vue component that follows component lifecycle.

So, based on the component lifecycle diagram, you will have beforeDestroy callback in the sub component that you can use to clear the timer.

Here is how you can do it:

const SubComponent = Vue.component('sub-component', {
    template: `...`,
    data: function() {...},
    mounted: function() {...},
    // 'prepareToExit' method not required here
    // And also there is no need to handle 'beforeRouteLeave' in parent
    beforeDestroy: function() {
        console.log("Stopping the interval timer")
        clearInterval(this.twoSecondsTimerEvents)
    }
});

Advantages:

  1. Much simpler than the earlier approach.
  2. No need to handle any child component refs in parent component.
  3. No need to trigger the cleanup method from parent component.

There are no disadvantages, but this trigger is not exactly tied to the route change, incase you wanted to do any other actions based on destination route. You may also use this if you like this better.

like image 158
Mani Avatar answered Sep 16 '22 18:09

Mani