I have a multi-step wizard that binds to data within a service (wizardStateSvc). I want to have a totals element on the screen that updates whenever base values for the wizard that affect the total are updated. The calculation is a bit complex (more than shown here on my sample) so I want to have it performed in the controller. I figured a $watch would work for this but what is occuring is that the $watch function is being called once during initialization and never triggering as I update items. How do I get this $watch to trigger properly?
Totals controller:
myApp.controller('wizardTotalsCtrl', ['wizardStateSvc', '$scope', function (wizardStateSvc, $scope) {
$scope.products= wizardStateSvc.quote.products;
$scope.$watch(function() { return wizardStateSvc.quote.products; }, function(products) {
var total= 0;
products.forEach(function(product) {
total += product.price * (1 - (product.dealerDiscount * 0.01)) * product.quantity;
});
$scope.baseTotal = total;
});
}])
State Service:
myApp.service("wizardStateSvc", [function () {
var quote = {
products: [],
options: {},
customer: {},
shipping: {},
other: {}
}
return {
quote: quote
}
}]);
If the only thing that can change is the contents of the products
array, i.e. products may be inserted or deleted from the array but their price
does NOT change, then use $scope.$watchCollection
. The code would be the same as yours, just replace $watch
with $watchCollection
.
Rationale: $watch
checks for equality; since the products
array itself does not change (i.e. products at time t1 === products at time t2
), the watch is never triggered. On the other hand, $watchCollection
watches the contents of the array, so it is what you want in this case.
If the price of the products may also change you need the costlier $scope.$watch(...,...,true)
. The true
at the 3rd argument means deep watch, i.e. traverse the object hierarchy and check each nested property. Check out the docs.