Search code examples
typescriptvue.jsvuex

How to use v-model with v-for array of strings in Vuex store?


I am trying to set value in to Array in Vuex Store, but caught an error

VueCompilerError: v-model cannot be used on v-for or v-slot scope variables because they are not writable.

Is there any way to realize this? (Without creating local copy of Vuex model array in component)

My component.vue template

<q-input v-for="phone, index  in CompanyPhones" :key="index" v-model="phone" mask="(##) ### ## ##" stack-label label="Phone" />

My component.vue scripts

setup () {
    const $store = useStore()
 const CompanyPhones = computed({
      get: () => $store.getters['CompanySettingsModule/getCompanyPhones'],
      set: (val) => {
        $store.commit('CompanySettingsModule/setCompanyPhones', val)
      }
    })
 return {  CompanyPhones, }
}

Vuex module state.ts

function state (): CompanySettingsInterface {
  return {
    CompanyPhones: ['', '', ''],
  }
}

Vuex module mutations.ts function

setCompanyMails (state: CompanySettingsInterface, val)  { state.CompanyMails = val },

Vuex module getters.ts function

getCompanyPhones (state: CompanySettingsInterface)  { return state.CompanyPhones  },

Solution

  • Problem is your computed getter/setter only accepts arrays, and updating a nested value (like companyPhones[index]) will have the same limitations as Vue's standard reactivity. Basically meaning you'll have to replace the array with a clone of itself whenever a child is updated directly. You could do this in a method:

    //script
    methods: {
      updateCompanyPhone(v, index) {
        const { companyPhones } = this; // destructuring ES6 syntax
        companyPhones[index] = v;
        this.companyPhones = companyPhones;
      }
    }
    
    //template
    <q-input v-for="phone, index  in CompanyPhones" :key="index" :model-value="phone" @update:model-value="v => updateCompanyPhone(v, index)" mask="(##) ### ## ##" stack-label label="Phone" />
    

    Since v-model is only syntactic sugar you can destructurize it into the :model-value+@update:model-value (Vue3) it represents and have more control over what it does.

    If this pattern reoccurs often, you can write a Higher Order Component that maintains an Array with nested values (via value prop and update:value emit, so it works with v-model), with a slot to provide it with a custom form(field) to update its nested values (and maybe some splice/push functions to add/remove items to the array).