How to make if statement in beforeEach only work on loadup once? - vue.js

I set up my router beforeEach, is it possible, for the slug thing to only take action on first load up of page?
For example, if I enter my page it should redirect me from default to phase-first, it works fine. But after when I try to click again on default button to redirect me to default page, it keeps redirecting me to phase-first. I've tried adding another to path default afterwards but it wont work.
router.beforeEach((to, from, next) => {
let slug = store.state.profile.currentPhase.slug;
if (to.path === '/phase/default' && slug === 'phase-first') {
return next('/phase-first');
}
if (to.path === '/phase/default') {
return next('/phase/default');
}
next();
}

How about
let redirectToFirstPhase = true;
router.beforeEach((to, from, next) => {
// let slug = store.state.profile.currentPhase.slug;
if (to.path === '/phase/default' && redirectToFirstPhase) {
redirectToFirstPhase = false;
next('/phase-first');
} else {
next();
}
})

Related

How to route to the route's children path in Vue

I'm trying to route to a different route in vue after clicking the back button in the browser. Below is what I have done:
beforeRouteLeave (to, from, next) {
if (to.path === '/place-ad') {
console.log("path is /place-ad")
next('/place-ad?ad_id=' + 12345)
} else {
console.log("path is I don't know")
next()
}
The problem with the above is that I end up with the error:
VueJs Maximum call stack size exceeded
If I now change the if statement check to to.path !== '/place-ad' then there is no error but it doesn't route me to where I want, in this case the else clause gets triggered. I've seen many other similar stackoverflow questions but from what I've seen they all want to reroute to a completely different route. Whereas I'm trying to route to the route's children path.
ie: /place-ad?ad_id=' + 12345 instead of just /place-ad. How do I achieve this? Thank you.
So you want to add a query param on a specific route, if it is not set?
You can also do it like this in your place-ad page component:
<script>
export default {
created () {
if (!this.$route.query.ad_id) {
this.$router.push({
query: {
ad_id: '12345',
...this.$route.query
}
})
}
}
}
</script>
Or, if you have to stick to the beforeRouteLeave, this one should work too:
beforeRouteLeave (to, from, next) {
if (to.path === '/place-ad') {
if (!to.query.ad_id) {
return next({
path: to.path,
query: {
...to.query,
ad_id: '12345'
}
})
}
}
return next()
}

Vue3 - Using beforeRouteEnter to prevent flashing content

I am using Vue with axios like
[...]
import VueAxios from "vue-axios";
import axios from "#/axios";
createApp(App)
.use(store)
.use(router)
.use(VueAxios, axios)
.mount("#app");
[...]
which works really great globally like this.axios... everywhere. I am also using Passport for authentification and in my protected route I would like to call my Express-endpoint .../api/is-authenticated to check if the user is logged in or not.
To make this work I would like to use the beforeRouteEnter-navigation guard, but unfortunately I can't call this in there.
Right now I am having in in the mounted-hook, which feels wrong. Is there any solution with keeping my code straight and clean?
I'd appreciate a hint. Thanks.
Edit: This worked for me.
beforeRouteEnter(to, from, next) {
next((vm) => {
var self = vm;
self
.axios({ method: "get", url: "authenticate" })
.then() //nothing needed here to continue?
.catch((error) => {
switch (error.response.status) {
case 401: {
return { name: "Authentification" }; //redirect
//break;
}
default: {
return false; //abort navigation
//break;
}
}
});
});
With beforeRouteEnter there is access to the component instance by passing a callback to next. So instead of this.axios, use the following:
beforeRouteEnter (to, from, next) {
next(vm => {
console.log(vm.axios); // `vm` is the instance
})
}
Here's a pseudo-example with an async request. I prefer async/await syntax but this will make it clearer what's happening:
beforeRouteEnter(to, from, next) {
const url = 'https://jsonplaceholder.typicode.com/posts';
// ✅ Routing has not changed at all yet, still looking at last view
axios.get(url).then(res => {
// ✅ async request complete, still haven't changed views
// Now test the result in some way
if (res.data.length > 10) {
// Passed the test. Carry on with routing
next(vm => {
vm.myData = res.data; // Setting a property before the component loads
})
} else {
// Didn't like the result, redirect
next('/')
}
})
}

Prevent route change on refresh with Vue navigation guards

I have navigation guards working perfectly based on a userTp value, however whenever the user reloads a page it redirects them to name: 'login'.
I would like to prevent a route change on refresh. Is there a simple way to do this within navigation guards?
router.beforeEach(async (to, from, next) => {
if (to.meta.requireAuth) {
if (!store.state.auth.user) {
await store.dispatch(AUTH_REFRESH)
}
if (to.meta.userTp === store.state.auth.user.userTp) {
next()
window.scrollTo(0, 0)
} else {
next({ name: 'login' })
window.scrollTo(0, 0)
}
} else {
next()
window.scrollTo(0, 0)
}
})
EDIT:
I found a semi-workaround for this, but it's not perfect. It allows users to access pages if they know the correct page url path, but since my setup has a server-side userTp check before sending data it just shows the page template but without any data in it.
My temp solution:
(Added as the first if statement)
router.beforeEach(async (to, from, next) => {
if (to = from) {
if (!store.state.auth.user) {
await store.dispatch(AUTH_REFRESH)
to()
}
}

Vue router guard beforeEach store user location before login and redirect after. What is right way to implement?

I'm using vue router with quasar framework. Here is my route guard at beforeEach hook:
let storedURL = null
router.beforeEach((to, from, next) => {
const record = to.matched.find(record => record.meta.auth)
if (record) {
if (store.getters['auth/check']) {
if (storedURL) {
const redirectURL = storedURL
storedURL = null
next(redirectURL)
} else {
next()
}
} else {
storedURL = to.path
next('/')
}
} else {
if (to.path === '/' && store.getters['auth/check']) {
next('/dashboard')
} else {
next()
}
}
})
It works but throws an error when store location:
Error: Script terminated by timeout at:
notify#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:730:36
reactiveSetter#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1056:11
proxySetter#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4625:5
__onControlFocusin#webpack-internal:///./node_modules/quasar/src/components/field/QField.js:345:9
invokeWithErrorHandling#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1853:26
invoker#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:2178:14
add$1/original._wrapper#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:6907:25
and when redirects:
uncaught exception: undefined
Without this guard there are no errors. But I can't understand what is wrong with my code. Thanks for help!

vue router next() function don't work in Promise in router.beforeEach

I have the following code:
router.beforeEach((to, from, next) => {
if (to.name !== from.name) {
store
.dispatch("fetchCurrentUser")
.then(() => {
console.log('then');
// do something
next();
})
.catch(() => {
console.log('catch');
router.push("/login");
next();
});
} else {
next();
}
// next();
});
I'm trying to get the current user, and if this succeeds, then do something with this data, and if the request is not successful, then redirect the user to the login page. But next () calls do not work, I get the "then" or "catch" in the console, but the redirect does not occur and an infinite loop begins. But if I take next () from condition (commented row) the redirect works fine.
To redirect you should use next('/') or next({ path: '/' }).
From the documentation:
next: Function: this function must be called to resolve the hook. The
action depends on the arguments provided to next:
next(): move on to the next hook in the pipeline. If no hooks are
left, the navigation is confirmed.
next(false): abort the current navigation. If the browser URL was
changed (either manually by the user or via back button), it will be
reset to that of the from route.
next('/') or next({ path: '/' }): redirect to a different location.
The current navigation will be aborted and a new one will be started.
You can pass any location object to next, which allows you to specify
options like replace: true, name: 'home' and any option used in
router-link's to prop or router.push
The promise resolves after the function ends.
This means that the commented next happens regardless of the result of the promise result. Then the promise resolves and you call another next.
The bottom line is that you don't need the commented next and should just cover the promise resolve.
I was able to implement an async validation inside beforeEach, authentication in my case.
export async function onBeforeEach(to, from, next) {
let { someUserToken } = to.query
if (someUserToken) {
let nextRoute = await authenticate(to)
next(nextRoute)
} else {
const userToken = store.state.application.userToken
if (!to.meta.public && !userToken) {
next({ name: 'Forbidden' })
} else {
next()
}
}
}
async function authenticate(to) {
let { someUserToken, ...rest } = to.query
return store
.dispatch('application/authenticate', {
someUserToken
})
.then(() => {
return {
name: 'Home',
query: {
...rest
}
}
})
.catch(() => {
return { name: 'Forbidden' }
})
}
I hope this helps.