Home > Software design >  Vue 3 How update sum of all subtotals on change
Vue 3 How update sum of all subtotals on change

Time:01-07

I have an app that collects data from a form then pushes a new counter object to an array (counters) then renders a counter component that can increment and decrement to update a subtotal property for that counter.

I am struggling with how to render a grandTotal of all subtotals as they change. I tried using a computed property but it only renders subtotal from last counter added.

This is my AddCounters component

<template>
  ... form
    <div >
      <p >Total Price {{ getTotals }}</p>
    </div>
  </div>
  <div >
    <ul >
      <li
        
        v-for="(counter, index) in counters"
        :key="index"
      >
        <Counter :counter="counter" />
      </li>
    </ul>
  </div>
</template>

<script>
import Counter from "./Counter.vue";
export default {
  data() {
    return {
      counters: [],
      grandTotal: 0,
      price: "",
      type: "",
      location: "",
      quantity: 0,
      counterId: 0,
      subtotal: null,
    };
  },
  components: {
    Counter,
  },
  methods: {
    addCounter() {
      let newCounter = {
        id: this.counterId   1,
        type: this.type,
        location: this.location || "In & Out",
        price: this.price,
        quantity: this.quantity,
        subtotal: this.subtotal,
      };

      if (this.price !== "" && this.type !== "") {
        this.counters.push(newCounter);
      }

      this.price = "";
      this.type = "";
      this.location = "";
      this.quantity = 0;
      this.subtotal = null;

      this.counterId = newCounter.id;
    },
  },
  computed: {
    //This only works on last counter rendered
    getTotals: function () {
      const grandTotals = this.counters
        .map((counter) => {
          console.log("Subtotals", counter.subtotal);
          return counter.subtotal;
        })
        .reduce((prev, curr) => (prev = curr), 0);
      return grandTotals;
    },
  },
};
</script>

This is what my counter component looks like.

<template>
  ... UI methods below are wired up
    <div >
      <label >Subtotal</label>
      <span id="" >{{ counter.subtotal }}</span>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    counter: Object,
  },
  methods: {
    deleteCounter() {
      console.log("Delete Counter Called");
    },
    incrementQty(counter) {
      counter.quantity  = 1;
      this.getSubtotal(counter);
    },
    decrementQty(counter) {
      if (counter.quantity > 0) {
        counter.quantity -= 1;
        this.getSubtotal(counter);
      } else {
        counter.quantity = 0;
      }
    },
    getSubtotal: function (counter) {
      let count = counter.quantity;
      let price = counter.price;
      let subTotal = count * price;
      counter.subtotal = subTotal.toFixed(2);
    },
  },
};
</script>

CodePudding user response:

getTotals: function () {
      const grandTotals = this.counters
        .map((counter) => {
          console.log("Subtotals", counter.subtotal);
          return counter.subtotal;
        })
        .reduce((prev, curr) => (prev = curr), 0);
      return grandTotals;
    },

In the reduce above, you're setting the prev to be equal to the current value. In other words, you're overwriting the value every time, not doing addition.

Instead you should be adding to prev:

getTotals: function () {
      const grandTotals = this.counters
        .map((counter) => {
          console.log("Subtotals", counter.subtotal);
          return counter.subtotal;
        })
        .reduce((prev, curr) => prev  = curr, 0);
      return grandTotals;
    },

You may also condense the map and reduce like so:

getTotals: function () {
      const grandTotals = this.counters
        .reduce((prev, counter) => prev  = counter.subtotal, 0);
      return grandTotals;
    },

Edit

counter.subtotal is a String, so it needs to be cast a a Number before doing addition.

getTotals: function () {
      const grandTotals = this.counters
        .reduce((prev, counter) => prev  = Number(counter.subtotal), 0);
      return grandTotals;
    },
  •  Tags:  
  • Related