Daividh
Daividh

Reputation: 405

How to elegantly use v-model and Vuex store?

I search for a clean way to use v-model and vuex store.

Vuex provide several helpers methods are pretty usefull but a little bit annoying when using with v-model.

The way I do today to use v-model and my modularized store is like

computed: {
  type: {
    get() {
      return this.$store.state.mymodule.type;
    },
    set(newValue) {
      this.$store.dispatch('mymodule/setType', newValue)
    }
}

This works BUT I found better to take profit of vuex helpers to avoid boilerplate code (this.$store, module name, ...)

Ok, so I firstly want to get rid of module name. Vuex provide a great createNamespacedHelpers which return modularized helpers.

Let's use it :

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

So, ok, we now have a clean mapState and mapActions functions which are module dedicated.

computed: {
  ...mapState(['type']) // No need here to specify module name :)
}

Pretty cool but since mapState has only get function, I can't set a dispatch function to update data...

In case of using v-model, I find helpers unusable. I can't use mapState, thus I can't use createNamespacedHelpers.

So : How can I use the benefit of Vuex helpers functions and v-model to work together ?

Upvotes: 4

Views: 3295

Answers (3)

Kharel
Kharel

Reputation: 827

Try this.

// in some utils/vuex.js file 
export const mapSetter = (state, setters = {}) => (
  Object.keys(state).reduce((acc, stateName) => {
    acc[stateName] = {
      get: state[stateName],
   };
   // check if setter exists
   if (setters[stateName]) {
      acc[stateName].set = setters[stateName];
   }

   return acc;
 }, {})
);

In your component.vue file

  import { mapSetter  } from 'path/to/utils/vuex.js';
  ...

  export default {
    name: 'ComponentName',
    computed: {
      ...mapSetter(
        mapState({
          result: ({ ITEMS }) => ITEMS.result,
          total: ({ ITEMS }) => ITEMS.total,
          current: ({ ITEMS }) => ITEMS.page,
          limit: ({ ITEMS }) => ITEMS.limit,
        }),
        {
          limit(payload) {
            this.$store.dispatch({ type: TYPES.SET_LIMIT, payload });
          },
        },
      )
    },
  }

now v-model binding should work.

Upvotes: 0

Daividh
Daividh

Reputation: 405

The way I finally found the most readable is the following :

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

Used like this :

computed: {
  ...mapGetters(['type'])
},
methods: {
  ...mapActions(['setType'])
}

And without the v-model

<input :value="type" @input="setType($event.target.value)">

Upvotes: 1

Vladislav Ladicky
Vladislav Ladicky

Reputation: 2489

You can't. There is no elegant way to combine helpers with v-model. But v-model is just a syntactic sugar, so maybe the most readable way is to use the helpers

computed: {
  ...mapGetters('path/to/module', ['type'])
},
methods: {
  ...mapActions('path/to/module', ['setType'])
}

without the v-model

<input :value="type" @input="setType">

Upvotes: 2

Related Questions