Vue router, Error: Redirected * to * via a navigation guard - vue-router

Access /dashboard/profile get error
vue-router.esm.js:1958 Uncaught (in promise) Error: Redirected when going from “/home” to “/dashboard” via a navigation guard.
// router.ts
const routes: Array<RouteConfig> = [
{path: '/', redirect: {name: 'Home'}},
{path: '/home', name: 'Home', component: Home},
{
path: '/dashboard', component: () => import('../views/Dashboard.vue'),
meta: {isLogin: true},
children: [
{path: '', component: () => import('../views/Dash/Certificate/index.vue'), meta: {isViewer: true},
{path: 'profile', component: () => import('../views/Dash/ProfileComponent.vue')},
]
},
];
//guard
router.beforeEach((to, from, next) => {
const profilePath = "/dashboard/profile";
if (!to.matched.some(res => res.meta.isLogin)) {
return next();
}
// isNotLogin
if (!localStorage['currentUser'])
return next({path: "/", query: {redirect: to.fullPath}});
// isLogin
if (to.matched.some(res => res.meta?.isViewer)) {
viewService.isCertificateViewer ? next() : next({path: profilePath})
} else next()
});

I spent four hours trying to solve this same problem. This is the article I found that explained where the error was coming from: vue-router — Uncaught (in promise) Error: Redirected from "/login" to "/" via a navigation guard
Imagine you have routes a, b, and c. The user is on route a. They then click a link that is supposed to send them to route b. We then catch that in a route guard and redirect them to route c. This makes sense to the developer because there was a reason to redirect to route c, but the people in charge have decided that is a jarring experience to the user, and we shouldn't call route redirects in ways that don't make sense to the user.
tl;dr the problem is with how you initially direct to a route. Instead of router.push('/b') do router.push('/b').catch((e) => e); Just suppress the error and everything will work great.

Related

Vue Router - catch all wildcard not working

I'm using Vue Router with Vue 3 and am trying to add a catch-all route to redirect the user if they try and access an invalid URL. When I try and use the wildcard (*), i get the following error logged to the console:
Uncaught Error: A non-empty path must start with "/"
at tokenizePath (vue-router.esm.js?8c4f:975)
at createRouteRecordMatcher (vue-router.esm.js?8c4f:1106)
at addRoute (vue-router.esm.js?8c4f:1190)
at eval (vue-router.esm.js?8c4f:1335)
at Array.forEach (<anonymous>)
at createRouterMatcher (vue-router.esm.js?8c4f:1335)
at createRouter (vue-router.esm.js?8c4f:2064)
at eval (index.js?a18c:26)
at Module../src/router/index.js (app.js:1402)
at __webpack_require__ (app.js:854)
I'm assuming this is because I don't prepend the path containing the asterisk with a '/', but if I do this then the catch all doesn't work. Here are my routes:
imports...
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/user',
name: 'User',
// route level code-splitting
// this generates a separate chunk (user.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "user" */ '../views/user/User.vue'),
children: [{path: '', component: UserStart}, {path: ':id', component: UserDetail}, {path: ':id/edit', component: UserEdit, name: 'userEdit'}]
},
{path: '/redirect-me', redirect: '/user'},
{path: '*', redirect: '/'}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
The wildcard route is the last object in the routes array. Does anyone know what I'm doing wrong?
Catch all routes (/*) must now be defined using a parameter with a custom regex: /:catchAll(.*)
For example:
{
// path: "*",
path: "/:catchAll(.*)",
name: "NotFound",
component: PageNotFound,
meta: {
requiresAuth: false
}
}
Personally, for Vue 2's * (star or catch all) routes in Vue 3 I use:
{
path: '/:pathMatch(.*)*', <== THIS
name: 'not-found',
component: NotFound
}
Catch all routes (*, /*) must now be defined using a parameter with a custom regex:
The parameter name can be whatever you want like catchAll, pathMatch, noPage etc
{
path: '/:pathMatch(.*)*', //will match everything and put it under `$route.params.pathMatch`
name: 'not-found',
component: NotFound
}
{
path: '/user-:afterUser(.*)',// will match anything starting with `/user-` and put it under `$route.params.afterUser`
component: UserGeneric
}
/:pathMatch(.*)*
The last * it is necessary if you plan on directly navigating to the not-found route using its name.
If you omit it the / character in params, it will be encoded when resolving or pushing.
For example if you use path: /:pathMatch(.*) (note: without the last asterisk) and you go to /user/not-found (a page that doesn't exists) the this.$route.params.pathMatch will be a string => 'user/not-found'
// bad example if using named routes:
router.resolve({
name: 'bad-not-found',
params: { pathMatch: 'not/found' },
}).href // '/not%2Ffound'
Instead, if you use path: /:pathMatch(.*)* (note: with asterisk) this.$route.params.pathMatch will be an array ['user', 'not-found']
// good example:
router.resolve({
name: 'not-found',
params: { pathMatch: ['not', 'found'] },
}).href // '/not/found'
Please read docs: From migration from vue 2 to vue 3 and Catch all / 404 Not found Route

Angular lazy loaded module with child component default route

Angular 9. Child component is not loaded with lazy loaded module component. Here is my code.
app-routing.module.ts
{
path: '',
redirectTo: '/auth',
pathMatch: 'full'
},
{
path: 'auth',
loadChildren: () => import('./pages/auth/auth.module').then(m => m.AuthModule),
}
auth.module.ts
{
path: '',
component: AuthRootComponent,
children: [
{ path: 'login', component: LoginComponent},
{ path: 'forgot', component: ForgotPasswordComponent},
{ path: '', redirectTo: 'login', pathMatch: 'full' },
]
}
When i use localhost:4200/ it redirects me to localhose:4200/auth. It does not load login component.
But when i hit url in browser (localhost:4200/auth) it will load login component, and new url will be desired url which is localhost:4200/auth/login.
Please tell me, why it does not load login from child array when auth module is loaded and the url is localhost:4200?
URL should be localhost:4200/auth/login but right now getting url localhost:4200/auth
I think you'll have to change from redirectTo: '/auth' to redirectTo: 'auth'. That is because when you're using absolute redirects(e.g /auth), only one redirect operation will be allowed, in this case the one which goes to /auth, meaning that any other redirects(e.g redirectTo: 'login') will be ignored.
Here's where it checks if there were any absolute redirects:
if (allowRedirects && this.allowRedirects) {
return this.expandSegmentAgainstRouteUsingRedirect(
ngModule, segmentGroup, routes, route, paths, outlet);
}
And here is where it states that after an absolute redirect, there can't be any other redirects:
if (e instanceof AbsoluteRedirect) {
// after an absolute redirect we do not apply any more redirects!
this.allowRedirects = false;
// we need to run matching, so we can fetch all lazy-loaded modules
return this.match(e.urlTree);
}
Absolute vs Relative Redirects
Excerpt from my GH repo.
The difference between redirectTo: 'foo/bar' and redirectTo: '/foo/bar' is that:
/ - will start again from the root(the first outermost array of routes)
`` - will start the search from the first route from the array this current route resides in

TypeError: e is not a function

Good day.
When running my app i get the following error related to my vue-router:
TypeError: e is not a function
however i have nothing ... i simply named "e" in my code.
I have a few before each options but nothing too big besides a few cookies deletions.
I have a few imports of apps and i am trying to use them in my router and they all work. So does the cookies. I did the beforeEach() method a few times to see if the error was there but so far no luck.
i got no idea of what is going on.
EDIT: When trying to figure this i was comenting on my code to see if i could find the error and when i removed most of the beforeEach() section i left on the next() function and a new error showed sayin "t is not a function", so i guess for some reason java script is only recognizing the last letters of my funcions, like t in next() and e in some().
EDIT2: After removing unecessary code that i copied from another project apperently the error happens in my next() function.
here's my router code:
import Vue from "vue";
import Router from "vue-router";
import Dashboard from "#/views/Dashboard.vue";
import Opcoes from "#/views/settings.vue";
import LoginForm from "#/components/component/loginForm.vue";
import store from "./store.js";
import Cookies from "js-cookie";
Vue.use(Router);
let router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "loginForm",
component: LoginForm,
meta: {
guest: true
}
},
{
path: "/dashboard",
name: "Dashboard",
component: Dashboard,
meta: {
requiresAuth: true
}
},
{
path: "/opcoes",
name: "Opcoes",
component: Opcoes,
meta: {
requiresAuth: true
}
},
{
path: "/sair",
name: "Sair",
component: LoginForm,
meta: {
requiresAuth: true
}
}
]
});
router.beforeEach((to, from, next) => {
const expirationDate = Cookies.get("expireIn");
const now = new Date().getTime();
if (now >= expirationDate) {
Cookies.remove("expirationDate");
Cookies.remove("token");
Cookies.remove("userId");
store.dispatch('LOGOUT');
} else {
next();
}
});
export default router;
Heres a print of my stack trace:
My Big Fat Guess
The error message points to line 2203 (line 1921 is part of a generic error handler). This line is in the Router push() method
onComplete && onComplete(route);
My guess is, somewhere in your code not shown in your question (perhaps in your LoginForm component), you are calling this.$router.push() with a second argument that is not a function, eg
this.$router.push({ name: 'Dashboard' }, somethingNotAFunction)
Other problems (from previous versions of the question)
Your / route (loginForm) has no params so this...
next({
path: "/",
params: { nextUrl: to.fullPath }
});
is invalid. Did you perhaps mean to send nextUrl as a query parameter. If so, use
next({ name: 'loginForm', query: { nextUrl: to.fullPath } })
You also have
next({ path: "login" });
and
next({ name: "login" });
neither of which exist in your routes. You should only forward the route request to existing routes.
Finally, if you're using Vuex, you should not be directly assigning state values. In strict-mode, this
store.state.isLoggedIn = false
will trigger an error. You should be committing state mutations instead.

Unable to understand how vue-router query params work

I am building an app with the following route settings.
export default new Router({
routes: [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'Dashboard',
component: Dashboard,
beforeEnter(to, from, next) {
if (!store.getters.isLoggedIn) {
next('/login');
} else {
next();
}
}
},
{
path: '/',
redirect: '/home'
}
]
});
I also set query parameters based on some select boxes I have by using $router.push say as follows.
this.$router.replace({
query: {
scope: this.$store.getters.filterScope
}
});
Problems I have
Reloading the page removes the query params from the URL.
Updating one query parameter removes the other one I had. Example - updating scope using the above method removes the dateRange parameter I already had.
I am using Vuex too so is there any way I can manage these query params using the store?
If you store your parameters in vuex and you don't use vuex-persistedstate, when you refresh the page state will be removed.
So, my suggestion for you:
your-awesome-site.com/dashboard?first=hello&second=hi
In your Dashboard component, you can do like this
mounted () {
console.log(this.$route.params)
// Update vuex state filterScope
}
So you will retrieve your parameters from url and save it in vuex.
Also you can solve your second issue using this.$route.params

How to Load Certain Route on Electron App Activate Event

I'm having a really hard time to figure out how to load a particular route when the activate event is fired. I'm creating an Electron application using the Electron-Vue framework and I've these following routes:
export default [
{
path: '/',
name: 'login-page',
component: require('components/LoginPageView')
},
{
path: '/tracker',
name: 'tracker-page',
component: require('components/TrackerPageView')
},
{
path: '*',
redirect: '/'
}
]
Now, I'd like to load the /tracker route once the app.on('activate') is fired on the following:
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
The main reason behind this is I'm creating a two window Electron application. The first window would be the login and the second window would be user profiles. When the user already logged in and closed the app using the system close button, the app stays in the Dock bar and when the app is clicked, the Electron activate event is fired and the login screen shows again. Since the user is already logged in, I don't want the user to show the login window again. Any suggestion would be must appreciated.
I was finally able to achieve this by using the Vue-Router Per-Route Guard beforeEnter method. Here's my draft:
let auth = true
export default [
{
path: '/',
name: 'login-page',
component: require('components/LoginPageView'),
meta: {
auth: false
},
beforeEnter: (to, from, next) => {
if (auth) {
next('/tracker')
} else {
next()
}
}
},
{
path: '/tracker',
name: 'tracker-page',
component: require('components/TrackerPageView'),
meta: {
auth: true
}
},
{
path: '*',
redirect: '/'
}
]
Any feedbacks are most welcome to improve this even better :)