Getting rid of extra $ watch'ers

Published on January 11, 2014

Getting rid of extra $ watch'ers

  • Tutorial
I would like to share a small note on how to speed up the execution of $ digest () by replacing standard directives with equivalents that do not call $ watch.

The point is that often on a page there are a lot of elements for which you need to bind data only once using model data that does not change, for example:

<h1 ng-bind="l10n.main_title"></h1>

<a ng-href="/edit/{{user.id}}" ng-bind="user.name"></a>

These and other standard directives will kindly check if the value of the expression has changed with every $ digest. The situation can be corrected by a set of rather simple custom directives, for example, for text:

app.directive("staticText", function() {
    return {
      restrict: "A"
      link: function(scope, element, attrs) {
        element.text(scope.$eval(attrs.customText));
      }
    };
  })

<h1 static-text="l10n.main_title"></h1>

The result is minus one $ watch.
Of course, you don’t need to invent your bike, there are two worthy modules on GitHub:

The first ($ watch fighters) is quite simple, it includes the following directives:
  • set-title
  • set-href
  • set-text
  • set-html
  • set-class
  • set-if

The second (Bindonce) is more interesting, because the developers thought that the data for binding might not be available at the time of rendering the directive template (for example, they are loaded with an ajax request). It looks like this:

<div bindonce="User">
<h1 bo-text="User.name"></h1>
</div>

For the parent bindonce directive, a temporary $ watch is created, when the value of the variable passed into it becomes different from undefined, the nested bo- * directives are launched, and the temporary $ watch is deleted.
The Bindonce module includes:
  • bo-if
  • bo-show
  • bo-hide
  • bo-text
  • bo-html
  • bo-href
  • bo-src
  • bo-class
  • bo-alt
  • bo-title
  • bo-id
  • bo-style
  • bo-value
  • bo-attr bo-attr-foo

You can read in more detail on the repository page , there is a fairly detailed readme.

Lastly, I’ll give the function that I found here . It roughly calculates the total number of $ watch'ers per page.

(function () { 
    var root = $(document.getElementsByTagName('body'));
    var watchers = [];
    var f = function (element) {
        if (element.data().hasOwnProperty('$scope')) {
            angular.forEach(element.data().$scope.$$watchers, function (watcher) {
                watchers.push(watcher);
            });
        }
        angular.forEach(element.children(), function (childElement) {
            f($(childElement));
        });
    };
    f(root);
    console.log(watchers.length);
})();

For the sake of interests, you can compare the amount to and the implementation field of zero-watch directives.
I hope someone comes in handy. Thank.