Mapping multiple URLs to the same component with Vue Router - vue.js

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?

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.

Related

Dynamically registered route is not working when it is just pushed

Hi i'm facing a problem where i want to add new route dynamically
My route structure will look something like this
I'm using "vue-router": "^3.5.3"
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, name:'User'
children: [
{
path: 'profile', component: Profile, name:'Profile'
children: [
{
path: 'about', component: About, name:'About'
children: [
{
path: 'details', component: Details, name: 'Details'
}
]
}
]
}
]
}
]
})
Now my intention is to add child route to Details
this is how my pseudo code looks like
findRouteWithName(routeObj,routeName)
{
// recursive find with any nested level
....
return foundRoute;
}
let routeObj = findRouteWithName(this.$router.options.routes,'Details');
routeObj.children.push(Route object{});
//this.$router.addRoutes([routeObj]) // this line creates problem as mentioned below in **problem:**
Note: if i'm doing like this https://stackoverflow.com/a/48833074/10054910 it is creating multiple nested routes in the url
Problem: page is becoming blank with direct push on children even route does not change in url. with this https://stackoverflow.com/a/48833074/10054910 approach route changes in url but append 2 times
Please help me thanks in advance !!

Use same page for multiple routes

I am trying to find out how to use the same page for multiple routes on a Nuxt.js with i18n module.
Basically I want this route: /product-category/:slug/in/:material to use the same page as /product-category/:slug
So far I have tried below, adding it to nuxt.config.js - but it doesn't work. It simply shows the _slug/_material/index.vue file.
router: {
extendRoutes (routes, resolve) {
routes.push({
path: '/product-category/:slug/in/:material',
component: resolve(__dirname, 'pages/product-category/_slug/index.vue')
})
}
},
Maybe because I am having the i18n module, maybe because I am doing something wrong.
This is my folder structure:
If I inspect my router.js file, I see the path shown twice:
This was my workaround, I just wish there was a simpler method. Plus it still works if you use nuxt i18n.
nuxt.config.js
router: {
extendRoutes (routes, resolve) {
const routesToAdd = [
{ // add your routes here
name: 'product-category-slug-material',
path: '/product-category/:slug/in/:material',
component: resolve(__dirname, 'pages/product-category/_slug/index.vue'), // which component should it resolve to?
chunkName: 'pages/product-category/_slug/_material/index' // this part is important if you want i18n to work
}
];
const existingRoutesToRemove = routesToAdd.map(route => route.name);
const generateRoutes = routes.filter((route) => {
return !existingRoutesToRemove.includes(route.name);
});
routesToAdd.forEach(({ name, path, component, chunkName }) => {
generateRoutes.push({
name,
path,
component,
chunkName
});
});
routes.splice(0, routes.length, ...generateRoutes); // set new array
}
},
You can use _.vue to catch everything.
If you do not know the depth of your URL structure, you can use _.vue to dynamically match nested paths. This will handle requests that do not match a more specific request.
Here you can find out more.

Recursive slugs in vue-router routes

I'm setting up a website which will contain groups, these groups can have sub-groups and these sub-groups can have sub-groups and so on...
Is this in any way possible?
Ofcourse making the path of a route like: /groups/:slug/:slug does not work since it contains duplicate params.
What I was thinking of was using the star pattern as child route, this child route will redirect the user back to the parent route with the slug and a sub-slug, then the next action will be to act on the sub-slug if it exists. But maybe someone has a better solution to this?
const groupRoute = {
path: `/groups/:slug`,
name: 'group',
component: Group,
children: [
{
path: '',
component: GroupsHome
},
{
path: '/subgroups',
component: SubGroups
},
{
path: '*',
beforeEnter(to, from, next) {
next({
name: 'group',
params: {
slug: to.params.slug,
subSlug: to.params.pathMatch
}
})
}
},
]
}
Here is a basic example:
Nested navigation with similar structure for nested objects. The trick here is to use a different param name for inner routes.

Vue ,listen to route param change but not child route change?

I have the following structure in vue.js ,using vue-router.
routes: [
{
path: '/domains',
component: ListOfDomains
},
{
path: '/domain/:domainName',
component: Domain,
children: [{
// Photos Tab content will be rendered inside domain's <router-view>
// when /domain/:id/photos is matched
path: 'posts',
name: 'posts',
component: PostsTabContent
},
{
path: 'photos',
name: 'photos',
component:PhotosTabContent
}
]
}
]
In my Domain component I use a watcher to fetch domain related data from server such as:
"$route": function(to, from) {
console.log("watcher triggered");
//do update only if domain has changed
//do something , ajax request , update store etc
}
This seems to work fine, but my problem is that I only want to do my updates if the url changes from, say, domain/domain23/posts to domain/domain45/posts and not when it changes from domain/domain23/posts to domain/domain23/photos.
How can I watch only that level in the route for changes?

Unable to understand how vue-router query params work

I am building an app with the following route settings.
export default new Router({
routes: [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'Dashboard',
component: Dashboard,
beforeEnter(to, from, next) {
if (!store.getters.isLoggedIn) {
next('/login');
} else {
next();
}
}
},
{
path: '/',
redirect: '/home'
}
]
});
I also set query parameters based on some select boxes I have by using $router.push say as follows.
this.$router.replace({
query: {
scope: this.$store.getters.filterScope
}
});
Problems I have
Reloading the page removes the query params from the URL.
Updating one query parameter removes the other one I had. Example - updating scope using the above method removes the dateRange parameter I already had.
I am using Vuex too so is there any way I can manage these query params using the store?
If you store your parameters in vuex and you don't use vuex-persistedstate, when you refresh the page state will be removed.
So, my suggestion for you:
your-awesome-site.com/dashboard?first=hello&second=hi
In your Dashboard component, you can do like this
mounted () {
console.log(this.$route.params)
// Update vuex state filterScope
}
So you will retrieve your parameters from url and save it in vuex.
Also you can solve your second issue using this.$route.params