Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why v-model doesn't work with an array and v-for loop?

I got a custom select component, it works with a simple variable, but when used with v-for it won't work:

https://jsfiddle.net/7gjkbhy3/19/

<select2 v-for="item, index in samples" v-model="item" ></select2>
data : { samples : [0, 0, 0]}

This works:

<select2 v-model="sample"></select2>
data : { sample : 0}  

what am I missing here?

like image 890
AngeloC Avatar asked Mar 25 '17 08:03

AngeloC


People also ask

What is the difference between V-model and V bind?

v-bind is a one-way binding. v-model is used for binding form elements like inputs, radio buttons, textarea, checkboxes. It is used for binding data, attributes, expressions, class, styles. V-model is input value sensitive.

Can you have multiple V models?

With the release of Vue 3 now we can add multiple v-model for two-way data binding in a more standardized way on the same component with more flexibility. As given in Vue 3 document the syntax of using v-model on a custom component is similar by-passing modelValue as a prop and emitting an update:modelValue event.

Can I use V-model with Div?

You can use v-model with a custom Vue component by accepting a prop named 'value' and emitting an event named 'input'. For example, the below custom component is a fake select using div elements. Clicking on a div selects it.

What does V-model lazy do?

lazy. By default, v-model syncs with the state of the Vue instance (data properties) on every input event - which means every single time the value of our input changes. The . lazy modifier changes our v-model so it only syncs after change events.


2 Answers

v-model and v-for do NOT go together well if v-model is used to an iteration alias w/ a primitive value.

The Vue warns:

You are binding v-model directly to a v-for iteration alias. This will not be able to modify the v-for source array because writing to the alias is like modifying a function local variable. Consider using an array of objects and use v-model on an object property instead.

Therefore using an array of objects each of which has a property for the select value would solve the issue:

WORKING EXAMPLE.

<select2 v-for="item, index in samples" v-model="item.value" ></select2>
new Vue({
     el: '#app',
     data: {
         sample: 0,
         samples : [{ value: 0 }, { value: 0 }, { value: 0 }]
     }
 })
like image 116
Hashem Qolami Avatar answered Oct 14 '22 17:10

Hashem Qolami


I don't like the idea of having to change the view model to resolve a framework design constraint. What if the model is to be sent to your backend via an API call? It would involve an additional step of having to mutate the model.

My solution to this was to create a Vue component that boxes the value at each array index into an object that can be referenced within it's slot. It then reacts to the data-changing by updating the array at the specified index via a watcher.

boxed-value.vue

<template>
    <div>
        <slot v-bind:item="boxedItem"></slot>
    </div>
</template>

<script>
    export default {
        props: {
            array: {
                type: Array,
                required: true
            },
            index: {
                type: Number,
                required: true
            }
        },
        data() {
            var data = {
                boxedItem: {value: this.array[this.index]}
            }

            return data
        },
        created(){

        },
        watch: {
            'boxedItem.value': function(oldValue, newValue) {
                // console.log('Array item at index ' + this.index + ' value changed from ' + oldValue + ' to ' + newValue)
                this.array[this.index] = newValue
            }
        }
    }
</script>

Example

<div v-for="(name, index) in primitiveValues" :key="index">
    <boxed-value :array="primitiveValues" :index="index">
        <template slot-scope="{item}">
            <el-input v-model="item.value"></el-input>
        </template>
    </boxed-value>
</div>
like image 20
Jamie Pearcey Avatar answered Oct 14 '22 16:10

Jamie Pearcey