Search code examples
vue.jsvuex

Can VueX's store also emit events to components?


I have the following component tree:

App
  List
  ItemDetails
    Widget1
    ...
  • the List component has a filters form, those are applied on button press
  • Widget1 has a button which applies another filter (by id) to the list, applying that one removes filters from the filter form and vice versa
  • the list is also live-updated via polling (later will be via WebSockets), so I have to separate v-model of the filter fields in List and the actually applied filters (those are in the store)

Currently, I've put the following state to the VueX store:

state: {
    filters: {
        // these come from the List's filter panel
        result: '',
        name: '',
        date: '',
        // this one comes from Widget1
        id: '',
    },
    pagination: {
        perPage: 10,
        page: 0,
        total: 0,
    },
    items: [],

    selectedItem: null,
},

and both filters from the List and from the button in Widget1 are applied via dispatching the following action to the store:

applyFilters ({ state, commit, dispatch }, filters) {
    if(filters.id) {
        for(let filterName in state.filters)
            if(filterName !== 'id')
                filters[filterName] = '';
    } else {
        filters.id = '';
    }
    commit('setFilters', filters);
    commit('setPage', 0);
    dispatch('loadExaminations');
},

But here's the problem: the List component has its model of filter fields (on press of the button those are gathered and applyFilters is dispatched). This works well except when the id filter (from Widget1) is applied, the filter fields in the List component are not emptied.

One obvious option to handle this is to move the model of those filter fields to the store as well. That doesn't look that nice since for each field (that uses v-model) I have to create a computed (in the List component) and write a setter and a getter from the store. It seems nicer to send an event from applyFilters to List saying "empty the filter fields", but I'm not sure if VueX can do this. Is this possible? Or should I stick with the "move filter fields' model to the store" plan? Or do you know a nice alternative? I'm aware of the event bus concept, but that one uses Vue instance which shouldn't be used in store, I guess.


Solution

  • You can listen to vuex's actions by using subscribeAction.

    // List.vue
    mounted() {
        this.$store.subscribeAction({
          before: (action, state) => {
            if (action.type === "applyFilters" && action.payload.id) {
              this.emptyComponentFields();
            }
          },
          after: (action, state) => {}
        });
      }
    

    CodeSandbox.