I'm new to using VUE.Js, and i created a very simple app to try out how it works.
The problem happens immediately where when i run the app, the watch for a variable is triggered in an infinite loop. I cannot figure out why. There is a v-for loop but that is on an array that only has two elements.
Initially the SubTotal should be 0. But as soon as the app is run, it triggers the Buy method, even though i haven't clicked the buy button and the sub total ends up being 442.37999999999965.
Thanks for any help.
Here is the jsfiddle Beer shopping cart
HTML :
<div id = "growler">
<table>
<tr>
<th style='width:150px'>Beer</th>
<th style='width:50px'>Price</th>
<th style='width:30px'></th>
</tr>
<tr v-for = "beer in beers">
<td>{{ beer.name }}</td>
<td>{{ beer.price }}</td>
<td>
<button :click="buy(beer)">buy</button>
</td>
</tr>
<tr>
<td>SubTotal</td>
<td>{{subTotal}}</td>
<td></td>
</tr>
</table>
</div>
JS:
new Vue({
el: "#growler",
data: {
beers: [
{name: 'Ahool Ale', price: 2.00},
{name: 'Agogwe Ale', price: 2.38}
],
shoppingCart: [],
subTotal: 0.00
},
watch: {
shoppingCart: function() {
console.log('shopping cart watch triggered');
this.updateSubTotal();
}
},
methods: {
updateSubTotal: function () {
var s=this.shoppingCart.length;
var t=0;
for (var i=0;i<s; i++){
t += this.shoppingCart[i].price;
}
this.subTotal = t;
},
buy: function (beer) {
console.log('beer pushed on array');
this.shoppingCart.push(beer);
}
},
beforeCreate: function() {
console.log('beforeCreate');
},
created: function() {
console.log('created');
},
beforeMount: function() {
console.log('beforeMount');
},
mounted: function() {
console.log('mounted');
},
beforeUpdate: function() {
console.log('beforeUpdate');
},
updated: function() {
console.log('updated');
},
beforeDestroy: function() {
console.log('beforeDestroy');
},
destroyed: function() {
console.log('afterDestroy');
}
});
I found your mistake:
<button :click="buy(beer)">buy</button>
You used :
(v-bind
) instead of @
(v-on:
) on the click handler.
When you first bind it, the function is called once and updates the shoppingCart
. This will update the subTotal
data, which will force a re-render of the DOM, which will trigger the buy
function again because of the :bind
.
Fix:
<button @click="buy(beer)">buy</button>
<!-- or -->
<button v-on:click="buy(beer)">buy</button>
Suggested changes for your code:
Use computed properties instead of a method to update a property that represents a sum of other values:
new Vue({
el: "#growler",
data: {
beers: [{
name: 'Ahool Ale',
price: 2.00
},
{
name: 'Agogwe Ale',
price: 2.38
}
],
shoppingCart: []
},
watch: {
shoppingCart: function() {
console.log('shopping cart watch triggered');
}
},
computed: {
subTotal: function() {
return this.shoppingCart.reduce(function(total, beer) {
return total + beer.price;
}, 0);
}
}
},
methods: {
buy: function(beer) {
this.shoppingCart.push(beer);
}
},
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="growler">
<button>buy</button>
<table>
<tr>
<th style='width:150px'>Beer</th>
<th style='width:50px'>Price</th>
<th style='width:30px'></th>
</tr>
<tr v-for="beer in beers">
<td>{{ beer.name }}</td>
<td>{{ beer.price }}</td>
<td>
<button @click="buy(beer)">buy</button>
</td>
</tr>
<tr>
<td>SubTotal</td>
<td>{{subTotal}}</td>
<td></td>
</tr>
</table>
</div>
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