Infinite loop for vue-router's beforeEach method - vue.js

I want to replace one of the params of to if to's name is a specific one and I have no idea why this doesn't work.
export const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior() {
return { x: 0, y: 0 };
},
routes: [
{
name: 'Details Page',
path: 'details/:name/:id',
component: () => import('#/views/Details.vue'),
meta: {
encode: true,
},
},
// other routes... (none of them has encode = true)
]
});
function needsEncode(route) {
return route.meta && route.meta.encode;
}
router.beforeEach(async (to, from, next) => {
if (to.name === 'Details Page' && needsEncode(to)) {
const toEncoded = Object.assign({}, to, {
meta: {
encode: false,
},
params: {
name: encodeURIComponent(to.params.name),
id: to.params.id,
},
});
return next(toEncoded);
}
return next();
}
What am I doing wrong?

Please don't store state in the meta property. Instead you can check if the name is already encoded:
function needsEncode(route) {
return route.meta && route.meta.encode && ( // <-- changes start here
decodeURIComponent(to.params.name) === to.params.name
) // <-- changes ends here
}
router.beforeEach(async (to, from, next) => {
if (to.name === 'Details Page' && needsEncode(to)) {
const toEncoded = Object.assign({}, to, {
// remove meta
//meta: {
// encode: false,
//},
params: {
name: encodeURIComponent(to.params.name),
id: to.params.id,
},
});
return next(toEncoded);
}
return next();
}
If decodeURIComponent(to.params.name) is equal to the name, then it's not encoded yet and you can proceed to encode it.

Related

Vue 3, SocketIO - won't join the room after login and router push

I am experiencing the following issue - once the user is logged in, and onMounted event is finished, SocketIO client side should join the room. But this doesn't happen for some reason. I have to manually refresh browser in order to join the room. What am I doing wrong here?
I have the following code for the SocketIO on the client side:
import { io } from "socket.io-client";
const token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
console.log(token);
const ioSocket = io('https://dolphin-app-e4ozn.ondigitalocean.app', {
withCredentials: true,
transportOptions: {
polling: {
extraHeaders: {
'authorization': `${token}`,
},
},
},
});
export const socket = ioSocket
The vue 3 router logic:
import { createRouter, createWebHistory } from 'vue-router'
import Landing from '#/views/Landing.vue'
import Login from '#/views/login/Login.vue'
import ResetPassword from '#/views/login/ResetPassword.vue'
import ForgotPassword from '#/views/login/ForgotPassword.vue'
const routes = [
{
path: '/login',
name: 'login',
component: Login,
meta: {
isGuest: true,
title: 'Servant | Login'
}
},
{
path: '/resetPassword',
name: 'resetPassword',
component: ResetPassword,
meta: {
isGuest: true,
title: 'Servant | Reset Password'
}
},
{
path: '/forgotPassword',
name: 'forgotPassword',
component: ForgotPassword,
meta: {
isGuest: true,
title: 'Servant | Forgot Password'
}
},
{
path: '/',
name: 'landing',
component: Landing,
meta: {
requiresAuth: true,
title: 'Servant',
role: 'waiter'
}
},
{
path: '/:pathMatch(.*)*',
component: Landing
},
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach((to, from, next) => {
document.title = to.meta.title || "Servant"
let token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
if(to.meta.requiresAuth && !token)
{
next({name: 'login'})
}
if (token && to.meta.isGuest )
{
next({ name: 'landing' })
}
next();
});
export default router
Login component logic:
function login() {
loading.value = true
formClass = ''
if (user.remember)
{
window.localStorage.setItem('remember', user.remember)
}
mainStore
.login(user)
.then((response) => {
loading.value = false
router.push({
name: 'landing',
})
})
.catch((err) => {
loading.value = false
errorMsg.value = err.response.data.messages.error
formClass = 'was-validated'
})
}
Once the component is mounter I have following logic:
onMounted(() => {
socket.emit("join", import.meta.env.VITE_SOCKET_ROOM, (message) => {
console.log(message);
});
})
On the SocketIO server side I have following logic:
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
if(header !== 'null')
{
jwtVerify(header, secret).then((res) => {
if (res === true) {
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next();
}
return next(new Error("authentication error"));
});
}
});

How to get around vue router infinite redirection error?

Getting this error as I want to check in router.beforeEach if there is a sessionToken already in storage and if not then redirect to Login where I could retrieve it:
Detected an infinite redirection in a navigation guard when going from "/" to "/login". Aborting to avoid a Stack Overflow. This will break in production if not fixed.
My code in router.js
router.beforeEach((to, from, next) => {
if(ENV == 'development') {
let sessionStorage = storage.sessionStorageGet('_sessionToken')
if (sessionStorage === null) next({ name: 'Login' })
else next()
}
})
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('../views/login'),
meta: {
requiresAuth: false
}
},
{
path: '/private',
... private route config,
meta: {
requiresAuth: true
}
}
];
router.beforeEach(async (to, from, next) => {
if (ENV == 'development') {
if (to.matched.some(record => record.meta.requiresAuth)) {
const sessionStorage = storage.sessionStorageGet('_sessionToken')
if (sessionStorage) {
next();
} else {
router.push({ name: 'Login' });
}
} else {
next();
}
}
});

$route.query mysteriously being set

Vue 2.6.12
I'm using router.beforeEach in my router to log to.query along my !auth / auth / entryUrl logic.
It starts out empty, but right before the operative next() it has an object (a filters object in my store). I'm mystified how it's getting set as I'm not doing it intentionally and don't know how to pinpoint the source.
Rereshing the page and logging this.$route.query in beforeCreate() should return an empty object but it's the filter object.
On other pages, logging this.$route.query returns {} as I would expect.
router/index
(tips on how to refactor this for efficiency welcome)
import Vue from "vue";
import VueRouter from "vue-router";
import AddItem from "../views/AddItem";
import store from "../store/index";
...
Vue.use(VueRouter);
const routes = [
{
path: "/login",
name: "AppLogin",
component: AppLogin,
meta: { requiresAuth: false },
},
{
path: "/art-inventory",
name: "ArtInventory",
component: ArtInventory,
meta: { requiresAuth: true },
},
{
path: "/",
redirect: { name: "ArtInventory" },
meta: { requiresAuth: false },
},
{
path: "*",
redirect: "/404",
component: NotFound,
meta: { requiresAuth: false },
},
{
path: "/404",
name: "404",
component: NotFound,
meta: { requiresAuth: false },
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
let entryUrl = null;
router.beforeEach((to, from, next) => {
console.log(to.query); // empty {}
let localStorageUserData = JSON.parse(localStorage.getItem("userData"));
let storeUserData = store.getters.getUserData;
let userData = localStorageUserData || storeUserData;
let isAuthenticated = userData.token !== "" && userData.token !== undefined;
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!isAuthenticated) {
if (to.name !== "AppLogin" && to.name !== "ArtInventory") {
entryUrl = to.fullPath;
}
if (to.name !== "AppLogin") {
next({ name: "AppLogin" });
}
} else if (entryUrl) {
let url = entryUrl;
entryUrl = null;
window.location.href = url;
} else {
console.log(to.query); // not empty {...}
next();
}
} else {
next();
}
});
export default router;

vue-router does not stay on same page after a page refresh

I have two routes on vue-router, /user/:uid and /itinerary/:id. The first route is shown after the user logs in and from there they select the itinerary they want to view which would bring them to the next route.
While on /itinerary/:id, if I do a page refresh/reload/f5 I see the browser address bar back on /user/:uid. In the vue-devtools, it just resets to showing /user/:uid (not a case of some router.push() or whatever). Why is this happening and how can I keep the user on /itinerary/:id even if they refresh the page?
const routes = [
{ path: '/', component: LoginPanel },
{
path: '/user/:uid',
component: ItineraryList,
beforeEnter: authGuard,
},
{
path: '/itinerary/:itinerary_id',
name: 'itineraryView',
component: ItineraryBuilder,
beforeEnter: authGuard,
},
];
function authGuard(to, from, next) {
// retrieved from localStorage
if (idToken && userEmail) {
next();
} else {
next('/');
window.alert('Please login first');
}
}
Edit: Added relevant code from ItineraryBuilder
beforeRouteLeave(to, from, next) {
if (this.confirmed) { // set on data() property
next();
} else {
this.$modal.show('dialog', {
title: 'Confirm save new changes?',
text: '',
buttons: [{
title: 'Yes',
handler: () => {
let itinerary_id = this.$route.params.itinerary_id
let queries = [];
this.itinerary.forEach(item => {
console.log(item);
let board_id = item.doc_id;
queries.push(db.collection('itineraries').doc(board_id).set(item, { merge: true }));
});
Promise.all(queries).then(() => {
// this.$store.commit('UPDATE_ITINERARY', this.itinerary);
this.$modal.hide('dialog');
this.confirmed = true
eventBus.$emit('itinerary_saved', true);
next();
})
}
},
{
title: 'No',
handler: () => {
this.$modal.hide('dialog');
next(false);
}
}]
});
}
}
I didn't notice it at first, but could it be related? I'm using this.confirm as a flag to handle if there are any changes made by the user, I'll emit a change event and set the flag to false - meaning there are unsaved changes so the current state is unconfirmed.

Vue JS (router.beforeEach) failed to convert exception to string

I'm trying to use router.beforeEach with localStorage. If there is data in localStorage, I want to skip the homepage. If there is no data, enter the homepage. I can print the data with console.log, but the router process fails
[vue-router] uncaught error during route navigation > failed to
convert exception to string.
How do I control the navigation?
My router.js:
Vue.use(Router);
const router = new Router({
routes: [{
path: '/',
name: 'index',
components: {
default: Index,
header: MainNavbar
},
props: {
header: {
colorOnScroll: 400
}
}
},
{
path: '/landing',
name: 'landing',
components: {
default: Landing,
header: MainNavbar,
footer: MainFooter
},
props: {
header: {
colorOnScroll: 400
},
footer: {
backgroundColor: 'black'
}
}
},
{
path: '/login',
name: 'login',
components: {
default: Login,
header: MainNavbar
},
props: {
header: {
colorOnScroll: 400
}
}
},
{
path: '/profile',
name: 'profile',
components: {
default: Profile,
header: MainNavbar,
footer: MainFooter
},
props: {
header: {
colorOnScroll: 400
},
footer: {
backgroundColor: 'black'
}
}
}
],
scrollBehavior: to => {
if (to.hash) {
return {
selector: to.hash
};
} else {
return {
x: 0,
y: 0
};
}
}
});
router.beforeEach((to, from, next) => {
let adres = JSON.parse(localStorage.getItem('adres'));
if (!adres) {
next('/');
} else {
next('/login');
}
});
export default router;
Example localdata:
{
"id":1,
"adi":"Demo",
"soyadi":"Sef",
"telefon":"05322375277",
"adres":"Girne Mahallesi 6022 Sk. No:22 Kahta/Adıyaman",
"fotograf":"http://localhost:8000/media/kullanici/sef/demosef/chef-1.jpg"
}
You are creating an infinite loop where your beforeEach guard gets triggered over and over. In the beforeEach it checks whether there is an address in localStorage and redirects to either / or /login. Then again before you enter the new route beforeEach is called and checks if there is an address and redirects. The process is repeated ad infinitum. You need to call next() without any parameters somewhere in your beforeEach guard to confirm normal navigation. So you might want to do something like this ..
router.beforeEach((to, from, next) => {
if (to.path == '/') { // If we are entering the homepage.
let adres = JSON.parse(localStorage.getItem('adres'));
if (!adres) {
next();
} else {
next('/login');
}
} else { // Not entering the homepage. Proceed as normal.
next()
}
});