vue-router default child route for tabs - vue.js

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.

Related

Vue Router Sidebar navigation item link changes while visiting inner pages

I have a SPA App in Vue JS, I have a side navigation bar which I want to stay visible for all pages. I have following links setup in side navigation bar
{
name: 'Overview',
icon: 'ti-dashboard',
path: 'overview',
},
{
name: 'Areas',
icon: 'ti-map-alt',
path: 'areas',
},
{
name: 'Assignments',
icon: 'ti-check-box',
path: 'assignments',
},
{
name: 'Records',
icon: 'ti-view-list-alt',
id: 'third-party',
children: [
{
name: 'Vaccination',
path: 'vaccination',
},
{
name: 'Out-of-Area Vaccinations',
path: 'vaccination/outer',
},
{
name: 'Surveys',
path: 'survey',
},
{
name: 'Archived',
path: 'archived',
},
],
}
...
Following is my router setup
const routes = [
{
path: '/',
component: App,
},
{
path: '/login',
component: require('../../../assets/js/components/Template/AppLogin.vue'),
},
{
path: '/platform/projects',
component: require('../../../assets/js/components/Template/Projects.vue'),
meta: {requiresAuth: true},
},
{
path: '/project/:projectId',
component: require('../../../assets/js/components/Template/UIComponents/SidebarPlugin/SideBarNew.vue'),
props: route => ({projectId: route.params.projectId}),
children: [
{
path: 'overview',
component: require('../../../assets/js/components/Template/mvdProjectOverview.vue'),
},
{
path: 'areas',
component: require('../../../assets/js/components/Template/AddVaccinationArea.vue'),
},
{
path: 'assignments',
component: require('../../../assets/js/components/Template/AssignAreaUsers.vue'),
},
{
path: 'vaccination',
component: require('../../../assets/js/components/Template/VaccinationRecord.vue'),
},
{
path: 'vaccination/outer',
name: 'projectOuterVaccinations',
component: require('../../../assets/js/components/Template/OuterVaccinations.vue'),
},
{
path: 'archived',
name: 'projectOuterVaccinations',
component: require('../../../assets/js/components/Template/ArchivedRecords.vue'),
},
{
path: 'survey',
component: require('../../../assets/js/components/Template/Surveys.vue'),
},
...
const router = new VueRouter({
routes,
mode: 'history'
})
When I visit vaccination/outer All of my side bar navigation links are appended with vaccination
Attaching images for more clarity
Here the URL is good and should stay like this only
When I navigate to vaccination/outer
The issue: Now all the links gets vaccination in between
I have a very basic knowledge of VUE ROUTER and ROUTER LINK. A help or guidance would be great. Thanks in advance.
I am pretty sure you are using paths from presented array as: <router-link to="LINK">some label</router-link>. However, as your path values are not starting with / - vue router will add value of to property to the current URL instead of replace it.
Let's imagine I am on /a/b/c URL.
When I click on <router-link to="dogs">Dogs</router-link> - I will be redirected to the /a/b/c/dogs.
When I click on <router-link to="/dogs">Dogs</router-link> - I will be redirected to the /dogs.
All you need to do is, start paths with the slash. So instead of path: vaccination/outer use path: /vaccination/outer and it will work as you want to.

Vue 3 nested routes

Given the following route:
{
path: '/detail/:someId',
component: SomeDetailComponent,
name: 'some-detail',
children: [
{
path: 'dashboard',
component: DashboardComponent,
name: 'dashboard'
},
{
path: '',
redirect: { name: 'dashboard' }
},
{
path: 'other',
component: OtherComponent,
name: 'other'
}
]
},
Why does this work (the dashboard component is visible):
this.$router.push(`/detail/123/`);
But this doesn't:
this.$router.push({name: 'some-detail', params: { someId: 123 }});
In one case, the URL gets a trailing slash while in the other it doesn't. I've read somewhere in the docs that this is a breaking change coming from Vue2. See:
named-children-routes-with-an-empty-path-no-longer-appends-a-slash
So the real question here could be: how can I still have my working child navigations (with redirection) while still being able to navigate using the route name, instead of the route url part.

Vue Router issue with dynamic route

I am trying to make these routes:
{
path: '/',
name: 'home',
component: Home
},
{
path: '/:username',
name: 'login',
component: Login
},
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard
}
But when I try to open /dashboard, I get Login page.
The order is important here. You can swap /dashboard and /:username position
[{
path: '/',
name: 'home',
component: Home
},
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard
},
{
path: '/:username',
name: 'login',
component: Login
}]
That's normal. Router is matching the routes from first to last.
/dashboard totally matches /:username with username == 'dashboard'
You should place the login roote at the end of the array, but even better you'd prefix it to be safer (like /user/:username)

Links on the page with optional dynamic route is always without the param in vuejs

I have my routes as define below:
const router = new Router({
base: '/',
mode: 'history',
routes: [
{
path: '/',
redirect: `${DEFAULT_LOCALE}`,
},
{
path: '/:locale?',
component: AppTemplate,
children: [
{
path: 'home',
name: 'home',
component: () => import('#/views/Home.vue'),
meta: {
title: 'Home',
}
}
{
path: 'about',
name: 'about',
component: () => import('#/views/About.vue'),
meta: {
title: 'About',
},
},
{
path: 'contact',
name: 'contact',
component: () => import('#/views/Contact.vue'),
meta: {
title: 'Contact',
},
},
{
path: '*',
redirect: { path: '/' },
},
],
},
{
path: '*',
redirect: { path: '/' },
},
],
});
export default router;
I use :locale for app translation. It is optional because empty locale is for default language. My problem here is even if locale is present, all the links to other pages are without the locale, if that makes sense. For eg, example.com/about is in Italian and example.com/en/about is in English. But which ever URL it is, the links present on the page always points to contact or home page as example.com/contact and example.com/home. Is there a way to fix this? Or is there an easier way to use app translation from URL. I am using vue-i18n for the translation. Thanks
For such configuration I think you have to pass param to make it work:
<router-link :to="{name: 'contact', params: {locale: $route.params.locale} }">
https://jsfiddle.net/3gx4hak5/
Also maybe router-link append attribute will do the trick for you: https://router.vuejs.org/api/#append
Setting append prop always appends the relative path to the current path. For example, assuming we are navigating from /a to a relative link b, without append we will end up at /b, but with append we will end up at /a/b.
<router-link :to="{ path: 'relative/path'}" append></router-link>

Vue.js nested routing with default child

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.