Search code examples
vue.jsvuex

Computed properties with v-model by index


Let's say I have the following vuex store...

state: {
  someObj: { 
    someList: [
      { key:'a', someSubList: [] },
      { key:'b', someSubList: [] },
      { key:'c', someSubList: [] },
    ]
  }
}

How would I bind a separate v-model to each someSubList? As an example, after I check some checkboxes, I would expect to see some Ids be populated into the someSubList like this:

    someList: [
      { key:'a', someSubList: [1, 13, 17, 19] },
      { key:'b', someSubList: [1, 2, 3, 4] },
      { key:'c', someSubList: [4, 16, 20] },
    ]

In other words, If I check a checkbox an associated id would be added to someSubList. If I uncheck the box, the id associated with that checkbox would be removed from the someSubList. Keep in mind that each someList has a different someSubList.

I'm thinking it would be similar to below, but I'm not sure what to use for the v-model param and how to pass the index to the set method

ex.

<span v-for="(someListRow.someSubList, index2) in someList" v-bind:key="index2">
  <v-checkbox v-model="myModel" />
</span>

computed: {
 someList: {
    get() {
      return this.$store.state.someObj.someList;
    }, 
    set(value) {
      this.$store.commit('someCommit', value)
    }
  }
}

UPDATE:

For anyone interested I got it solved using the tips provided in the posts below and ended up doing this:

<v-checkbox @change="myChangeMethod($event, myObj)" label="MyLabel" 
 :input-value="isMyObjSelected(myObj)" />

myChangeMethod(event, myObj) {
  if (event) {
    this.$store.commit('AddToMyList', {myObj});
  } else {
    this.$store.commit('RemoveFromMyList', {myObj});
  }
}
isMyObjSelected(myObj){
    this.$store.getters.isMyObjSelected(myObj});
}

Solution

  • I believe you want to map your inputs to some value in your store? For this to work you cannot use v-model. Instead work with a input="updateStore($event, 'pathToStoreField')" (or @change="...") listener and a :value="..." binding. In case of a checkbox you need to use :checked="..." instead of value.

    For example:

    <input type="checkbox" :checked="isChecked" @input="updateField($event.target.checked, 'form.field')">
    
    ...
    computed:
    ...
      isChecked() {
        return this.$store.state.form.field;
      },
    ...
    methods: {
      ...
      updateField(value, path) {
        const options = { path, value };
        this.$store.commit('setFieldByPath', options);
      },
      ...
    },
    

    Then in your store you will need a mutation setFieldByPath that resolves the string-path to a property in the state object (state.form.field) and sets this property to value.

    You can also place the updateField() method as setter of a computer property.

    There is library that makes this a bit more convenient: https://github.com/maoberlehner/vuex-map-fields

    Just look out for checkboxes: to set them checked the checked property needs to be true not the value property.