Vue Router sub-routes not working as expected - vue.js

When trying to get sub-routes to work using vue-router, my sub-routes are rendering the parent route component and not the declared component for that sub-route. It seems that a component must be declared for a parent route, or no component will be displayed at all. For instance, if I declare my routes like this:
Router.map({
'/login': {
component: Login
},
'/stations': {
subRoutes: {
'/': {
component: Station
},
'/create': {
component: CreateStation
}
}
},
});
Nothing is displayed on any route. But if I declare my routes like this:
Router.map({
'/login': {
component: Login
},
'/stations': {
component: Station,
subRoutes: {
'/create': {
component: CreateStation
}
}
},
});
My stations/create route displays the same component as the stations route. What gives?

You still need to declare the root component for the /stations route, like this:
'/stations': {
component: Station,
subRoutes: {
'/': {
component: ListStations
},
'/create': {
component: CreateStation
}
}
}
According to the documentation:
router.map({
'/foo': {
component: Foo,
// add a subRoutes map under /foo
subRoutes: {
'/bar': {
// Bar will be rendered inside Foo's <router-view>
// when /foo/bar is matched
component: Bar
},
'/baz': {
// Same for Baz, but only when /foo/baz is matched
component: Baz
}
}
}
})
Now, with the above configuration, when you visit /foo, nothing will
be rendered inside Foo's outlet, because no sub route is matched.
Update:
When you create subroutes, you are telling the parent component (in this case Station), that it will need to host some components inside its template. Station and CreateStation don't sit side by side, they have a parent-child relationship (in terms of routes).
That's why the component Station needs to have a router-view element in its template, and both ListStations and CreateStation will render inside it.
Something like this: http://jsfiddle.net/naeg67da/329/

Related

Is there a way to set a route prop in vue?

I want to navigate to a specific tab in a page, with
this.$router.push({
name: "AdminNotifications",
params: { tab: "requests" },
})
so inside the page i can get the param and set the tab:
mounted() {
const routeTab = this.$route.params.tab;
if (routeTab) this.tab = routeTab;
}
It works if the current page is not AdminNotifications.
But else, there is an error:
NavigationDuplicated: Avoided redundant navigation to current
So... is there a way to just set the tab props, without navigate?
thanks
You can't navigate to a route if you're already there. But, since you're already there, you can just set this.tab to the desired value:
if (this.$route.name === 'AdminNotifications') {
this.tab = 'requests';
} else {
this.$router.push({
name: "AdminNotifications",
params: { tab: "requests" },
})
}
If the component in charge of navigating is not the same as the one containing tab, you could push the tab param to the $route:
if (this.$route.name === 'AdminNotifications') {
this.$router.replace({
params: { tab: "requests" }
});
} else {
this.$router.push({
name: "AdminNotifications",
params: { tab: "requests" },
})
}
And in the page component, replace the "watcher" in mounted with a proper watch, which sets tab to any truthy value of $route.params.tab, dynamically:
watch: {
'$route.params.tab': {
handler(val) {
if (val) {
this.tab = val;
}
},
immediate: true
}
}
If i understood your question correctly you can just do this.$route.params.tab = "any value" like any other variable. this.$route.params.tab is just a variable like all the others.
Here is how I was handling this with the vue-router.
Add one parent component which will contain tabs and a tab content components.
Your structure can looke like this:
tabs/Tab1.vue
tabs/Tab2.vue
tabs/Tab2.vue
Tab.vue
In Tabs.vue paste code below. The component should contain in the place where you want to display the content of your tabs and router-links to link a specific tab.
Tab.vue
<template>
<div class="tabs">
<router-link to="/tab1">Tab 1</router-link>
<router-link to="/tab2">Tab 2</router-link>
<router-link to="/tab3">Tab 3</router-link>
<router-view />
</div>
</template>
<script>
export default {
name: "tabs",
components: {},
};
</script>
Then fill tabs content components.
In your router.js register your tab routes as shown below.
import Tabs from "./Tabs";
import Tab1 from "./tabs/Tab1";
import Tab2 from "./tabs/Tab2";
import Tab3 from "./tabs/Tab3";
{
path: "/",
redirect: "/tab1",
component: Tabs,
children: [
{
path: "/tab1",
name: "tab1",
component: Tab1
},
{
path: "/tab2",
name: "tab2",
component: Tab2
},
{
path: "/tab3",
name: "tab3",
component: Tab3
}
]
}
Now you should be able to navigate a specific tab by router link.
In the file where you define the routes, you need to define the props for each route, something like this:
const routes = [
{
path: "admin-notifications",
name: "AdminNotifications",
component: AdminNotificationsView,
props: r => ({
tab: r.params.tab
})
}
]
And then define the prop tab in AdminNotificationsView assuming that's the component you use to render the view.

this.$route.params is empty in app.vue when building site global navigation (outside of router-view)

I have vue setup and working fine, I can route to pages, and they are shown correctly in the router-view component. I can access this.$route.params.xyz in the components within the page, however, when trying to access in a component, such as the global navigation, the params collection is empty.
The current route URL is localhost:5011/forum/2/details where the 2 is an id value. I can access the 2 happily on the page for some local routing, but I wanted a settings page, to be available on the global menu.
{
title: 'Forum Settings ',
icon: 'mdi-cogs',
text: 'Forum Settings ' + this.$route.params.id,
route: {
name: 'ForumSettings',
params: {
id: this.$route.params.id
},
},
},
However, params is {} and id is undefined.
How can I make this work?
route.js:
{
path: '/forum/:id/settings',
name: 'ForumSettings',
component: ForumSettings,
meta: {
authorize: true,
},
},
on the page itself as a test:
<dr-btn
text="Settings"
:to="{ name: 'ForumSettings', params: {id: this.$route.params.id}}"
>
<v-icon>mdi-cog</v-icon>
</dr-btn>
This works fine.
in the app.vue:
mounted() {
console.info('Mounted Router', this.$route);
},
This is not the current URL, so it seems the router isn't setup at this point. How can it be achieved to get the forum id for the current route (if it is on another page, the settings link will be hidden, so if actually no id, then no menu item)
You could use a vuex store. Have the page that needs settings pass the id param to a vuex variable, have the navigation use a computed property that reads that vuex variable. If you have a lot more than this to do you might also consider a library that synchronizes vue router with vuex.
I had the same issue. I solved it by moving my logic to navigation guards (https://next.router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards)
For example:
const routes = [
{
path: '/',
name: 'Home',
beforeEnter:async() => {
let loggedIn = await store.checkIfLoggedIn()
if (loggedIn) router.push({name:'Dashboard'})
return true
},
component: () => import('../views/Home.vue')
},
{
path: '/dashboard',
name: 'Dashboard',
beforeEnter:async() => {
let loggedIn = await store.checkIfLoggedIn()
if (!loggedIn) {
router.push({name:'Home'})
}
return true
}]

Vue js - How to route requests with query string

I have a router-view object made in this way
export default new Router({
linkExactActiveClass: 'active', // active class for *exact* links.
mode: 'history',
routes: [
{
path: '/admin',
name: 'dashboard',
component: Dashboard
},
{
path: '/company',
name: 'company',
component: ObjectList,
props: {
url: '/web/company',
idColumn: 'companyId'
}
}
]
})
when i hit /company link the router correctly sends me to the ObjectList component
In this page I have a simple pagination component made in this way
<template>
<div class="overflow-auto">
<b-pagination-nav
align="center"
:link-gen="linkGen"
:number-of-pages="totalPages"
use-router
></b-pagination-nav>
</div>
</template>
<script>
export default {
data() {
return {
};
},
name: "pagination",
props: {
current: Number,
url: String,
totalPages: Number
},
methods: {
linkGen(pageNum) {
return pageNum === 1 ? "?" : `?page=${pageNum}`;
}
}
};
</script>
Now, the second page, for instance, correctly has this url:
/company?page=2
and if i click on it, i go to the correct url. the problem is how to tell the router-link that on pressing the second page button another call to the backend must be done?
I thought the use-router option should catch this link and make the request as when i click the /company link in my standard navigation bar, but obviously not..
You can receive query props using this.$route.query
You should define computed prop to receive:
computed: {
page () {
return this.$route.query.page || 1
}
}
Now you can watch this property and on every change call the backend:
watch: {
page () {
// call backend
}
}
Also consider calling backend on created event hook once computed property page is set (depends on your needs):
created () {
// call backend by passing this.page
}
Feel free to ask more if something unclear or if I misunderstood you.
You can also handle every url query param change with this piece of code:
watch: {
'$route.query': {
immediate: true,
handler(newVal) {
console.log(newVal)
// make actions with newVal.page
}
}
}
Just want to add an improvement related to #Ignas Damunskis answer that is already great.
If you want to listen to changes on created and when the specific property changes (in this case the query parameter)
You don't need the created method, you can do it all in one on the watch method, like below:
watch: {
page: {
handler: function (val, oldVal) { /* ... */ },
immediate: true
}
}
Hope it helps :)
I wasn't able to get this working with vue-router however
When parent component is created() {...}
check for if (this.$route.query.order} exists
then set this.viewOrder to true, and load the child component
and pass props to child component
you can do this also by vue route's dynamic-matching: https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
created () {
// call backend
},
watch: {
$route(to, from) {
// call backend with (to.params.xxxx)
}
}

Vuejs Display a router name in Component

How can I display a router name in a component ?
Example:
const routes = [
{ path: '/documents', component: Documents, name:"Documents" ,props:true},
{ path: '/queries', component: Queries, name:"Queries", props:true}
]
I want to display the name property as a title in the component. Is this possible? how?
props:true will convert path parameters to properties:
{ path: '/documents/:name', component: Documents, name:"Documents", props:true},
You can use an object instead of true and then send in a string.
{ path: '/documents', component: Documents, name:"Documents", props:{ name:'Documents'}},
In your component, register the property
props: { name:String }
And then use it in a template like this:
<div>{{name}}</div>
You can also refer to the route name using the components $route object
<div>{{$route.name}}</div>
To specify title to a component you can use router's meta property, Link
const routes = [
{
path: '/documents',
component: Documents,
name:"Documents" ,
props:true,
meta: {
title: 'Documents'
}
},
{
path: '/queries',
component: Queries,
name:"Queries",
props:true,
meta: {
title: 'Queries'
}
}
]
In main.js,
import router from '#/routes'
router.beforeEach((to, from, next) => {
document.title = `Currently at - ${to.meta.title}`
next()
})

Passing props to Vue.js components instantiated by Vue-router

Suppose I have a Vue.js component like this:
var Bar = Vue.extend({
props: ['my-props'],
template: '<p>This is bar!</p>'
});
And I want to use it when some route in vue-router is matched like this:
router.map({
'/bar': {
component: Bar
}
});
Normally in order to pass 'myProps' to the component I would do something like this:
Vue.component('my-bar', Bar);
and in the html:
<my-bar my-props="hello!"></my-bar>
In this case, the router is drawing automatically the component in the router-view element when the route is matched.
My question is, in this case, how can I pass the the props to the component?
<router-view :some-value-to-pass="localValue"></router-view>
and in your components just add prop:
props: {
someValueToPass: String
},
vue-router will match prop in component
sadly non of the prev solutions actually answers the question so here is a one from quora
basically the part that docs doesn't explain well is
When props is set to true, the route.params will be set as the component props.
so what you actually need when sending the prop through the route is to assign it to the params key ex
this.$router.push({
name: 'Home',
params: {
theme: 'dark'
}
})
so the full example would be
// component
const User = {
props: ['test'],
template: '<div>User {{ test }}</div>'
}
// router
new VueRouter({
routes: [
{
path: '/user',
component: User,
name: 'user',
props: true
}
]
})
// usage
this.$router.push({
name: 'user',
params: {
test: 'hello there' // or anything you want
}
})
In the router,
const router = new VueRouter({
routes: [
{ path: 'YOUR__PATH', component: Bar, props: { authorName: 'Robert' } }
]
})
And inside the <Bar /> component,
var Bar = Vue.extend({
props: ['authorName'],
template: '<p>Hey, {{ authorName }}</p>'
});
This question is old, so I'm not sure if Function mode existed at the time the question was asked, but it can be used to pass only the correct props. It is only called on route changes, but all the Vue reactivity rules apply with whatever you pass if it is reactive data already.
// Router config:
components: {
default: Component0,
named1: Component1
},
props: {
default: (route) => {
// <router-view :prop1="$store.importantCollection"/>
return {
prop1: store.importantCollection
}
},
named1: function(route) {
// <router-view :anotherProp="$store.otherData"/>
return {
anotherProp: store.otherData
}
},
}
Note that this only works if your prop function is scoped so it can see the data you want to pass. The route argument provides no references to the Vue instance, Vuex, or VueRouter. Also, the named1 example demonstrates that this is not bound to any instance either. This appears to be by design, so the state is only defined by the URL. Because of these issues, it could be better to use named views that receive the correct props in the markup and let the router toggle them.
// Router config:
components:
{
default: Component0,
named1: Component1
}
<!-- Markup -->
<router-view name="default" :prop1="$store.importantCollection"/>
<router-view name="named1" :anotherProp="$store.otherData"/>
With this approach, your markup declares the intent of which views are possible and sets them up, but the router decides which ones to activate.
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
// for routes with named views, you have to define the props option for each named view:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
Object mode
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
]
})
That is the official answer.
link
Use:
this.$route.MY_PROP
to get a route prop