6

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.

1
  • You better use vuejs.org/v2/api/index.html#beforeCreate rather than #created lifecycle-method. Read carefully how and when create method fires and why you receive error. Commented Jun 4, 2018 at 20:06

1 Answer 1

7

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.