AngularJS: Watching a collection

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 current watchExpression and the previous call to watchExpression are not equal… The inequality is determined according to angular.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 () { ... });

About Jennifer Phillips Campbell

Software Developer and Medieval Historian
This entry was posted in AngularJS. Bookmark the permalink.

Leave a comment