Angular Watchers - What’s the Difference?
TIL what the difference between angular’s watcher offerings are: $watch
, $watchCollection
, $watchGroup
and $watch (objectEquality)
.
Angular watchers facilitate the 2 way data binding between views and controllers / services. Watchers bind listener functions to $scope properties (or expressions that evaluate to $scope properties). Bound $scope
properties are dirty-checked during each $digest()
cycle, and if a change in the property is detected the bound listener function is called.
There are a few different variations of watchers, each with different performance and purposes:
$watch
This is the most basic form of watcher. It accepts an expression as a string (either a $scope
property or expression that evaluates to a $scope
property) and a listener function to be called when the expression changes. This type of watcher only detects changes to shallow aspects of the bound $scope
property (reference equality via ===
comparison). This means if the bound $scope
property is an object, and a value in that object changes, the listener callback will not be called.
Example:
$watchCollection
This registers a watcher for an array or object that is bound to $scope. If any item within the collection changes, including the addition or removal of a new item, the listener is called. Equality is determined using ===
between cycles.
Example:
$watchGroup
The distinction between this watcher and $watchCollection
, is that $watchGroup
takes a group of expressions to watch instead of just one collection object. This can be useful if you need to bind a group of $scope
properties to the same listener callback (instead of needing to write different watchers for them all). For performance reasons, $watchGroup
delays calling the bound listener until the end of the $digest()
cycle, to wait and see if multiple items in the group change during $digest()
so the listener is only called once. $watchGroup
is only available in angular 1.3 onwards, so we don’t use it yet in our application (still on angular 1.2.27).
$watch (objectEquality)
$watch
can take a third boolean argument, which tells the watcher to use angular.equals
to check for deep objectEquality of the bound item. This causes the watcher to fire even when a sub-key or value of an object inside of the watched $scope
property changes.
Example:
Because $watch (objectEquality)
uses angular.equals
for deep object comparison, it is the slowest performing of the watchers (deep comparison takes longer than shallow). For this reason, if you just need to watch a list of items and you don’t care about deep value changes in the list, $watchCollection
or $watchGroup
would better choices. Of course, if you just need to watch a single value (and shallow comparison will suffice) $watch
without objectEquality is the speediest option.
Bonus TIL: I also learned that registering a watcher on scope returns a deregistration function that can be used to remove the watcher from the digest cycle if it is no longer needed.
Example:
The next time $scope.name
changes, the watcher will not be registered so the alert will not occur. This is a good way to tidy up after yourself and reduce the length of the digest cycle, if watchers only need to exist temporarily or under certain conditions. $watchCollection
also returns a deregister function, so it can be used in the same way.
Comments