In vue-router, how to use beforeLeave - vue-router

I want to finish some information verify before one component leave.
I've scanned the vue-router document: https://router.vuejs.org
But I use vue-cli, in my file: router1.vue,
console.log(this.$router.beforeLeave) -> undefined
How can I use it?

Add this to your router1.vue:
export default {
//...
beforeRouteLeave (to, from, next) {
// called when the route that renders this component is about to
// be navigated away from.
// has access to `this` component instance.
},
//...
}
for example:
beforeRouteLeave (to, from , next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
And it will be called before this route leave.
ref: https://router.vuejs.org/en/advanced/navigation-guards.html

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()
}

onBeforeEnter does not exist in vue-router#next

I am trying to switch over to vuejs3 and the new vue-router.
Now I see that beforeRouteEnter is not exposed:
// same as beforeRouteUpdate option with no access to `this`
onBeforeRouteUpdate((to, from, next) => {
// your logic
console.log("Hello world") //this is only triggered if the id changes
next()
})
So my question is: How can I trigger the initial axios-requests on a specific route like /dashboard as I did before?
It's not possible to execute code in setup before the route enters because at the time of setup the navigation has already been confirmed.
Another option #1
You can still use the options api, so you could still use beforeRouteEnter:
setup() {
...
},
beforeRouteEnter(to, from, next) {
console.log(to);
}
Another option #2
Use beforeEnter in the router:
{
path: '/foo',
component: Foo,
beforeEnter(to) {
console.log(to)
}
}

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 beforeRouteLeave fired twice

I am trying to create a global "unsaved changes confirm" functionality.
So whenever some form is edited, I set a global vuex state saying there's unsaved changed, and then I want the user to confirm before the route changes.
I have created a global mixin - and it works fine, except when theres nested router views - then the beforeRouteLeave is fired twice, and then the next() function doesn't work?
This is how it looks:
Vue.mixin({
computed: {
unsavedChanges() {
return this.$store.getters["helpers/unsavedChanges"]
}
},
beforeRouteLeave (to, from, next) {
if (this.unsavedChanges) {
this.$Modal.confirm({
title: 'Unsaved changes',
content: '<p>Are you sure you wish to leave the page?</p>',
okText: 'Continue',
cancelText: 'Cancel',
onOk: () => {
next()
},
onCancel: () => {
next(false)
}
});
}
}
})
It works fine whenever there's no sub router views, but if there are, then the router doesnt change, even if you click Continue and the next() function is called.
How do I solve this? Thanks.