Undocumented secrets of $scope.$watch
The $scope.$watch() you know and love
Most angular devs I’ve talked to use
$scope.$watch sparingly and as advertised: for DOM manipulation inside directives.
For the unfamiliar, each time
$scope.prop changes, our callback gets invoked and updates the DOM. This is, in fact, the same API that angular uses to implement one direction of its two-way data binding.
The $scope.$watch() that sneaks out at night
What most devs don’t know is that
$scope.$watch takes an expression, not just a property name.
- array access:
- dot access:
- logical, comparison, and ternary operators:
you > excited || vulcan
- function invocation:
thatIs = "just wow"
$scope.$watch has a pretty sweet API, but, if you’re like most devs, you don’t use it all that often, so… what? Well, consider the consequences of using a an isolated
$scope object as the base class for your models: functional reactive programming1. For example, let’s say we have a widget whose save button we show only if it has been edited and is selected:
We can use
$watch to define a model property in terms of other model properties such that we don’t ever have to think about that property again—unless we want to change its definition, but not if we add features that change its dependent properties2. Cool, but cumbersome.
What if it were as simple as saying:
net <~ revenue - expenses?
net should always reflect the value of
The $scope.$watch() that will haunt your dreams
WAT. While not nearly as elegant as our pure-bred
net example, here we see our pony
$watch accepting another expression as its second argument, which brings us a step closer to the FRP ideal of expressing relationships instead of operations3.
How did I discover this? On accident, reading the source, after I’d implemented the same feature by monkey-patching
$watch using angular’s
$parse service. But that’s another story in which
$watch gets a pretty serious face-lift, including
$watch(...).once(), $watch(...).times(n), and
This is the main draw of FRP over procedural programming—by saying what you want rather than how you want it done, you save yourself from, e.g., the possibility of forgetting to update
showSaveButtonwhen you add a feature that manipulates the
Again, this is FRP (relationships) vs procedural (operations). FRP leverages higher-level abstractions, reducing the developer’s opportunities to introduce bugs. ↩
blog comments powered by Disqus