0

In my Vue 2.7.5 app (using Vue Router 3.5.4). I'm trying to map multiple URLs to the same component. Currently, I have a single route mapped to the component:

{
  path: '/customer/:customerId',
  component: CustomerOrders
}

My goal is to add an optional orderId parameter, such that if a URL like /customer/42/order/59 is accessed, then the same component is loaded, but the order with ID 59 is highlighted (the details of how the param is going to highlight the order are not important).

I tried changing the path to /customer/:customerId/orders/:orderId?, but this would no longer match any URLs of the form /customer/:customerId and would therefore be a breaking change.

My current solution is to use a child route:

{
 path: '/customer/:customerId',
 component: CustomerOrders,
 children: [
   {
     path: 'order/:orderId',
     component: CustomerOrders
   }
  ]
}

This work as the CustomerOrders component is loaded by paths matching either /customer/:customerId or /customer/:customerId/order/:orderId, but it seems like a slightly convoluted approach and I'm not sure it's an appropriate use of child routes. Is there a better solution?

1 Answer 1

2

The easiest way is to register the same component for both routes:

{
  path: '/customer/:customerId',
  name: 'CustomerOrders',
  component: () => import( '../views/CustomerOrders.vue'),
},
{
  path: '/customer/:customerId/order/:orderId',
  name: 'CustomerOrders',
  component: () => import( '../views/CustomerOrders.vue'),
},

An exact solution that you are looking for is parsing params manually:

{
  path: '/customer/:param+',
  name: 'CustomerOrders',
  component: () => import( '../views/CustomerOrders.vue'),
  props: router => {
    const params = router.params;
    const split = params.param.split('/');
    params.customerId = split[0];
    if (split.length > 2) {
      params.orderId = split[2];
    }
  },
},

Here the :params+ ensures that a customerId and the rest of the route get caught. On the other hand, using :params* catches the /customer route without even a customerId.

CAUTION This approach also /customers/42/...everything... The vue3 solution is solved here.

EDIT: the following approach cannot catch orderId

Using an alias improves reusability and reduces rendering time but comes with a price of capturing params-change in a watch handler.

{
  path: '/customer/:customerId',
  name: 'CustomerOrders',
  alias: '/customer/:customerId/order/:orderId',
  component: () => import( '../views/CustomerOrders.vue'),
},

In this case, your component doesn't get rebuilt by changing routes and also onMount or beforeCreate hooks don't get called either. To catch params-change add a proper watch:

export default {
  name: 'CustomerOrders',
  watch: {
    '$route.params'() {
      console.log('params changed. Extract params manually and reload');
    },
  },
};

This issue is addressed here.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your reply. Are you sure it's legal to define 2 routes with the same name?
Sure. Vue Router supports alias that can be a path or an array of paths. github.com/vuejs/vue-router/blob/dev/examples/route-alias/… React Router event support and an array of paths for a specific component. It's a matter of reusability.
It's legal to have the same name multiple times, but when you navigate to { name: duplicatedName } it will always resolve to the first match in the routes tree. I'd avoid it, on general principles.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.