AngularJS $watch vs $watchCollection: which is better for performance?
For watching an object scope variable, is $scope.$watch
with objectEquality
set to true OR $scope.$watchCollection
better?
For a $scope
object variable (like 15 attributes, some nested 2 levels deep) updated with input elements and ng-model
in the view, how bad is $scope.$watch
with objectEquality
set to true
? Is this a big thing to avoid?
Is $watchCollection
a better solution?
I am looking for easy wins to improve performance on my AngularJS App (I'm still stuck on v1.2.2).
// ctrl scope var
$scope.filters = {
name: '',
info: {test: '', foo: '', bar: ''},
yep: ''
// etc ...
}
// ctrl watch ?
$scope.$watch('filters', function(newVal, oldVal) {
if(newVal !== oldVal) {
// call with updated filters
}
}, true);
// or ctrl watch collection ?
$scope.$watchCollection('filters', function(newVal, oldVal) {
if(newVal !== oldVal) {
// call with updated filters
}
});
// view input with ng-model
<input type="text" ng-model="filters.name" />
<input type="text" ng-model="filters.info.test" />
<input type="text" ng-model="filters.yep" />
// etc ...
Solution 1:
$watch() will be triggered by:
$scope.myArray = [];
$scope.myArray = null;
$scope.myArray = someOtherArray;
$watchCollection() will be triggered by everything above AND:
$scope.myArray.push({}); // add element
$scope.myArray.splice(0, 1); // remove element
$scope.myArray[0] = {}; // assign index to different value
$watch(..., true) will be triggered by EVERYTHING above AND:
$scope.myArray[0].someProperty = "someValue";
JUST ONE MORE THING...
$watch() is the only one that fires when an array is replaced with another with the same exact content. For example:
$scope.myArray = ["Apples", "Bananas", "Orange" ];
var newArray = [];
newArray.push("Apples");
newArray.push("Bananas");
newArray.push("Orange");
$scope.myArray = newArray;
Below is a link to an example JSFiddle that uses all the different watch combinations and outputs log messages to indicate which "watches" were triggered:
http://jsfiddle.net/luisperezphd/2zj9k872/
Solution 2:
The
$watchCollection()
function is a sort-of mid-ground between the two$watch()
configurations above. It's more in-depth than the vanilla $watch() function; but, it's not nearly as expensive as the deep-equality$watch()
function. Like the$watch()
function, the$watchCollection()
works by comparing physical object references; however, unlike the$watch()
function, the$watchCollection()
goes one-level deep and performs an additional, shallow reference check of the top level items in the collection.
see this explanation
Solution 3:
$watchCollection
is optimized for vector arrays []
where elements can be push
and $watch
is good for associative arrays objects {}
$watchCollection
will not watch for depth changes, is like watch with objectEquality set to false.
If you already know to structure of the depth you can optimize like this:
// ctrl watch ?
$scope.$watch('filters', function(newVal, oldVal) {
if(newVal !== oldVal) {
// call with updated filters
}
});
// ctrl watch ?
$scope.$watch('filters.info', function(newVal, oldVal) {
if(newVal !== oldVal) {
// call with updated filters
}
});