Skip to content

Add possibility to handle aborted navigations globally #3157

Closed
@bponomarenko

Description

@bponomarenko

What problem does this feature solve?

Adds possibility to handle aborted navigations globally.

Initiated vue-router navigation could have different outcomes:

  • successful navigation
  • errored navigation (in case there is non-internal js error in one of the guards or hooks)
  • aborted navigation

In cases, when application logic depends on these navigation results, vue-router has very limited support to catch aborted navigations, and no support to do it globally.

Successful navigation can be catched in global afterEach hook or in beforeResolve guard (however error still can be thrown there). Also there is optional onComplete callback for router.push, but there is no way to set it from <router-link>.
Navigation with error (because of JS error in one of the guards or hooks) can be catched in global onError hook. Also there is optional onAbort callback for router.push (which will be called for both navigation with error and aborted navigation), but there is no way to set it from <router-link>.
When it comes to aborted navigations, the only place where it can be catched – onAbort callback for router.push. However, as mentioned before, it is not possible to set it from <router-link>. Moreover, there is no global hook to catch aborted navigations at all.

Use case

In our application we would like to simply display loading indicator in the root App component while navigation is in progress. Also, we would like to have this functionality be generic, so it can be applied to different applications. The easiest way to achieve it is by creating separate ES module like this navigation-state-plugin.js:

import Vue from 'vue';

export const state = Vue.observable({ navigating: false });

export default router => {
  // When navigation initiated – update state and proceed
  router.beforeEach((to, from, next) {
    state.navigating = true;
    next();
  });

  // When navigation successfully finished – update state accordingly
  router.afterEach(() => {
    state.navigating = false;
  });

  // When navigation failed –also update state
  router.onError(error => {
    state.navigating = false;
  });
};

In this way in can be applied in the main.js:

import VueRouter from 'vue-router';
import applyPlugin from './navigation-state-plugin.js`;

// do other initializations

const router = new VueRouter(...);
applyPlugin(router);

// init application

Also state from this file can be imported in App.vue and used to display loading indicator when state.navigating is true.

However, when navigation is aborted there is no way to track it in a global manner, which leads to loading indicator to stay visible on a screen.

As possible solution, it was suggested to "use router.push or the v-slot api and get the promise returned from navigate" (which is currently not available because #3042 has no PR). However this solution would require to create wrappers around vue-router API, which should always be used throughout entire application instead of native vue-router api. Even though it is easy to do for router.push, it is cumbersome to do for <router-link> because wrapper should be able to support the same rendering scenarios (without slot content, with slot content, with scoped slot content, etc.)
It is also not suitable for us, because we have shared set of router components, which are re-used in different vue applications. And not all applications will have navigation-state-plugin.js. So we need to have possibility to use native router.push and <router-link> with an option to globally manage navigation state.

What does the proposed API look like?

Most obvious would be to introduce global onAbort hook:

  router.onAbort(function callback(abortedRoute, reason) { ... });

where reason can be one of the errors from #3047

It may be possible to call existing onError or afterEach hooks also for aborted navigations, but that would be a breaking change, and probably would make API more confusing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions