i'm trying to loop through array of objects to make a list of input field, everything works fine except v-model, it just appears in the text-field but nothing changed, i've seen a lot of similar questions here but none of the answers worked
my root component:
<template>
<div class="container">
<add-product :inputs="inputs"></add-product>>
</div>
</template>
<script>
export default {
data(){
return{
inputs: [
{id: '1', label: 'name', type: 'text', model:'name'},
{id: '2', label: 'description', type: 'text', model: 'desc'},
{id: '3', label: 'price', type: 'text', model: 'price'},
{id: '4', label: 'size', type: 'text', model: 'size'},
{id: '5', label: 'stock', type: 'text', model: 'stock'},
]
}
},
components: {
addProduct: AddProduct
}
}
Nested component:
<template>
<transition name="fade">
<div class="add-box">
<div class="input--form" v-for="(input, index) in inputs" :key="index">
<label >{{ input.label }}: </label>
<input :type="input.type" v-model="input.model">
</div>
</div>
</transition>
</template>
<script>
export default {
props: {
inputs: Array
},
data(){
return{
name: '',
desc: '',
price: '',
size: '',
stock: ''
}
},
</script>
The main reason nothing happens is because you don't emit an event called input from the child component. v-model
is just a shortcut for binding a value and emitting an event, thus implementing the two-way data binding with a single directive (see here).
However, you shouldn't try to directly modify the prop in the children components. It is a highly recommended practice to not modify any value without the parent knowing about it.
In general, whenever you need to iterate over values, rather than making a component that handles the whole array, it is often better to have a component handle a single value out of the array. The way you named the child component, AddProduct, suggests that you also wanted to it like this in a way. So I would suggest the following modifications.
Nested component
<template>
<transition name="fade">
<div class="add-box">
<div class="input--form">
<label>{{ input.label }}: </label>
<input :type="input.type" :value="input.model" @input="onInput" />
</div>
</div>
</transition>
</template>
<script>
export default {
props: {
input: Object,
},
methods: {
onInput(event) {
this.$emit("input", {
...this.input,
model: event.target.value,
});
},
},
};
</script>
Parent component
<template>
<div id="app">
<add-product
v-for="(input, index) in inputs"
:key="input.id"
:input="input"
v-model="inputs[index]"
></add-product>
</div>
</template>
<script>
import AddProduct from "./AddProduct";
export default {
name: "App",
components: {
AddProduct,
},
data() {
return {
inputs: [
{ id: "1", label: "name", type: "text", model: "name" },
{ id: "2", label: "description", type: "text", model: "desc" },
{ id: "3", label: "price", type: "text", model: "price" },
{ id: "4", label: "size", type: "text", model: "size" },
{ id: "5", label: "stock", type: "text", model: "stock" },
],
};
},
};
</script>
The child component doesn't modify the prop, only emits the input object with updated value. Since the name of the event is input and the prop's name is value, v-model
will work. In the parent the inputs array is iterated and for each input the v-model
directive is attached. Find a live demo here