'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