Why is the Vue.js input value not updating?

HartleySan picture HartleySan · May 20, 2019 · Viewed 16.5k times · Source

I have a Vue.js text-input component like the following:

<template>
    <input
        type="text"
        :id="name"
        :name="name"
        v-model="inputValue"
    >
</template>

<script>
    export default {
        props: ['name', 'value'],
        data: function () {
            return {
                inputValue: this.value
            };
        },
        watch: {
            inputValue: function () {
                eventBus.$emit('inputChanged', {
                    type: 'text',
                    name: this.name,
                    value: this.inputValue
                });
            }
        }
    };
</script>

And I am using that text-input in another component as follows:

<ul>
    <li v-for="row in rows" :key="row.id">
        <text-input :name="row.name" :value="row.value">
        </text-input>
    </li>
</ul>

Then, within the JS of the component using text-input, I have code like the following for removing li rows:

this.rows = this.rows.filter((row, i) => i !== idx);

The filter method is properly removing the row that has an index of idx from the rows array, and in the parent component, I can confirm that the row is indeed gone, however, if I have, for example, two rows, the first with a value of 1 and the second with a value of 2, and then I delete the first row, even though the remaining row has a value of 2, I am still seeing 1 in the text input.

Why? I don't understand why Vue.js is not updating the value of the text input, even though the value of value is clearly changing from 1 to 2, and I can confirm that in the parent component.

Maybe I'm just not understanding how Vue.js and v-model work, but it seems like the value of the text input should update. Any advice/explanation would be greatly appreciated. Thank you.

Answer

kots picture kots · May 21, 2019

You cannot mutate values between components like that.

Here is a sample snippet on how to properly pass values back and forth. You will need to use computed setter/getter. Added a button to change the value and reflect it back to the instance. It works for both directions.

<template>
    <div>
        <input type="text" :id="name" v-model="inputValue" />
        <button @click="inputValue='value2'">click</button>
    </div>
</template>
<script>
    export default {
        props: ['name', 'value'],
        computed: {
            inputValue: {
                get() {
                    return this.value;
                },
                set(val) {
                    this.$emit('updated', val);
                }
            }
        }
    }
</script>

Notice that the "@updated" event updates back the local variable with the updated value:

    <text-input :name="row.name" :value="row.value" @updated="item=>row.value=item"></text-input>