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.
You can call a method from computed properties or watchers.
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.
vue-async-computed. With this plugin, you can have computed properties in Vue that are computed asynchronously.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With