3

I have some skeleton code below which demonstrates an issue I'm having with forcing the correct order of calls. The order that I want is:

  • A user triggers Foo.vue's someLocalMethod().
    • A flag is set so that Bar will become aware that a change has been made.
  • The flag will trigger Bar.vues's watcher to go ahead and do some work.
    • When the work is complete it will unset the flag
  • Foo.vue's someLocalMethod() will become aware that the flag was unset and continue on with its work

I should mention that both Bar and Foo are doing work that changes the store, so the files are related (instead of solely using the store as a flag between both files).

I'm assuming there's a much better/cleaner way to approach what I'm doing, but I can't quite put my finger on it. Code is shown below.

Bar.vue

computed: {
  ...mapGetters('store', ['getSomeFlag']),
},
methods: {
  ...mapMutations('store', ['setSomeFlag']),
},
watch: {
  getSomeFlag(newValue) {
    if (newValue === true) {
      console.log('Flag is set. Going to do work');
      doWorkRelevantToBarPage();
      setSomeFlag(false);
    }
  }
}

Foo.vue

methods: {
  ...mapActions('store', ['someStoreCall']),
  ...mapMutations('store', ['setSomeFlag']),

  someLocalMethod(someData) {
    this.setSomeFlag(true);
    // somehow wait until Bar.vue's watcher becomes aware of it and completes it's own work
    this.someStoreCall(someData);      
  }
},

store.js

state: {
  someFlag: false,
  data: { 
    // Can be tons of nested structures, properties, etc
  },
},
getters: {
  getSomeFlag: (state) => state.someFlag,
},
mutations: {
  setSomeFlag(state, value) {
    state.someFlag = value;
  },
},
actions: {
  async someStoreCall({dispatch, commit, state}, data) {
    console.log('In someStoreCall');
  }
},

2 Answers 2

1

I would suggest using an event bus for this use case:

  1. in your Foo component's LocalMethod you emit an event (e.g. called "FooChanged") on the bus
  2. the event handler in Bar component (subscribed to "FooChanged" events) will do its work and then emit another event (e.g. called "BarFinished") on the bus
  3. the event handler in your Foo component (subscribed to "BarFinished" event) will continue with the next step in your flow

Something like this:

eventBus.js

import Vue from "vue";
export default new Vue();

Foo.vue

import events from "eventBus";

export default
{
  created()
  {
    events.$on("BarFinished", this.onBarFinished);
  },
  beforeDestroy()
  {
    events.$off("BarFinished", this.onBarFinished);
  },
  methods:
  {
    LocalMethod()
    {
      ....
      events.$emit("FooChanged", param1, param2);
    },
    onBarFinished(param1)
    {
      ...
    }
  }
}

Bar.vue

import events from "eventBus";

export default
{
  created()
  {
    events.$on("FooChanged", this.onFooChanged);
  },
  beforeDestroy()
  {
    events.$off("FooChanged", this.onFooChanged);
  },
  methods:
  {
    onFooChanged(param1, param2)
    {
      ...
      events.$emit("BarFinished", param1);
    }
  }
}

Sign up to request clarification or add additional context in comments.

3 Comments

Yeah, that sounds like a better way to go. I'm gonna leave this question open a bit longer to see if any other answers come up. Thanks!
@ekjcfn3902039 Event buses are a good solution for smaller applications but if you want to create a big/scalable application with vue.js it is better to use vuex
@MrClottom, how would you do that in vuex using the example above?
1

Simple solution

The problem is Foo.vue is essentially trying to run the doWorkRelevantToBarPage method in Bar.vue which is probably done easiest using an event bus as suggested in another response but using an event bus is not a good idea if you want to create larger/scalable applications with vue.js

The problem lies in the approach

With doWorkRelevantToBarPage I assume you most likely want to change some state stored in Bar.vue or run some other methods that do so. Now that you have this method you want to run it from Foo.vue but this seems like the wrong approach (especially if you want to use vuex), you see you are essentially trying to change the state of a component externally which mostly isn't a good idea.

Using Vuex

The following steps will summarize what you can do to adapt your code to use vuex

  1. Move any state that Foo.vue needs to change externally to the store.
  2. Create mutations and getters for the states you placed in the store
  3. Hook up the getters to your Bar.vue component
  4. Create an action that commits the necessary mutations as shown here (choose a more descriptive name):
updateBarData({commit, dispatch}, ...args) {
  commit('mutation1')
  commit('mutation2')
  commit('mutation3')
  dispatch('action1')
  dispatch('action2')
  // ... other logic
}
  1. Add the action to Foo.vue
methods: {
  ...mapActions('store', ['someStoreCall', 'updateBarData']),
  someLocalMethod(someData) {
    this.updateBarData()
    this.someStoreCall(someData)
  }
}

I hope this helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.