/ Gists / V-model in loops and computed
On gists

V-model in loops and computed

Vue.js

App.vue Raw #

/* 
  https://stackblitz.com/edit/superlasice-efzixd?file=src%2FApp.vue 
*/

<template>
  <div id="app">
    <button v-on:click="add">add new row</button>
    <p>Total price {{ total }} | {{ total2 }}</p>

    <ul>
      <li v-for="(item, index) in items">
        Name<br />
        <input type="text" v-model="item.name" />
        <br />

        Quantity<br />
        <input type="number" v-model.number="item.quantity" min="1" />
        <br />

        Price<br />
        <input
          type="number"
          v-model.number="item.price"
          min="0.00"
          max="10000"
          step="1"
        />
        <br />

        Total (readonly) <br />
        <input v-model="totalItems[index]" readonly /> <br />

        total in row:
        {{ totalItem(item) }}
        <br />
        <br />

        <button v-on:click="remove(index)">Delete row</button>
        <hr />
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      items: [
        { name: 'A', quantity: 1, price: 20 },
        { name: 'B', quantity: 2, price: 30 },
      ],
    };
  },
  methods: {
    add() {
      this.items.push({
        name: 'New product',
        quantity: 0,
        price: 0,
      });
    },
    remove(index) {
      this.items.splice(index, 1);
    },
    totalItem(item) {
      return item.price * item.quantity;
    },
  },
  computed: {
    totalItems() {
      let arr = [];
      this.items.forEach((item) => {
        arr.push(parseFloat(item.price) * parseFloat(item.quantity));
      });

      return arr;
    },
    total() {
      let sum = 0;
      this.items.forEach((item) => {
        sum += parseFloat(item.price) * parseFloat(item.quantity);
      });

      return sum;
    },
    // another approach how to sum
    total2() {
      return this.items.reduce((prev, item) => {
        return prev + item.price * item.quantity;
      }, 0);
    },
  },
};
</script>

App2.vue Raw #

/* https://stackblitz.com/edit/vmodel-loop-v2?file=src%2Fcomponents%2FFormRow.vue */

<template>
  <div id="app">
    Celková cena je: {{ totalPrice }}
    <br />
    <button @click="addNewRow">add new row</button>
    <FormRow
      v-for="(item, index) in items"
      :key="item.name"
      v-model="items[index]"
    />
  </div>
</template>

<script>
import FormRow from './components/FormRow';

export default {
  name: 'App',
  components: {
    FormRow,
  },
  data() {
    return {
      items: [
        {
          name: 'A',
          quantity: 1,
          price: 10,
        },
        {
          name: 'B',
          quantity: 2,
          price: 20,
        },
      ],
    };
  },
  computed: {
    totalPrice() {
      let total = 0;
      this.items.forEach((item) => {
        total += item.price * item.quantity;
      });

      return total;
    },
  },
  methods: {
    addNewRow() {
      this.items.push({
        name: null,
        quantity: 0,
        price: 0,
      });
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

FormRow.vue Raw #

<template>
  <div>
    <input v-model="modelValue.name" />
    <input type="number" v-model="modelValue.quantity" />
    <input type="number" v-model="modelValue.price" />
    <input v-model="sum" disabled />
    <hr />
  </div>
</template>

<script>
export default {
  name: 'FormRow',
  props: ['modelValue'],
  computed: {
    sum() {
      return this.modelValue.quantity * this.modelValue.price;
    },
  },
};
</script>

<style>
input {
  width: 100px;
  margin: 5px;
}
</style>