Search code examples
vue.jsvuetify.jsvuexv-select

Vuetify combobox item template is not updating with vuex


I am using Vuetify combobox with item and selection slots. My vuex store is updating when I check uncheck the items. But if I remove one of the selected items from the store then selection slot updates but item slot does not.

Below is the code pen. What am I missing?

https://codepen.io/mjchaudhari/pen/xxRVavx?editors=1011

<div id="app">
  <v-app id="inspire">
    <v-container fluid>
      <v-combobox
        v-model="values"
        :items="items"
        label="Select Item"
        multiple
      >
        <template v-slot:selection="{ item, index }">
          <v-chip v-if="index <= 1">
            <span>{{ item }}</span>
          </v-chip>
          <span
            v-if="index === 2"
            class="grey--text caption"
          >
            (+{{ values.length - 2 }} others)
          </span>
        </template>
        <template v-slot:item="{ active, item, attrs, on }">
          <v-list-item v-on="on" >
            <v-list-item-action>
              <v-checkbox :input-value="active"></v-checkbox>
            </v-list-item-action>
            <v-list-item-content>
              {{item.id}} - {{item.name}}
            </v-list-item-content>
          </v-list-item>
        </template>
      </v-combobox>
    </v-container>
    <div v-for="v in values">
      <span>{{v.name}}</span> <v-btn v-on:click="deleteVal(v)">X</v-btn>
    </div>
  </v-app>
  
</div>


const store = new Vuex.Store({
  state: {
    items: [
      {id: 1, name:'foo'}, {id: 2, name:'bar'}, {id: 3, name:'fizz'},
      {id: 4, name:'buzz'}, {id: 5, name:'fizzbuzz'}, {id: 6, name:'foobar-foo'}
    ],
    values: []
  },
  mutations: {
    'update-values': function(state, values=[]) {
      state.values = values
    }
  }
})
import { mapState, mapMutations } from "https://cdn.skypack.dev/vuex"
new Vue({
  el: '#app',
  store,
  vuetify: new Vuetify(),
  data: () => ({
  }),
  computed: {
    ...mapState({
      items: state => state.items,
      values: state => state.values
    }),
    values: {
      get: function () {
        return this.$store.state.values
      },
      set: function (val) {
        this.updateSelectedVal(val)
      }
    }
  },
  methods: {
    ...mapMutations({
      updateSelectedVal: 'update-values'
    }),
    deleteVal(val) {
      let idx = this.values.findIndex(v=> v.id === val.id)
      let vals = [...this.values]
      vals = vals.splice(idx,1)
      console.log(vals)
      this.updateSelectedVal(vals)
      
    }
  }
  
})

Solution

  • You either make a copy of the values when you update the ones in state:

    mutations: {
      'update-values': function(state, values=[]) {
        state.values = [...values];
      }
    }
    

    Or, an alternative, simpler and better solution is not to splice a copy, but splice the original (the state's values), in a delete-value mutation:

    mutations: {
      'delete-value': function(state, index) {
        state.values.splice(index, 1);
      }
    }
    

    and, obviously, call it as:

    deleteVal (val) {
      let idx = this.values.findIndex(v=> v.id === val.id);
      if (index > -1) {
        this.$store.commit('delete-value', index)
      }
    }
    

    Even simpler, just map the delete-value mutation and call it with key from the markup: https://codepen.io/andrei-gheorghiu/pen/xxRVQPp?editors=1011


    Side note: Your :input-value condition seems wrong. It should be:

    <v-checkbox :input-value="values.includes(item)"></v-checkbox>
    

    I fixed it in my version, but in yours the displayed selection gets out of sync with the actual selection when you click the label of the checkbox.