Razokibe
Razokibe

Reputation: 23

Vue js, how to bind input value from array?

i have a number field, that i want to bind value from an array like this :

<div v-for="p in products">
    <input type="number" :value="carModel[p.name]" />
</div>

where p.name is a string (product name), car_a :

const app = Vue.createApp({
        data() {
            return {
                products: {...},
                carModel:[{car_a:4}, {car_b:2} ]
            }
        }
...

But this does not works, the input remains empty, while following runs without issue :

<div v-for="p in products">
    <input type="number" :value="carModel" />
</div>

const app = Vue.createApp({
        data() {
            return {
                products: {...},
                carModel:4
            }
        }
...

So, my question is, how to bind the value properly from array if i have the key ?

thank you

Upvotes: 2

Views: 5142

Answers (3)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285430

First off, I wonder if your carModel array item object structure could be improved; perhaps better would be something like:

  products: ["car_a", "car_b"],
  carModel: [
    {
      name: "car_a",
      value: 4,
    },
    {
      name: "car_b",
      value: 2,
    },
    {
      name: "car_c",
      value: 3,
    },
  ]

I assume that the Strings held in the products array are a sub-set of the Strings in the carModel array of objects, and since you want an input that is reactive with the data, you will want the input's model to be the values held by each carModel object. So rather than v-for loop over the products array, v-for loop over the carModel but filter out the elements whose Strings are not held by the products array. Since we should not combine v-for with v-if, this filtering should be done in a computed property:

  computed: {
    filteredCarModel() {
      return this.carModel.filter(cm => {
        return this.products.includes(cm.name);
      });
    }
  },

so then in the HTML template you can loop over the computed property:

    <div v-for="cm in filteredCarModel" :key="cm.name">
      <label :for="cm.name">{{ cm.name }} </label>
      <input type="number" :id="cm.name" v-model="cm.value" />
    </div>

This is key:

This will display proper values in the input elements, and these elements will remain reactive to the model such that changes to the inputs will cause changes to the data model, and visa-versa. Thus the display will be truly bound to the data model, which is what you're asking and what Vue.js is all about.

Here is a sample HTML showing that the data is in fact reactive:

enter image description here

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <html>
    <head>
      <meta charset=utf-8" />

      <title>Vue Example</title>
      <script src="https://unpkg.com/vue@next"></script>
    </head>

    <body>
      <h1>Vue Example</h1>
      <div id="app">
        <h2>Show Data</h2>
          <div v-for="cm in carModel" :key="cm.name">
            {{ cm.name }} : {{ cm.value }}
          </div>
        <h2>Change Data</h2>
        <div v-for="cm in filteredCarModel" :key="cm.name">
          <label :for="cm.name">{{ cm.name }} </label>
          <input type="number" :id="cm.name" v-model="cm.value" />
        </div>
      </div>
      <script>
        Vue.createApp({
          data() {
            return {
              products: ["car_a", "car_b"],
              carModel: [
                {
                  name: "car_a",
                  value: 4,
                },
                {
                  name: "car_b",
                  value: 2,
                },
                {
                  name: "car_c",
                  value: 3,
                },
              ],
            };
          },
          computed: {
            filteredCarModel() {
              return this.carModel.filter((cm) => {
                return this.products.includes(cm.name);
              });
            },
          },
        }).mount("#app");
      </script>
    </body>
  </html>
</html>

Note that if the products array is not a subset of the carModel array, if all the Strings present in products are also present in carModel name fields, then there will be no need to have a filteredCarModel() computed property.

Upvotes: 2

Roh&#236;t J&#237;ndal
Roh&#236;t J&#237;ndal

Reputation: 27232

Observations :

  • products should be an array to iterate via v-for.
  • As carModel is an array. You can not access object properties directly. Access it via index.

Working Demo :

new Vue({
  el: "#app",
  data: {
    products: [{name: 'car_a'}, {name: 'car_b'}],
    carModel:[{car_a: 4}, {car_b: 2}]
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="(product, index) in products" :key="index">
    <input type="number" :value="carModel[index][product.name]"/>
  </div>
</div>

Upvotes: 0

Tentaro F
Tentaro F

Reputation: 146

change your carModel to hash

carModel: { 'car_a': 4, 'car_b': 2 }

because what you are trying to do is access array by string(which it should be number as index of the array)

Upvotes: 0

Related Questions