vue-router neither watch route or navigation guards firing - vue.js

Using vue-router in a single page application with the code below, the watch $route function in not firing when redirecting to mycomponent.
Also the beforeRouteUpdate in mycomponent is also not firing.
How can I detect when a variable has been tagged on to a route during component load?
App.vue
<template>
<router-view></router-view>
</template>
<script>
import Vue from 'vue'
export default {
name: 'app'
}
</script>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import MyView from '#/views/MyView'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect: '/home',
name: 'Home',
children: [
{
path: '/mycomponent',
name: 'MyComponent',
component: MyComponentView
},
{
path: '/mycomponent/:id',
component: MyComponentView,
props: true
}
]}]})
mycomponent.vue
<template>
<component :is="activeComponent" :id="id"></component>
</template>
<script>
export default {
name: 'MyComponentView',
components: {
...
},
mounted: function() {
#this logs path in browser
console.log('>>mounted route: ' + this.$route.path)
},
watch: {
'$route': function () {
#this does not fire
console.log('route watcher: ' + this.$route.path)
}
},
beforeRouteUpdate (to, from, next) {
#this does not fire
console.log('>>beforeRouteUpdate')
},
data () {
return {
activeComponent: 'somecomponent'
}
}
}
</script>
component1.vue
...
mounted: function() {
Event.$on('vue-tables.row-click', function(data) {
#this logs correct information in browser
console.log('data.row.id: ' + data.row.id)
router.push({path: 'mycomponent', query: {id: data.row.id}})
})
},
...

It doesn't work because beforeRouteUpdate is in component which is going to reload (Look at Life cycle of Vue). When you change the route, watch & beforeRouteUpdate is terminated and you won't see any results. In this scenario you should provide something like this:
MainRouterView.vue
<template>
<router-view/>
</template>
<script>
name: 'MainRouterView',
beforeRouteUpdate (to, from, next) {
console.log('beforeRouteUpdate')
},
</script>
router.js
export default new Router({
routes: [
{
{
path: '/mycomponent',
name: 'MainRouterView',
component: MainRouterView,
children: [
{
path: '/mycomponent/:id',
component: SecondComponent,
}
]
},
}]})
But if you want to stick up with your structure and check the status of the current route, you can replace beforeRouteUpdate to beforeRouteEnter or beforeRouteLeave in the component. You can use global guard beforeEach in router as well.
To better understand how beforeRouteUpdate works, check out this snippet: http://jsfiddle.net/yraqs4cb/

Related

Vue 3 dynamic components at router level

Dynamic imports is needed for me, eg. i have 10 layouts, but user only visited 3 layouts, I should not import all of the layouts, since its consumed unnecessary resources.
Since its dynamic import, each time i switch between Login & Register path <RouterLink :to"{name: 'Login'}" /> & <RouterLink :to"{name: 'Register'}" />, I got rerender or dynamic import the layout again.
My question is what is the better way to handle it, without rerender or dynamic import the layout again? Or can I save the dynamic import component into the current vue 3 context?
App.vue this is my app with watching the router and switch the layout based on route.meta.layout
<template>
<component :is="layout.component" />
</template>
<script>
import DefaultLayout from "./layout/default.vue";
import {
ref,
shallowRef,
reactive,
shallowReactive,
watch,
defineAsyncComponent,
} from "vue";
import { useRoute } from "vue-router";
export default {
name: "App",
setup(props, context) {
const layout = shallowRef(DefaultLayout);
const route = useRoute();
watch(
() => route.meta,
async (meta) => {
if (meta.layout) {
layout = defineAsyncComponent(() =>
import(`./layout/${meta.layout}.vue`)
);
} else {
layout = DefaultLayout;
}
},
{ immediate: true }
);
return { layout };
},
};
</script>
router/index.js this is my router with layout meta
import { createRouter, createWebHistory } from "vue-router";
import Home from "#/views/Home.vue";
import NotFound from "#/views/NotFound.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/login",
name: "Login",
meta: {
layout: "empty",
},
component: function () {
return import(/* webpackChunkName: "login" */ "../views/Login.vue");
},
},
{
path: "/register",
name: "Register",
meta: {
layout: "empty",
},
component: function () {
return import(/* webpackChunkName: "register" */ "../views/Register.vue");
},
},
{ path: "/:pathMatch(.*)", component: NotFound },
];
const router = createRouter({
history: createWebHistory(import.meta.env.VITE_GITLAB_BASE_PATH),
routes,
scrollBehavior(to, from, savedPosition) {
// always scroll to top
return { top: 0 };
},
});
export default router;
You could use AsyncComponent inside the components option and just use a computed property that returns the current layout, this will load only the current layout without the other ones :
components: {
layout1: defineAsyncComponent(() => import('./components/Layout1.vue')),
layout2: defineAsyncComponent(() => import('./components/Layout2.vue')),
},
Had this issue and Thorsten Lünborg of the Vue core team helped me out.
add the v-if condition and that should resolve it.
<component v-if="layout.name === $route.meta.layout" :is="layout">

Vue Unable to push to another page from component

I am trying to push from one component to another using vue routes but am having issues!
This is my router->index.js:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/pages/HelloWorld'
import GroupStart from '#/pages/GroupStart'
import NotFound from '#/pages/NotFound'
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/groupstart',
name: 'GroupStart',
component: GroupStart
},
{
path: '*',
name: 'Notfound',
component: NotFound
}
],
mode: 'history'
})
Now, from my helloworld component I am trying to do this:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'This is the startpage'
}
}
}
this.$router.push({ path: '/groupstart' })
</script>
When doing so I get this error:
Uncaught TypeError: Cannot read property 'push' of undefined
at eval (HelloWorld.vue?18db:17)
Not sure what I am doing wrong and hoping for help :-)
Thanks in advance.
you need to write this this.$router.push({ path: '/groupstart' }) inside some hook or method. if you want to do it right away when page is loaded, you can do something like this
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'This is the startpage'
}
},
mounted () {
this.$router.push({ path: '/groupstart' })
}
}
</script>

VueRouter this.$route.query always empty

I'm trying to get the query param code, but $route.query is always empty. I've used function mode per the docs. What is missing?
Router:
// use vue-router
import Router from 'vue-router'
Vue.use(Router)
// create router
const router = new Router({
routes: [
{
path: '/',
component: Home,
props: (route) => ({ code: route.query.code })
}
]
})
Home.vue
<template>
<div>
<Navbar />
<FlatHeader />
<v-content>
<ComingSoon />
<Changes />
<EmailSubscribe />
</v-content>
<AuthorizationModal />
</div>
</template>
<script>
import AuthorizationModal from './components/AuthorizationModal';
import FlatHeader from './components/FlatHeader';
import ComingSoon from './components/ComingSoon';
import Changes from './components/Changes';
import EmailSubscribe from './components/EmailSubscribe';
export default {
name: 'Home',
components: {
FlatHeader,
ComingSoon,
Changes,
EmailSubscribe,
AuthorizationModal
},
props: {
code: {
type: String,
default: null
}
},
methods: {
},
mounted() {
console.log(this.$route)
}
}
</script>
$route console output:
I resolved this by setting the mode on Router to 'history'.
Router:
// create router
const router = new Router({
mode: 'history', // add 'history' mode
routes: [
{
path: '/',
component: Home,
props: (route) => ({ code: route.query.code })
}
]
})

Vue.js App Element update data

I am trying to have a template in App.vue which is main component of my app and it contains navigation bar. However I would like to hide this bar when in login page, but I cannot force App.vue to update. Any help please? :)
App.vue - here I would like to have a flag if I should show toolbar and I want to use it in template. The main problem is that currentRoute.path doesn't get updated automatically. I also tried adding router.afterEach, but didn't manage to make it work.
<script>
export default {
name: 'app',
data() {
return {
msg: 'initial',
showToolbar: router.currentRoute.path !== '/login'
}
},
}
</script>
main.js
firebase.auth().onAuthStateChanged(function (user) {
if (!app) {
/* eslint-disable no-new */
app = new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
})
}
});
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '#/components/Home'
import Login from '#/components/Login'
import VueMaterial from 'vue-material'
import 'vue-material/dist/vue-material.css'
Vue.use(VueMaterial);
Vue.use(VueRouter);
let router = new VueRouter({
routes: [
{
path: '*',
redirect: '/'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/',
name: 'Home',
component: Home,
meta: {
requiresAuth: true
}
}
]
});
export default router;
You're currently setting up the initial value of showToolbar, but not setting it up to watch for route changes. For that to work, move showToolbar to the computed section of you App.vue vm:
export default {
name: 'app',
data() {
return {
msg: 'initial',
}
},
computed: {
showToolbar() { return this.$router.currentRoute.path !== '/login' }
}
}
Also, since you are not explicitly importing router in App.vue, you access it in App.vue vm like this.$router, not like router.

Vue lifecycle events triggers on every route

According to the documentation https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram all events from beforeCreate till mounted should be called once. But they are being triggered on every vue-router path navigated
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App },
created: function () {
this.$http.get('/user/get').then(response => {
if (response.data.error) {
console.log(response.data.error)
} else {
User.set(response.data.user)
router.go('/dashboard') // this does force looping
}
}, response => {
router.go('/')
})
}
})
This is App.vue
<template>
<div id="app">
<topmenu></topmenu>
<router-view></router-view>
</div>
</template>
<script>
import Topmenu from '#/components/topmenu'
export default {
name: 'app',
components: {
topmenu: Topmenu
}
}
</script>
The Router.vue
Vue.use(Router)
let route = new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/user/signup',
name: 'Signup',
component: Signup
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
}
]
})
route.beforeEach((to, from, next) => {
if (to.path.match(/^(\/|\/signup)$/)) {
return next()
}
if (User.valid()) {
return next()
}
route.push('/')
})
export default route
How to avoid this events from being triggered on every route switched?