Search code examples
angularjsangularjs-service

AngularJS data in service trigger a calculate function


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
    }
}]);

Solution

  • 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.