Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computed function being called twice in vuejs app

Tags:

vuejs2

I'm learning some VueJS by implementing a simple shopping cart.

Here's my HTML:

<div id="app">
  <table>
    <thead>
      <tr>
        <th>Product</th>
        <th>Unit price</th>
        <th>Quantity</th>
        <th>Total</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(cartItem, index) in order.cartItems">
        <td>{{ cartItem.productName }}</td>
        <td>{{ cartItem.unitPrice }}</td>
        <td>{{ cartItem.quantity }}</td>
        <td>{{ lineTotal[index] }}</td>
      </tr>
      <tr class="shipping">
        <td>Shipping</td>
        <td>{{ shipping }}</td>
        <td>{{ totalItems }}</td>
        <td></td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td colspan="3">TOTAL</td>
        <td></td>
      </tr>
    </tfoot>
  </table>
</div>

Here's the JS:

const shipping = 1.5;

const app = new Vue({
  el: '#app',
  data: {
    shipping: shipping.toFixed(2),
    order: []
  },
  created() {
    fetch('https://api.myjson.com/bins/1fk6ua')
      .then(response => response.json())
      .then(json => {
      this.order = json.order
    })
  },
  computed: {
    lineTotal: function () {
      return this.order.cartItems.map(function (cartItem) {
        return (cartItem.unitPrice * cartItem.quantity).toFixed(2);
      });
    },
    totalItems: function(){
      return this.order.cartItems.reduce(function (totalItems, cartItem) {
        return totalItems + parseInt(cartItem.quantity);
      }, 0);
    }
  }
});

Here's a Fiddle: https://jsfiddle.net/8fg70ud2/.

As you can see I'm only part way through the implementation. I've got the product line totals working by implementing a computed function called lineTotal. Now I'm trying to get the shipping line working by first getting the number of products in the cart (which will ultimately be multiplied by the shipping constant). I've got as far as implementing a totalItems function which seems to do the job, but I notice there's now a console error:

TypeError: Cannot read property 'reduce' of undefined

Digging a bit deeper, it seems that the totalItems function is being called twice; the first time this.order.cartItems is undefined hence the reduce call is erroring.

Why is this happening? I may well be going about all of this in the wrong way so am open to suggestions as to how to move forward; I'm at the very start of the learning curve :)

Thanks.

like image 500
Dan Avatar asked Jun 04 '18 19:06

Dan


People also ask

Can you call a computed function Vuejs?

You can call a method from computed properties or watchers.

How does computed work in Vuejs?

In Vue. js, computed properties enable you to create a property that can be used to modify, manipulate, and display data within your components in a readable and efficient manner. You can use computed properties to calculate and display values based on a value or set of values in the data model.

Can computed be async Vue?

vue-async-computed. With this plugin, you can have computed properties in Vue that are computed asynchronously.

What is the difference between watchers and computed property in Vuejs?

Computed properties are used to calculate the value of a property based on some other conditions. Watchers, on the other hand, are not primarily used for changing the value of a property; instead, they are used to notify you when the value has changed and let you perform certain actions based on these changes.


1 Answers

When the Vue instance is created, it has a property order which you initialized to []. In your v-for you try to get order.cartItems which also results in undefined but you don't notice any problem there, because Vue bails out from creating the structure inside v-if never hitting your another computed property lineTotal.

But, totalItems is computed right away. With order still an []. order.cartItems is undefined. Which obviously does not have reduce method. Hence, the error.

Then, in the created callback, you actually fetch the data and populate the order property with the actual object. Since order property is the dependency of the computed property totalItems and it has updated, Vue will compute the property again and this time order property has the correct data structure. And, things will go fine in the second run.

The solution is quite simple. Wait until your order property is populated by using v-if="order.cartItems" in the containing tr element with the class .shipping.

like image 141
Prashant Avatar answered Oct 19 '22 14:10

Prashant