Vue.js nested routing with default child - vue.js

I have an issue with a default children route in Vue.js 2.
When I visit localhost/listings initially, it correctly loads index.vue and map.vue as a child.
When I navigate using router-link to localhost/listings/1, and then using router-link back to localhost/listings, then it still loads the show.vue template. This shouldn't happen?
I have no navigation guards or anything that should interfere. Is there anyway to correct this?
My routes:
window.router = new VueRouter({
routes: [
...
{
path: '/listings',
name: 'listing.index',
component: require('./components/listing/index.vue'),
children: [
{
path: '',
component: require('./components/listing/map.vue')
},
{
path: ':id',
name: 'listing.show',
component: require('./components/listing/show.vue')
}
]
},
...
]
});

The "father" router should not be named if you want a default child route, so instead using :to="{name: 'listing.index'}", use the name of the default child route (e.g :to="{name: 'listing.map'}").
The code should look like this:
window.router = new VueRouter({
routes: [
...
{
path: '/listings',
component: require('./components/listing/index.vue'),
children: [
{
path: '',
name: 'listing.map'
component: require('./components/listing/map.vue')
},
{
path: ':id',
name: 'listing.show',
component: require('./components/listing/show.vue')
}
]
},
...
]
});

Maybe try re-arranging the children, routes are fired in the order they match from top-to-bottom, so this should hopefully fix it:
window.router = new VueRouter({
routes: [
...
{
path: '/listings',
name: 'listing.index',
component: require('./components/listing/index.vue'),
children: [
{
path: ':id',
name: 'listing.show',
component: require('./components/listing/show.vue')
}
{
path: '',
component: require('./components/listing/map.vue')
},
]
},
...
]
});
Just for a bit of clarification, your path: '' essentially serves as a "catch all", which is likely why when it's at the top it's being found immediately and so the router never propagates any further down to the :id route.

In Vue 2.6.11 you can automatically redirect to a child route if parent route is hit:
const routes = [
{
name: 'parent',
path: '/',
component: Parent,
children: [
{
name: 'parent.child',
path: 'child',
component: Child,
}
],
/**
* #type {{name: string} | string} Target component to redirect to
*/
redirect: {
name: 'parent.child'
}
}
];

When you are using named routes and you want to load the component with your child inside, you have to use the name route for the child.
In your Navigation menu links, if you use name route for the parent, the child will not load automatically, even if the child path is nothing.
Let's say for example we have a User route, and we want to show list of users inside the component by default so whenever we go to '/user' path we want to load a list of users as a child in there:
routes: [
{
path: '/user',
name: 'User',
component: User,
children: [
{path: '', name: 'UserList', component: UserList}, // <== child path is = '';
]
}
]
If you think about the code, you might assume if you go to route with name 'User' you might get UserList in there as well, because the path for parent and children both are same. but it's not and you have to choose 'UserList' for the name.
Why this is happening?
Because Vuejs loads the exact component you are referring to, not the url.
you can actually test this, instead of using named route in your links, you can just refer the url, this time vuejs will load the parent and child together with no problem, but when you use named route, it doesn't look at the url and loads the component you are referring to.

Related

Vue Router Optional Parameter on Parent Doesn't Render Child

I have the following setup in my vue router (v4.0.13):
const routes = [
{
path: "/:lang(en|fr)?",
component: Layout, //Top-level component that contains the basic elements (sidebar, header, footer).
children: [
{
path: "",
name: "Home",
component: Home,
},
{
path: "other",
name: "Other",
component: Other,
},
],
},
];
with Layout, Home, Other being my components. The problem is that the following routes don't render the Home component:
/ or /en.
If I remove the ? and make the parameter non optional then /en renders the Home component correctly but the / route doesn't work anymore.
What am I missing?

Can a component know its child-routes in vue-router

<Main>
<router-view></router-view>
</Main>
[
{
path: '/',
component: Intro,
},
{
path: '/path1',
component: Main,
children: [{},{},{}]
},
{
path: '/path2',
component: Main,
children: [{},{},{},{},{}]
}
]
I want Main to know what child routes are defined for it.
I created a function to crawl around the overall routes object and figure it out, but its evaluated differently depending on this.$router.currentRoute which makes it verbose and terrifying and more complicated the more path options there are in the children. Is there a straightforward way for the parent component to know where it is within the routes and have a list of its children?
IE in the main component how to I tell how many children I have and which of them is currently active?
Will this solve your problem? Here is a sample codepen. Everything you need to know and set - route name (even more - you can instead use meta or even path properties). You can even create recursive function which will search for every route including every children array.
Here is a simple function to search for child components knowing parent path name:
function recursiveChildrenSearch(routes, name) {
for (let route of routes) {
if (route.name === name)
return route.children;
else if (route.children.length > 0)
return recursiveChildrenSearch(route.children, name);
}
}
Can be called like so:
recursiveChildrenSearch(router.options.routes, "one")
Called on sample routes object:
routes: [
{
path: '/hello',
name: "main",
component: A,
children: [
{name: 'one', path: '1', component: B,
children: [
{name: 'subone', path: '1.1', component: C}
]},
{name: 'two', path: '2', component: C},
],
},
]
will return:
[Object { name: "subone", path: "1.1", component: {…} }]
You can do so by looping through the nested routes this.$router.options.routes you desire to extract and then push them into an array when the component is mounted()
Note: I'm using Vue Class Component library that's why the code looks like this. But the same logic can be applied to Options API and Components API
routeName = 'randomName'
pathNames = []
mounted(){
const routes = this.$router.options.routes
for (const route of routes) {
if (route.name === routeName) {
for (const children of route.children) {
this.pathNames.push(children.name)
}
}
}
}

Default router: make id persist to all children

I want to have an id to persist through all routes.
e.g. www.website.com/#/:id/routes
I have tried to do the following:
export default new Router({
routes: [
{
path: '/:id/',
props: true,
component: FirstView,
children: [
{
path: '/',
name: 'Second',
component: SecondView
}
]
}
]
});
but i cant get the :id to persist. Any ideas?
You've specified your nested route path as /, which makes the router treat it as the root path instead of prefixing with /:id in the parent route.
Here's a simple demonstration of nesting routes.

vue-router default child route for tabs

I'm trying to have a homepage with tabs containing 2 lists, with 1 open by default.
I have the following route config, I've changed the names to simplify
let routes = [{
path: '/',
name: 'home',
component: require('./views/Home.vue'),
children: [{
path: 'list1',
name: 'home.list1',
component: require('./views/List1.vue')
}, {
path: 'list2',
name: 'home.list2',
component: require('./views/List2.vue')
}]
}
Inside ./views/Home.vue I have a <router-view></router-view> below 2 <router-link>s for each tab (child route).
When I visit the app route http://domain/ I would like to activate the list1 tab. The only way I can currently do this is if I visit http://domain/list1.
I have tried
children: [{
path: '',
name: 'home.list1'
and this initially works well, however if I visit http://domain/list2 both my tab links (router-links) have the active state.
JSFiddle which I can't get to run but helps for context
Is there a better solution to this?
Add one more child route with redirect (should be first)
children: [{
path: '',
redirect: 'list1', // default child path
},
...
]
For making a component(tab) appear default at visiting the parent route, you need to add a path as '' (empty string)
The following is a n example from the Vue Router docs
const router = new VueRouter({
routes: [
{
path: '/user/:id', component: User,
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: UserHome },
// ...other sub routes
]
}
]
})
Don't use a '/', it will be considered as the root route.
You need to put the redirect on the parent, and it works on the first load.
Otherwise, it only works when I reload the page.
put redirect: 'home.list1' on the parent
put your child as path: ''
hope it works.
let routes = [{
path: '/',
name: 'home',
redirect: {name: 'home.list1'}, // Redirect to named route
// redirect '/list2' // Or redirect to path
component: require('./views/Home.vue'),
children: [{
path: '',
name: 'home.list1',
component: require('./views/List1.vue')
}, {
path: 'list2',
name: 'home.list2',
component: require('./views/List2.vue')
}]
}
I think what you want to do works if your home route isn't "/"
routes: [
{ path: '/home',
name: 'home',
component: require('./views/home.vue')
children: [
{ path: '/', name: 'list1', component: list1 },
{ path: 'list2', name: 'list2', component: list2},
],
}
]
This will load the home component and the list1 component inside of your initial . Then you can user router link to nav to list2:
<router-link :to="{ name: 'list2', params: { ...}}">
Or, maybe I don't understand the question.
Here is what works.
You have to use redirect: {name: 'home.list1'} (for the named route) property on your parent route 'home'.
Make sure you use the correct redirect property format either for named route (as above) or for path: redirect: '/list1'.
Here is the correct routes config which is only 1 line (the redirect one) different vs your config:
let routes = [{
path: '/',
name: 'home',
redirect: 'home.list1',
component: require('./views/Home.vue'),
children: [{
path: 'list1',
name: 'home.list1',
component: require('./views/List1.vue')
}, {
path: 'list2',
name: 'home.list2',
component: require('./views/List2.vue')
}]
}
Then every visit to / (your 'home' route) would be redirected to /list1.
Moreover, the router-link-active and router-link-exact-active will be correctly assigned on child link (both of them) and on parent link (only router-link-active).
This will also work for deeper nested non-child and child routes.
For more redirect & aliasig options see the official docs.

Load Vue's parent component and all nested routes lazily

I have an issue with lazy loading of my nested routes!
this is my parent route:
import ChildRoutes from "app/modules/child.route”;
routes: [
{
path: '/child',
component: resolve => require(['app/modules/root'], resolve),
children: ChildRoutes
}
]
and my child.route.js is
import ChildHome from …
import ChildContact from …
export default [
{
path: '',
name: 'childHome',
component: ChildHome
},
{
path: 'about',
name: 'childAbout',
// doesn’t work
component: resolve => require(['./components/about.vue'], resolve)
},
{
path: 'contact',
name: 'childContact',
// this one doesn’t work as well
component: ChildContact
},
...
]
Of course the first sub-rout (childHome) works automatically, but after that I just get blank pages with no component rendered!
If I load neither parent nor children lazily, everything will be fine!
What am I doing wrong?
Worth to mention my project uses vue 2.0, vue-router, vuex with SSR
I'm looking at two apparent problems.
First, it looks like your code diverges from the vue-router docs in calling require() instead of import().
See it here
https://router.vuejs.org/guide/advanced/lazy-loading.html
The improved version of your child.route.js file is
import ChildHome from "";
import ChildContact from "";
export default [
{
path: '',
name: 'childHome',
component: ChildHome
},
{
path: 'about',
name: 'childAbout',
component: () => import("./components/about.vue")
},
{
path: 'contact',
name: 'childContact',
component: ChildContact
},
]
There is a chance that this could resolve whatever lazy loading problems you may have. There is also a chance that it's inconsequential, and if so, read on.
Second issue, there is a bit of a conundrum with the /child route, and vue-router is picky with these kinds of things. Your parent route file has a component for the /child route:
path: '/child',
component: resolve => require(['app/modules/root'], resolve),
Then your child route file also has a component for this route:
path: '',
name: 'childHome',
component: ChildHome
In this case, the child '' route is the same as /child from the children. Vue is very likely confused when two components are loaded for one route. Clear this up and your problems should go away.
Parent route
import ChildRoutes from "app/modules/child.route”;
routes: [
...ChildRoutes,
]
child.route.js
export default [
{
path: '/child',
component: () => import ('#/app/modules/root'), <-- Just verify this path,
children: ...
}
]