Reputation: 2569
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 ...
Upvotes: 50
Views: 57886
Reputation: 1
I know this is a suuuper late response, but might be useful for someone
Another option if you want to trigger the watcher only on when an element is added to or removed from the array is
$scope.$watch('filters.length', ...)
Take into account this will not trigger when
$scope.filters = []
$scope.filters = $scope.filters.filter(...)
or any other option that replaces the array reference.
Upvotes: 0
Reputation: 4611
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.
Upvotes: 42
Reputation: 41
$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
}
});
Upvotes: 4
Reputation: 28120
$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/
Upvotes: 202