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