How to use v-model inside the v-for loop in order to access a current object

I have a simple v-for loop that renders inputs with checked state depending on a boolean active property:

This is the model:

const inputs = [
   {
        year: '2012',
        active: false
   },
   {
        year: '2013',
        active: true
   }
]

And this is the template:

<li class="filter__item" v-for="{year, active} in inputs">
    <input class="filter__input" type="checkbox"
           :name="year"
           :id="year"
           :checked="active"
           v-model="active"
    >
</li>

I would expect v-model="active" to hold a reference to an object in a loop, but it doesn’t work this way. When checkboxes are selected/unselected then my model state remains unchanged. Am I doing something wrong or is it a Vue limitation? I also tried to replace v-model="active" with v-model="this", but it doesn’t work either.

I know that I could use $event.target.value together with v-for index in order to access inputs[index] and change the value… but it looks so jQuery-ish. I would feel very bad if I did it this way.

Is there any solution?

1 Like

It works fine if you don’t destructure the argument:

<ul>
  <li v-for="input in inputs">
    {{ input.year }}
    <input type="checkbox" v-model="input.active">
  </li>
</ul>

When you de-structure the argument you are just passing it two primitive values and vue isn’t able to pick up on those values (which are basically local to the loop function).

7 Likes

Thanks. I’ve just found the solution on my own, I was about to write about it, but you were faster :slight_smile:

Additionaly, there was one more reason why it didn’t work. I’ve been using a computed property.

IMO it should be mentioned in the documentation, because this is not so obvious that destructuring can’t be used. It would be great if some message appeared in the js console too.

The Problem is not so much with destructuring itself, and more with the fact that active is now a variable with a copy of the value from the object.

It happens in pure JavaScript as well:

arrray.forEach({active} => active = true)

The above does nothing, for the same reason.

Ok, I see. I thought it’s a Vue-specific issue. I didn’t know that destructuring acts this way under the hood. I thought it’s just a syntactic-sugar.

if inputs are stored in Vuex state, is there a way to bind those properties in strict mode?
How can I apply the computed setter & getter in a v-for loop?

3 Likes

i know its an old topic but i found a couple of ways to use it without any issues

  • we can use the index from the v-for but it might produce some side effects, so 2xcheck b4 settling with it
<li v-for="(item,i) in items" :key="item.id">
    <input v-model="items[i].value">
</li>
  • or we can use a method to do the index lookup if the prev didnt work
<li v-for="item in items" :key="item.id">
    <input v-model="items[getIndex(items, item.id)].value">
</li>
getIndex(list, id) {
  return list.findIndex((e) => e.id == id)
},
6 Likes

This helped me understand it so much better! Thanks!

What if your v-for utilizes a filter function like the second example of filtering in the List rendering docs
https://vuejs.org/v2/guide/list.html#Displaying-Filtered-Sorted-Results. Is there a way to access the return value of the filterItems function? Something like v-for="(item,i) in filterItems(items) as filteredList

I can’t seem to find a good way to do this…

<li v-for="(item,i) in filterItems(items)" :key="item.id">
    <input v-model="items[i].value">
</li>

this wont work because filterItems return a new array with different indexs from the original, so for this you would need an id where u search the original items list and make ur changes.

to keep both syncd, u better use the filteritems as a computed prop like the first example in the link

Thanks for the response, unfortuantely in my situation I can’t use a computed prop because I am dealing with a nested loop. My outer loop I can use a computed prop but not for the inner loop. Something like below. Note the nested v-for needs to pass in the child sections to filter.

<div v-for="(parentSection, index) in filteredParentSectionArray" :key="parentSection.id">
   <h2>{{ parentSection.name }}</h2>
   <div v-for="(childSection, index) in filterSections(parentSection.childSections)" :key="childSection.id">
      <section-editor v-model="childSection" />
   </div>
</div>