I was recently in a situation in one of my AngularJS projects where I wanted to perform an action if the content of an array assigned to a property on the scope changed (e.g. if an item was added or removed). It took a few attempts to get the result I wanted…
Attempt 1: $scope.$watch('myArray', function () { ... });
This didn’t work because the array assigned to the myArray
property on the scope wasn’t changing, only its contents, so the listener didn’t get called. As the documentation explains:
The
listener
is called only when the value from the currentwatchExpression
and the previous call towatchExpression
are not equal… The inequality is determined according toangular.equals
function.
The $watch
method can take a third parameter, though:
$watch(watchExpression, [listener], [objectEquality]);
so I tried that.
Attempt 2: $scope.$watch('myArray', function () { ... }, true);
However, this blew up spectacularly in my case due to the complexity of the objects in the array I was watching:
RangeError: Maximum call stack size exceeded
...
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Turns out that for what I wanted to do I could use $watchCollection
instead:
$watchCollection(obj, listener);
Shallow watches the properties of an object and fires whenever any of the properties change (for arrays, this implies watching the array items; for object maps, this implies watching the properties).
Attempt 3: $scope.$watchCollection('myArray', function () { ... });
Success!
Note: Syntax for getting the item to be watched
The first parameter to $watch
can be a string (evaluated as an expression) or a function.
The
watchExpression
is called on every call to$digest()
and should return the value that will be watched
So given the following setup
$scope.currentValue = 1;
$scope.getCurrentValue = function () { return $scope.currentValue; };
any of the following will work
$scope.$watch('currentValue', function () { ... });
$scope.$watch('getCurrentValue()', function () { ... });
$scope.$watch(function(scope) { scope.getCurrentValue(); }, function () { ... });