Vue Router: Keep query parameter and use same view for children

15,671

Solution 1

You can resolve this in the global hooks of Router

import VueRouter from 'vue-router';
import routes from './routes';

const Router = new VueRouter({
  mode: 'history',
  routes
});

function hasQueryParams(route) {
  return !!Object.keys(route.query).length
}

Router.beforeEach((to, from, next) => {
   if(!hasQueryParams(to) && hasQueryParams(from)){
    next({name: to.name, query: from.query});
  } else {
    next()
  }
})

If the new route (to) does not have its own parameters, then they will be taken from the previous route (from)

Solution 2

You can add in a mounted hook a router navigation guard beforeEach like this preserveQueryParams:

// helpers.js
import isEmpty from 'lodash/isEmpty';

const preserveQueryParams = (to, from, next) => {
  const usePreviousQueryParams = isEmpty(to.query) && !isEmpty(from.query);
  if (usePreviousQueryParams) {
    next({ ...to, query: from.query });
  } else {
    next();
  }
};
// StartComponent.vue
removeBeforeEachRouteGuard: Function;

mounted() {
  this.removeBeforeEachRouteGuard = this.$router.beforeEach(preserveQueryParams);
}

// don't forget to remove created guard
destroyed() {
  this.removeBeforeEachRouteGuard();
  // resetting query can be useful too
  this.$router.push({ query: {} });
}
Share:
15,671

Related videos on Youtube

offz
Author by

offz

Updated on September 15, 2022

Comments

  • offz
    offz over 1 year

    I'm rewriting an existing Angular 1 application with Vue.

    The application always needs to authenticate an user by locale, id and token before entering any views. Respecting the conventions of our API, I specified the token as a query parameter within my main parent route.

    Coming from the existing Angular's UI router implementation I thought this is the way to go:

    // main.js
    new Vue({
      el: '#app',
      router,
      store,
      template: '<router-view name="main"></router-view>'
    })
    
    // router.js
    const router = new Router({
      mode: 'history',
      routes: [
        {
          name: 'start',
          path : '/:locale/:id', // /:locale/:id?token didn't work
          query: {
            token: null
          },
          beforeEnter (to, from, next) {
            // 1. Get data from API via locale, id and token
            // 2. Update store with user data
          },
          components: {
            main: startComponent
          },
          children: [{
            name: 'profile',
            path: 'profile',
            components: {
              main: profileComponent
            }
          }]
        }
      ]
    })    
    

    When I navigate to the profile view, I expect the view to change and the query token to stay, e.g. /en-US/123?token=abc to /en-US/123/profile?token=abc. Neither happens.

    I'm using Vue 2.3.3 and Vue Router 2.3.1.

    Questions:

    • Can I keep query parameters when navigating to child routes?
    • Am I using the Vue router right here? Or do I need to blame my UI router bias?
  • oliverpool
    oliverpool about 6 years
    Actually using toWithQuery = Object.assign({}, to, {query: from.query});next(toWithQuery); is more robust
  • Kuba
    Kuba almost 6 years
    @oliverpool +1, I had troubles with going back to the root page without this. p.s. this looks like one of ways to handle localization so I'm surprised there isn't so much more to read about this use case.
  • Darky WC
    Darky WC over 3 years
    Thank you very much for this! More note to that is next() must be the last navigation call for Vue Router to know when should it stop going to the next page.
  • Adam Jagosz
    Adam Jagosz over 3 years
    I have optional dynamic routes so my routing chokes on this when there are params. This works though: next({ ...to, query: from.query });
  • Dmitriy
    Dmitriy over 2 years
    What to do if you need to erase query? This solution is bugging.