Setter for computed property obtained from store in vue.js

Viewed 13619

I'd like to make two checkboxes which get value from store.js and send them to backend through a form:

<label>Notify me 
    <input type="checkbox" v-model="notification" value="notification" />       
</label>

<label>Email me 
    <input type="checkbox" v-model="email" value="email" />     
</label>

I get the values as a computed property:

computed: {
  BASE_URL () {
    return this.$store.state.BASE_URL;  
  }, 
  notification () {
    return this.$store.state.notification; 
  },

  email () {
    return this.$store.state.email; 
  }
}

The problem is that checking the checkboxes does not change the values in the store, and in addition to that I get this warning in the console, like:

vue.esm.js?65d7:479 [Vue warn]: Computed property "notification" was assigned to but it has no setter.

I know that one can define setter in computed property, as described in the vue.js docs, but I don't know how to do that when there are multiple values to set, like in my particular case.

So appreciate your help to fix this.

1 Answers

To change Vuex state, you will need a mutation.

Provided you have a mutation setNotification for changing the notification state, you can configure the property in your component like this:

computed: {
    notification: {
        get() { return this.$store.state.notification; },
        set(value) { this.$store.commit('setNotification', value); },
    },
},

You can now bind to it with v-model="notification" as normal.

See Form Handling in the docs for more info.


Since this is a frequent thing that I do in my projects, I have written a helper function which generates the computed properties:

function mapStateTwoWay(...args) {
    const result = {};

    if (args.length === 1) {
        for (const prop of Object.keys(args[0])) {
            result[prop] = {
                get() { return this.$store.state[prop]; },
                set(value) { this.$store.commit(args[0][prop], value); },
            };
        }
    } else {
        for (const prop of Object.keys(args[1])) {
            result[prop] = {
                get() { return this.$store.state[args[0]][prop]; },
                set(value) { this.$store.commit(args[0] + '/' + args[1][prop], value); },
            };
        }
    }

    return result;
}

Use it like this:

computed: {
    ...mapStateTwoWay({
        notifications: 'setNotifications',
        email: 'setEmail',
    }),

    // Namespaced
    ...mapStateTwoWay('namespace', {
        notifications: 'setNotifications',
        email: 'setEmail',
    }),
}
Related