I'm having a hard time with using v-model in a chained fashion.
Essentially I have a Parent component that can have many Child instances. Child works fine alone and I want Parent to keep track of all values of Child and spit out an array of the values with some custom formatting.
It kind of works, but removing a child has some weird behaviour.
When I remove the first entry, the data is the expected ['']. However visually I see the wrong child being removed. Any ideas what is going on?
Child.vue
<script setup>
import { ref, watch, defineProps } from 'vue';
const { modelValue } = defineProps(['modelValue']);
const emitUpdate = defineEmits(['update:modelValue']);
const category = ref(0);
const foods = ref(0);
// Emit new value when user picks a new food
watch(foods, () => {
emitUpdate('update:modelValue', `${category.value} ${foods.value}`);
});
</script>
<template>
<div>
<select v-model="category">
<option value="Fruits">Fruits</option>
<option value="Pasta">Pasta</option>
</select>
<select v-model="foods">
<option value="oranges">Oranges</option>
<option value="lasagna">Lasagna</option>
</select>
</div>
</template>
Parent.vue
<script setup>
import Child from './Child.vue';
import { ref, defineProps } from 'vue';
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
const children = ref([]);
function addChild() {
children.value.push({ value: '' });
}
function removeChild(index) {
children.value.splice(index, 1);
emit('update:modelValue', children.value.map(child => child.value));
}
function updateChildValue(index, value) {
children.value[index].value = value;
emit('update:modelValue', children.value.map(child => child.value));
}
</script>
<template>
<div>
<button @click="addChild">Add child</button>
<div v-for="(child, index) in children" :key="index">
<Child v-model="child.value" @update:modelValue="updateChildValue(index, $event)" />
<button @click="removeChild(index)">Remove child</button>
</div>
</div>
</template>
Usage
import Parent from '@/components/Parent.vue';
const myParentValue = ref('')
<template>
...
<p>PARENT</p>
<Parent v-model="myParentValue"></Parent>
<p>myParentValue: {{ myParentValue }}</p>
...
</template>
...


Do not use index as your
v-forkey. The key should be a unique value. When you have an array where elements are being added and removed, the item at each index can change, meaning the index is not a unique identifier for the given item. This is the reason the UI is not updating correctly. Vue is not able to properly diff the changes to the array based on the key.I suggest adding a unique
idfield when adding a new child. This is one possible simple way of doing it:Then just update your v-for:
Two other non-critical issues spotted:
First, in Child.vue:
You shouldn't destructure
defineProps. You can lose reactivity this way. Always opt to assign the return value ofdefinePropsto a variable, e.g.const props = defineProps. This isn't an issue in your code at the moment because you don't actually use modelValue in your Child component... You can remove this prop and thev-modelon the<Child>element in the parent if you want to since they're not technically doing anything. What's really doing all the work is the emits.Second, and not really an issue, but you don't need to import
defineProps, it is automatically available inside script setupAll corrections can be seen in this Vue Playground example