Vue-router: Suggestion for guard: beforeRouteUpdate

Created on 25 Nov 2016  路  15Comments  路  Source: vuejs/vue-router

Consider this setup:

routes: [{
  path: 'profile/:profileId',
  component: ProfileComponent,
  beforeEnter (to, from, next) {
    //Fired the first time the profile route is matched
  },
}]

The first time the route is matched, the beforeEnter callback is executed which allows me to implement any guards I want. This is however not the case if I navigate from profile/user1 to profile/user2. This works as intended and makes sense to me because the route isn't "entered" again. However, I see a valid use case for implementing a guard in this scenario and by reading closed issues, the only suggestion given before closing is to create a watcher for $route. This feels wrong for several reasons:

  1. There's no way to create a watcher for per-route beforeEnter configuration in the VueRouter constructor options.
  2. If implemented on the component, the component now knows about application route structure. This might be ok in many scenarios but I don't think it should be forced.

I suggest creating a beforeRouteUpdate callback which works exactly the same way as beforeRouteEnter, but only fires when a route param like :profileId changes.

2.x feature request intend to implement

Most helpful comment

How would one go about watching those changes from outside a component?

All 15 comments

We've had some discussion in the team and there are two possible options, the first is what you proposed, the second is to let beforeEnter fire on params/query/hash change. I guess we'll wait for Evan to make the call, but it's good to start a discussion here.

Hi, I think the global guard beforeEach can help, because it will be triggerred even only params or query changes. And it's pretty simple to use beforeEach especially when you have preFetch hooks in SSR.

I have completed a loading bar at my blog using beforeEach, you can see it by navigating to any other route. This link is how I implemented it using beforeEach. Hope it helps you

By the way, I found that watch is not great when the first time I upgrade to vue2.0, since watch can listen to params and query changes but can't prevent the route changes because it doesn't have next method which all before guards have. I think a beforeRouteUpdate hook makes since to replace beforeEach when your web application is very large

@wolthers @Smallpath Would you think it's rational if beforeEnter/beforeRouteEnter is called on params/query/hash change? This way we avoid adding a new hook.

@fnlctrl Yes, I think it's better than making a new hook

I think a new hook has better semantics and doesn't break backwards compatibility.

I'm currently using the global beforeEach to call custom component hooks on every route change. It simply calls the custom hook on every component that is matched, which is fine since each component can determine if it should do anything special by inspecting the to parameter.

Not sure which option I like better, I think both are fine. A newbeforeRouteUpdate might be confusing though, you'd have to check when exactly it gets called. Only on params/query/hash change? Is it also called on route leave?

Just beforeRouteEnter/beforeRouteLeave would be simpler, but breaks backwards compatibility. And you'd have to check yourself if the route really leaves or just a param change.

Would it be an option to connect this to <router-view>'s key prop? i.e. if the key changes, then call the beforeRouteEnter/beforeRouteLeave as well? I think that'd make sense since you actually want to reload the component if the key changes.

a couple topics that have some overlapping considerations:

I would like to mention #992 as a possible conflict to having beforeRouteEnter fire on location changes.

+1 for using beforeRouteEnter but either way would be much appreciated!

Would this also fix the issue that the transitions are ignored you navigate from profile/user1 to profile/user2 ?

@valentinvieriu see example for transitions on param changes

Thank you @yyx990803 for pointing out this workaround. This is not mentioned in the documentation. ( or at least I didn't find it )
For future reference and help other people that want to achieve the same thing, you can use this approach with <router-view></router-view> too. Here is an example of how somthing like this can be implemented:

<transition name="fade" mode="out-in">
     <router-view></router-view>
</transition>

// then in your ProductView.vue 
<template>
  <div class="product-view" v-if="product" :key="product.productId">
    <spinner :show="!product"></spinner>
.....
  </div>
</template>

Implemented in 7cb3e4e.

How would one go about watching those changes from outside a component?

Also interested how to do it in consistent way outside of the component in router only:

{
    ...Pages.DETAILS,
    component: () => import('@/views/pages/DetailsPage'),
    beforeEnter: async (to, from, next) => {
      const contentId = atob(to.params.contentId)
      console.log('> router -> beforeEnter > detailsPageUrl =', contentId)
      await DetailsTask.load(contentId)
      await DetailsTask.createMenuItems()
      DetailsPageModel.commit((state) => {
        state.isReady = true
        console.log('> router -> beforeEnter > DetailsPageModel: state =', state)
        next()
      })
    }

I navigate to the same page from current page, but with different param

Was this page helpful?
0 / 5 - 0 ratings

Related issues

posva picture posva  路  3Comments

kerlor picture kerlor  路  3Comments

yyx990803 picture yyx990803  路  3Comments

gil0mendes picture gil0mendes  路  3Comments

thomas-alrek picture thomas-alrek  路  3Comments