This post was originally published on Oct 14, 2014 but has been updated to reflect the latest information.
The time has come. Angular 1.3 is finally out and it comes with tons of new features, bug fixes, improvements but also breaking changes. And because of all this new stuff happening there, we thought it would make sense to help making the adaption of this release easier for all of us, by exploring its main features and improvements and make a blog series out of it. This is the first post of “Exploring Angular 1.3” and it covers one of the most important features ever: one-time binding.
We’ve written a few other articles on 1.3 already. Here’s a list:
Wait! Isn’t this Angular thing about databinding that automatically keeps the UI in sync? Well, yes it is and that’s great. However, Angulars implementation of databinding requires the framework to keep an eye on all values that are bound. This can lead to performance issues and one-time bindings are here to help. But before we explore one-time bindings, let’s understand Angulars concepts of databinding and watchers first.
Understanding data-binding and watchers
In order to make databinding possible, Angular uses $watch APIs to observe model mutations on the scope. What the scope actually is and where it comes from, depends on your application code. If you don’t create a child scope by, for example, using the
ngController directive to create an association between your DOM and your actual controller code, you’re probably dealing with the $rootScope, which is (as the name says) the scope that acts as root scope for your application and created by Angular itself through the
ngApp directive, unless you bootstrap your app manually.
However, at some point you always deal with a scope and that one is used to observe changes on it with the use of so called watchers. Watchers are registered through directives that are used in the DOM. So let’s say we use the interpolation directive to reflect scope model values in the DOM:
This interpolation directive registers a watch for a property
name on the corresponding scope (which in our case is
$rootScope) in order to interpolate against it to display the value in the DOM.
Defining a property with exactly that identifier on our scope and assigning a value to it, makes it magically displaying it in the DOM without further actions:
Great! We just bound a model value to the view with an interpolation directive. If now something changes the value, the view gets updated automatically. Let’s add a button that updates the value of
name once it’s clicked:
Clicking the button assigns the string
name which triggers a $digest cycle that automatically updates the DOM accordingly. In this particular case we’re just updating the value one-way (top → down). However, when for example dealing with an
input element that has an ngModel directive applied, and a user changes its
value property by typing something into it, the change is reflected back to the actual model.
This happens because when a
$digest cycle is triggered, Angular processes all registered watchers on the current scope and its children and checks for model mutations and calls dedicated watch listeners until the model is stabilized and no more listeners are fired. Once the
$digest loop finishes the execution, the browser re-renders the DOM and reflects the changes.
Here’s a running example of the code described above:
The problem with too many watchers
Now that we have a picture of how the databinding mechanism in Angular actually works, we might wonder why there is a feature for one-time binding.
Due to Angulars nature of using watchers for databinding, we might get some problems in terms of performance when having too many of them. As we learned, watch expressions are registered on the scope together with their callback listeners so Angular can process them during
$digest cycles in order to update the view accordingly. That simply means, the more watchers are registered, the more Angular has to process.
Now imagine you have a lot of dynamic values in your view that have to be evaluated by Angular. Internationalization for example, is a very common use case where developers use Angulars databinding to localize their apps, even if the language isn’t changeable during runtime, but set on initial page load. In that case every single string that is localized in the view and written to the scope, sets up a watch in order to get updated once something triggers the next
$digest. This is a lot of overhead especially when your language actually doesn’t change at runtime.
One-time bindings to the rescue!
This is where one-time bindings come in. So what are one-time bindings? Let’s just read what the official docs say:
One-time expressions will stop recalculating once they are stable, which happens after the first digest…
And this is exactly what the Angular world needs to tackle the problems mentioned above. So what does it look like when we want to use one-time binding? Angular 1.3 comes with a new syntax for interpolation directives and expressions in order to tell Angular that this particular interpolated value should be bound one-time.
Using this new syntax is as easy as starting an expression with
::. So if we apply the one-time expression to our example above, we change this:
This works for all kind of typical Angular expressions you’re used to use throughout your app. Which means you can use them in
ng-repeat expressions or even for directives that expose attributes that set up a two-way binding from the inside out. From the outside you’re able to just feed them with a one-time expression:
Okay, let’s see it in action. We already updated the
::name to ensure the one-time binding. The rest of the code just stays as it is to demonstrate that our one-time binding works. Remember the button we added to update the
Christoph? Well, try it again:
name won’t ever change again.
Pascal is a much better name anyway, right?
Exploring Angular 1.5: Lifecycle Hooks
Angular 1.5 is finally out! This article discusses the new lifecycle hooks in Angular.
Sponsoring AngularConnect. Again.
Today we are very happy to announce that we're going to sponsor AngularConnect - again! We're also going to run...
ngMessageFormat - Angular's unheard feature
Angular 1.5 is pretty much around the corner. It turns out that there's a feature that already landed in Angular...
Multiple Transclusion and named Slots
One of those bigger features in the 1.5 release is multiple transclusion via named slots. In this article we're going...
Service vs Factory - Once and for all
This is yet another article on services vs factories in AngularJS. It explains once and for all, why we mostly...
Taking Angular Master Class to the next level
We always collect feedback to make our material and trainings even better. We listened. Here's what we did.
You might also be interested in
Exploring Angular 1.3: ng-model-optionsRead more
Exploring Angular 1.3: Angular-hintRead more
Exploring Angular 1.3: Stateful FiltersRead more
Exploring Angular 1.3: ES6 Style PromisesRead more
Exploring Angular 1.3: Disabling Debug InfoRead more
Exploring Angular 1.3: Binding to Directive ControllersRead more
Exploring Angular 1.3: Validators PipelineRead more
Exploring Angular 1.3: Go fast with $applyAsyncRead more
Exploring Angular 1.3: ngMessagesRead more