Related
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.
can you give me some feedback about my solution?
I want to prevent vue router to open a site the user sees already.
The problem was: the user had open a site with a double id as paramters like this: path: '/detail/:idType/:itemId After a click on the same tab again the last id /:itemId was removed in the url and the user sees a different view, which I want to prevent.
My current solution is adding a navigation guard:
// router.js
router.beforeEach((to, from, next) => {
if (to.name === from.name) return
else next()
})
Is it okay to return if the names matches?
Do I use the correct router method?
thanks!
Edit for Praveen
// router.js
const router = new VueRouter({
routes: [
{
path: '/new',
name: 'New',
component: () => import('../layout/New'),
props: {
mode: 'create'
}
},
{
path: '/edit/:uuid',
name: 'Edit',
component: () => import('../layout/New'),
props: {
mode: 'edit'
}
},
{
path: '/detail/:idType/:itemId/:uuidId?',
name: 'Detail',
component: () => import('../layout/Detail'),
props: true,
}
],
mode: 'hash',
linkActiveClass: 'active'
})
// tab navigation
<b-link
:to="{ name: ['Edit', 'Detail'].indexOf($route.name) !== -1 ? $route.name : 'New'}"
class="btn btn-site-nav"
type="button"
v-text="'Booking'"
/>
To abort a navigation, call next(false) (in your case: if (to.name === from.name) next(false))
To allow it to pass (to its target), call next with undefined: next() (or next(undefined) - if you want to write more explicit code)
And to redirect it, call next with an object containing either name or path (i.e: next({ name: 'Login' }))
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
}]
I have App.vue file that I mounted function will redirect to maintenance page if maintenance set to true. When home page is landing it is not redirecting but when I do refresh the page it redirect to maintenance page.
mounted() {
const siteIsMaintenanceMode = this.$store.getters.getMaintenancMode;
if (siteIsMaintenanceMode) {
this.$router.push({ path: "/maintenance" });
}
this.preloadRoutes();
},
You can use beforeMounted method and bind data variable in data object as below;
data() {
return {
siteIsMaintenanceMode: this.$store.getters.getMaintenancMode
};
},
beforeMount(){
if (siteIsMaintenanceMode) {
this.$router.push({ path: "/maintenance" });
}
},
mounted() {
this.preloadRoutes()
}
Hope this helps!
Normally, in an app, I would put my partials in a template file.
Something like:
<app>
<nav></nav>
<sidebar></sidebar>
<router-view></router-view>
<footer></footer>
</app>
Depending on the route (login), I want to use a different template.
<app>
<login></login>
</app>
I was thinking I could create two components: say landing-page and Backend.
routes: [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/dashboard',
name: 'content',
component: Backend
}
]
Backend could look like I want it to:
<backend>
<nav></nav>
<sidebar></sidebar>
<router-view></router-view>
<footer></footer>
</backend>
However, how would i specify that then the route is \dashboard, I should render the dashboard component?
File router/index.js
export default new Router({
routes: [
{
path: '/',
component: Page,
children: [
{
path: '',
name: 'Dashboard',
component: Dashboard,
auth: true
},
{
path: 'users',
name: 'Users',
component: Users,
auth: true
}
]
},
{
path: '/login',
name: 'Login',
component: Login
}
]
})
App.vue
<template>
<div class="main-component">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
Create File Login.vue
Complete Login View and When Logged in set cookie in localStorage, then redirect to path /
Create File Page.vue
Complete Dashboard view with Header and Footer and Include a <router-view> tag
In main.js, Use this type of logic to check user is logged in before each transition & if server gives 401 status on api call, then redirecting to login page
router.beforeEach(function (to, from, next) {
console.log('beforeEach', to.path + ' - Auth: ' + auth.user.authenticated)
if ((to.path !== '/login' && to.path !== 'login') && !auth.user.authenticated) {
next({ path: '/login' })
} else if ((to.path === '/login' || to.path === 'login') && auth.user.authenticated) {
next({ path: '/' })
} else {
next()
}
})
// Whenerver Server Gives 401 Status Code, it logouts and redirect to login page
Vue.http.interceptors.push(function (request, next) {
next(function (response) {
if (response.status === 401) {
let msg = response.body.returnMessage
localStorage.setItem('logoutReason', msg)
auth.logout()
}
})
})
auth.user.authenticated is variable to check whether token exists in localstorage or not