Use same page for multiple routes - vue.js

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.

Related

Mapping multiple URLs to the same component with Vue Router

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.

How to include all "vue" files from directory into the build without explicitly importing them

is there a way to build all .vue files in my views/ folder without importing them into my vue application directly?
What I want to achieve is a Router with dynamic route definitions which will be filled from a configuration which is a json filled with name, router path and the component name which should be lazy loaded.
My issue right now: The application does only build the files which are imported somewhere in my application. What I need: Build all files in views/ as chunk in order to be able to have a dynamic Router definition.
Thank you for your help :)
It's hard to give proper solution without any code provided, but I'll try
With code like this:
// route definition loaded from json
const routeDef = [
{ name: 'about', path: '/about', componentName: 'about' },
{ name: 'profile', path: '/profile', componentName: 'profile' }
]
// we can create routes like this
const routes = routeDef.map(function(def) {
return {
name: def.name,
path: def.path,
component: () => import('#/views/' + def.componentName + '.vue')
}
})
const router = new VueRouter({
routes: routes
})
By using import('#/views/' + def.componentName + '.vue') you are telling Webpack I want to import one of the .vue files in the views directory but which one will be known at runtime. So Webpack will bundle all file in that directory and make it available for import at runtime....
Note that what you pass into import() is really important - part of the argument must always be a static string so Webpack have at least some clue about the directory (import(def.componentName) wont work) - see the docs
Additional example - loading route definitions from server at runtime
router.js
export default createRouter() {
return axios.get('/getRouteDefinitions').then(function(data) {
const routes = data.routeDef.map(function(def) {
return {
name: def.name,
path: def.path,
component: () => import('#/views/' + def.componentName + '.vue')
}
})
return new VueRouter({
routes: routes
})
})
}
main.js
import VueRouter from `VueRouter`
import createRouter from `router.js`
Vue.use(VueRouter)
createRouter().then(function(router) {
const app = new Vue({
router
})
app.$mount("#app")
})

How to create an optional param in nested routes in Nuxt.js?

in a nuxt.js app, I have nested routes like this:
route-1/route-2/route-3
I want to add an optional param after route-1 to render the same old route but with extra info(item id or something like that), which mean it will map to 2 route formats
route-1/:param/route-2/route-3 or route-1/route-2/route-3
without duplicate my folder structure
if I add a file with the param name it will be a required param and I will have to duplicate the folder structure without this param to handle the 2 scenarios
In my case solved with this. I have this pages tree:
pages/
--| search/
----| index.vue
--| index.vue
I need an optional 'category' param in the search page. I will to extend the routes like this:
// nuxt.config.js
export default {
router: {
middleware: [...],
extendRoutes (routes, resolve) {
routes.push({
name: 'search-category',
path: '/search/:category',
component: resolve(__dirname, 'pages/search/index.vue'),
chunkName: 'pages/search/_category/index'
})
}
}
}
If I navigate to /search or /search/my-category this render the same page in both cases, and in the second case I have the value my-category in $route.params.category :)
In your case, perhaps something like:
// nuxt.config.js
export default {
router: {
middleware: [...],
extendRoutes (routes, resolve) {
routes.push({
name: 'my-new-route',
path: '/route-1/:param/route-2/route-3',
component: resolve(__dirname, 'pages/same-route/index.vue'),
chunkName: 'pages/same-route/_param/index'
})
}
}
}
BTW: chunkName is necessary if you use nuxtI18n plugin, and after, in this plugin, the configuration for the dynamic route is (in my case):
// nuxt.config.js
export default {
i18n: {
pages: {
'search/index': { // <-- route in pages tree
es: '/buscar',
en: '/search'
},
'search/_category/index': { // <-- dynamic route configured
es: '/buscar/:category',
en: '/search/:category'
}
}
}
}

is it possible to specify which component should be used on router.go() in VueJS

In VueJS im trying to setup a scenario where the component used is determined by the url path without having to statically map it.
e.g.
router.beforeEach(({ to, next }) => {
FetchService.fetch(api_base+to.path)
.then((response) => {
router.app.$root.page = response
// I'd like to specify a path and component on the fly
// instead of having to map it
router.go({path: to.path, component: response.pageComponent})
})
.catch((err) => {
router.go({name: '404'})
})
})
Basically, I'd like to be able to create a route on the fly instead of statically specifying the path and component in the router.map
Hope that make sense. Any help would be appreciated.
I think that what you're trying to archive is programmatically load some component based on the current route.
I'm not sure if this is the recommended solution, but is what comes to my mind.
Create a DynamicLoader component whit a component as template
<template>
<component :is="CurrentComponent" />
</template>
Create a watch on $route to load new component in each route change
<script>
export default {
data() {
return {
CurrentComponent: undefined
}
},
watch: {
'$route' (to, from) {
let componentName = to.params.ComponentName;
this.CurrentComponent = require(`components/${componentName}`);
}
},
beforeMount() {
let componentName = this.$route.params.ComponentName;
this.CurrentComponent = require(`components/${componentName}`);
}
}
</script>
Register just this route on your router
{ path: '/:ComponentName', component: DynamicLoader }
In this example I'm assuming that all my componennt will be in components/ folder, in your example seems like you're calling an external service to get the real component location, that should work as well.
Let me know if this help you
As par the documentation of router.go, you either need path you want to redirect to or name of the route you want to redirect to. You don't the component.
Argument of router.go is either path in the form of:
{ path: '...' }
or name of route:
{
name: '...',
// params and query are optional
params: { ... },
query: { ... }
}
so you dont need to return component from your API, you can just return path or name of route, and use it to redirect to relevant page.
You can find more details here to create named routes using vue-router.

Dynamic build routes {or dynamic component import} Angular 2 [duplicate]

This question already has answers here:
Async load routes data and build route instruction for Angular 2
(4 answers)
Closed 6 years ago.
Maybe anyone know how to dynamicly build routes (or just dynamic import Components).
For example:
I have JSON that contains objects with RouteName, path, ComponentNames (string).
I want to iterate it and build dynamicly routes definitions (route config). But I don`t know, how to make dynamic Component import.
I can passs string "ComponentName" from JSON to import rule, because import want static definition (finded it on some soure from googling).
failed
let a = "MyComponentName"
import {a} from ......
(One idea that I came up with - its like create some map key-value, and keep into key - route, value - Component, and after that equals routename from JSON and my MAP and push needed component into final route config array. But its so ugly solution) Maybe another way exists?
I stuck. Many thanks for any help.....
You could leverage Async routes to do this. Based on your route configuration, you could load route from modules. In this case, you need to add the module path to get the components to associate with routes.
Here is a sample:
var routes = {
path: '/path',
name: 'some name',
module: './my.component',
component: 'MyComponentName'
}
routes.forEach((route : any) => {
this.routeConfigArray.push(
new AsyncRoute({
path : route.path,
loader : () => System.import(route.module).then(m => m[route.component]),
name : route.name
});
);
});
this._router.config(this.routeConfigArray);
Another approach could be to add a function to get the name of functions. Based on this you can check if you have a potential component that matches.
Here is a sample:
ngOnInit() {
this.routes = [
{
path: '/test', component: 'OtherComponent', name: 'Test'
}
];
this.configureRoutes(this.routes);
this.router.config( this.routes);
}
configureRoutes(routes) {
var potentialComponents = [ OtherComponent ];
routes.forEach((route) => {
route.component = potentialComponents.find((component) => {
return component.name === route.component;
});
});
}
See this plunkr: https://plnkr.co/edit/KKVagp?p=preview.
See this question for more details:
Dynamic Route Loading in Angular 2 Fails. (Beta)
https://github.com/angular/angular/issues/11437#issuecomment-245995186 provides an RC.6 Plunker
update
In the new router (>= RC.3) https://angular.io/docs/js/latest/api/router/index/Router-class.html#!#resetConfig-anchor resetConfig can be used
router.resetConfig([
{ path: 'team/:id', component: TeamCmp, children: [
{ path: 'simple', component: SimpleCmp },
{ path: 'user/:name', component: UserCmp }
] }
]);
original
What should work is
import from 'myComponents' as myComponents;
...
someFunc(name:string) {
console.debug(myComponents[name]);
}
Routes can be loaded using
constructor(private router:Router) { }
someFunc() {
this.router.config([
{ 'path': '/', 'component': IndexComp },
{ 'path': '/user/:id', 'component': UserComp },
]);
}
I haven't tried this myself.
See also this related question Angular2 App Routing through Services
say. three screens as page1, page2 and page3 and components as app/page1.ts, app/page2.ts and app/page3.ts
let screens : Array<string> = ["Page1","Page2","Page3"];
let aRouter : RouteDefinition;
this.routes = new Array<RouteDefinition>();
screens.map(function(screenId){
aRouter = new AsyncRoute({
path: "/" + screenId,
name: screenId,
loader: () => System.import("app/" + screenId).then(c => c[screenId]) // not import {page1, page2, page3}}
});
this.routes.push(aRouter);
}.bind(this)); //we need to bind to current "this" instead of global this
this.router.config(this.routes);
trick is .bind(this), which is vanella javscript
for whole sulution, check this
Dynamically load Angular 2 Async router
https://github.com/Longfld/DynamicalAsyncRouter