'Detect navigation abort/cancel in a guard
Currently it's expected that the navigation is confirmed, so both beforeEach
and afterEach
global guards are triggered. showSpinner
and hideSpinner
are supposed to be called in pair on each navigation.
router.beforeEach((to, from, next) => {
showSpinner();
next();
}
router.afterEach((to, from) => {
// This line can be never called
hideSpinner();
});
From what I see, navigation result isn't available through next
in guards.
Then in another place the navigation is cancelled, so it stays on the previous route, and afterEach
is not triggered (the same likely applies to navigation abort):
beforeRouteEnter() {
...
next(false);
}
The result is that afterEach
and hideSpinner
not being triggered.
This happens with Vue Router 3.4.9 and Vue 2.6.
How can this be solved?
Solution 1:[1]
It seems that there is no built-in way to get an abort event from a canceled navigation.
However, digging into vue-router
's code I found that every transition to a route is handled in router.history
by transitionTo
function that accepts as a third argument an abort function, which is called whenever the navigation has not been confirmed. It is possible to intercept that event by overwriting this function.
I'm sure this code will get some critique, but it is here maybe to rise some ideas.
const transitionTo = Object.getPrototypeOf(router.history).transitionTo;
Object.getPrototypeOf(router.history).transitionTo = function (...args) {
const abortFn = args[2] && new Function("return " + args[2].toString());
args[2] = (err) => {
// eventBus.$emit("hide-spinner"); // or any other method to hide the spinner
console.log("Aborted", err);
abortFn && abortFn(err);
};
transitionTo.apply(router.history, args);
};
And here is a working codesandbox
Solution 2:[2]
As I understood from your question, you want afterEach is triggered even when the navigation is cancelled. For better answering the question we suppose that we have an About route that in some conditions the navigation to this route is cancelled. As Vue-router documentation says:
you can access the instance by passing a callback to next. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument.
So in the About route we can have this code:
beforeRouteEnter(to, from, next) {
next(vm => {
// put your codes here, for example the condition that must be applied
let condition = true;
if (condition) {
vm.$router.push(from.path);
}
})
}
By using that code instead of next(false)
, we actually cancels the navigation to About route, but in this case the afterEach
is also triggered.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Igor Moraru |
Solution 2 | hamid-davodi |