One-time bindings in AngularJS for improved performance

AngularJS version 1.3 (released on 13th Oct 2014) brought, among other things, an important feature for optimizing your application's performance - one-time bindings. They are expressions in views (HTML templates) that only get evaluated until their result is a non-undefined value. After that they remain unchanged, even if the underlying model changes (i.e. the Javascript variable that the expression in the view is bound to).

Using one-time bindings is as simple as prefixing an expression with a double colon. For example:


or equivalent:

<h1 ng-bind="::pageTitle"></h1> 

But when is this useful you may ask? Isn't AngularJS all about two-way bindings, automatically synchronizing the DOM with values in Javascript code and vice-versa? While this is indeed one of Angular's biggest selling points, it also comes with a cost (more on that in a minute). For the things we know they won't be changing, two-way bindings are not needed anyway and should be avoided for performance reasons.

$watches and digest cycles

Have you ever wondered, how Angular knows when a particular value in Javascript has changed so that it can update the DOM accordingly? It just works out of the box, automagically. The answer is dirty checking.

Internally, Angular maintains a list of all the values that could change. Everytime you do something like ng-model="username" or {{errorMessage}} in a template, Angular adds the corresponding value to the watchlist and then periodically checks if any values in that list have changed. The checks are performed in so-called digest cycles, which are automatically started when Angular thinks it's necessary and can also be manually triggered, e.g. by calling the $apply() method on a scope. If any changes are detected, the relevant parts of DOM are updated.

The problem is that there is no free lunch and checking for changes requires computational resources. If there are too many values to be watched in your application, digest cycles become noticeably slow and the applications starts to feel laggy. As a rule of thumb, this happens when the total number of watches starts approaching 2000 (this seems to be a general consensus on the internet).

In the Article Edit Screen (AES) plugin, this new one-time bindings feature was quickly adopted, since it is both useful and very easy to incorporate into an existing application. We only needed to upgrade Angular from 1.2.x to 1.3, which was more or less a painless step in our case, and you should probably do the same.