[Vuejs]-Vue-router – How to cancel operations when user clicked menu links

2👍

You could use a beforeRouteLeave navigation guard to abort that action (i.e., cancel the timer in your example) upon switching routes.

  1. Assuming identifiable submit actions, save the ID of the operation result (i.e., save the timer ID from setTimeout‘s return value in your example).
  2. Add a beforeRouteLeave handler to the component to cancel the submit action (i.e., clear the timer ID in your example).
const Home = {
  methods: {
    submit() {
      this.timerId /* 1 */ = setTimeout(() => {
        this.$router.push("/bar");
      }, 5000);
    }
  },
  beforeRouteLeave (to, from, next) {
    clearTimeout(this.timerId) /* 2 */
    next()
  }
};

updated jsfiddle

👤tony19

1👍

Here’s one idea: make a component that provides (using Vue’s provide/inject API):

  1. A function that starts an operation. This is called when a form is sent. It provides a whenDone callback which is either executed or ignored, depending on if the operation is cancelled.
  2. A function that cancels all pending operations. The cancel function could be called when the user navigates away.

The implementation could look like this:

const CancellableOperationProvider = {
  name: "CancellableOperationProvider",
  props: {},
  data: () => ({
    pendingOperations: []
  }),
  /*
   * Here we provide the theme and colorMode we received
   * from the props
   */
  provide() {
    return {
      $addOperation(func) {
        this.pendingOperations.push(func);
        func(function whenDone(callback) {
          if (this.pendingOperations.includes(func)) callback();
        });
      },
      $cancelAllOperations() {
        this.pendingOperations = [];
      }
    };
  },
  render() {
    return this.$slots.default[0];
  }
};

The usage would look like this:

const Home = { 
  template: '<div><button @click="submit">Save and go Bar!</button></div>',
  inject: ['$addOperation', '$cancelAllOperations'],
  methods: {
    async submit() {
      this.$addOperation(whenDone => {
        await setTimeout(() => {
          whenDone(() => this.$router.push("/bar"));
        }, 5000);
      });
    }
  }
};

You could then add a navigation guard to the Vue Router so that $cancelAllOperations is called after clicking any link. Since $cancelAllOperations is only accessible through the inject API you will have to make a component that imperatively adds a navigation guard to the Vue router after mounting and removes it when unmounting.

Let me know if it doesn’t work–I haven’t done Vue in a while.

0👍

I used the answer from tony19 to make solution which fits my needs for use cases without setTimeout too:

const Home = { 
  template: '<div><button @click="submit">Save and go Bar!</button></div>',
  data() {
    return {
        onThisPage: true
    }
  },
  beforeRouteLeave(to, from, next) {
    this.onThisPage = false;
    next();
  },
  methods: {
     submit() {
           setTimeout(() => {
           if (this.onThisPage) {
                this.$router.push("/bar");
           }
        }, 5000);
     }
  }
};

See here: https://jsfiddle.net/ovmse1jg/

Leave a comment