In angular, I have an object that will be exposed across my application via a service.
Some of the fields on that object are dynamic, and will be updated as normal by bindings in the controllers that use the service. But some of the fields are computed properties, that depend on the other fields, and need to be dynamically updated.
Here's a simple example (which is working on jsbin here). My service model exposes fields a
, b
and c
where c
is calculated from a + B
in calcC()
. Note, in my real application the calculations are a lot more complex, but the essence is here.
The only way I can think to get this to work, is to bind my service model to the $rootScope
, and then use $rootScope.$watch
to watch for any of the controllers changing a
or b
and when they do, recalculating c
. But that seems ugly. Is there a better way of doing this?
A second concern is performance. In my full application a
and b
are big lists of objects, which get aggregated down to c
. This means that the $rootScope.$watch
functions will be doing a lot of deep array checking, which sounds like it will hurt performance.
I have this all working with an evented approach in BackBone, which cuts down the recalculation as much as possible, but angular doesn't seem to play well with an evented approach. Any thoughts on that would be great too.
Here's the example application.
var myModule = angular.module('myModule', []);
//A service providing a model available to multiple controllers
myModule.factory('aModel', function($rootScope) {
var myModel = {
a: 10,
b: 10,
c: null
};
//compute c from a and b
calcC = function() {
myModel.c = parseInt(myModel.a, 10) * parseInt(myModel.b, 10);
};
$rootScope.myModel = myModel;
$rootScope.$watch('myModel.a', calcC);
$rootScope.$watch('myModel.b', calcC);
return myModel;
});
myModule.controller('oneCtrl', function($scope, aModel) {
$scope.aModel = aModel;
});
myModule.controller('twoCtrl', function($scope, aModel) {
$scope.anotherModel = aModel;
});
Although from a high level, I agree with the answer by bmleite ($rootScope exists to be used, and using $watch appears to work for your use case), I want to propose an alternative approach.
Use $rootScope.$broadcast
to push changes to a $rootScope.$on
listener, which would then recalculate your c
value.
This could either be done manually - i.e. when you would be actively changing a
or b
values, or possibly even on a short timeout to throttle the frequency of the updates. A step further from that would be to create a 'dirty' flag on your service, so that c
is only calculated when required.
Obviously such an approach means a lot more involvement in recalculation in your controllers, directives etc - but if you don't want to bind an update to every possible change of a
or b
, the issue becomes a matter of 'where to draw the line'.