0

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>
  • Your `v-model` should be on the parent component, not on the children. Keep your `inputs` prop on the children but you need to add a `this.$emit` to send the prop back to the parent (from the children). – kissu Jan 09 '21 at 16:17

1 Answers1

0

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

tao
  • 82,996
  • 16
  • 114
  • 150
Dan
  • 473
  • 3
  • 13