This post was originally published on Oct 19, 2014 but has been updated to reflect the latest information.
Hi again. This is the second article of “Exploring Angular 1.3”. If you haven’t read the first one you might want to check out that too. In this article, we cover another feature that turns out to be very useful in our daily development of Angular applications. Introducing the
We’ve written a few other articles on 1.3 already. Here’s a list:
ngModelOptions allows us to control how
ngModel updates are done. This includes things like updating the model only after certain events are triggered or a debouncing delay, so that the view value is reflected back to the model only after a timer expires. To get an idea of what that actually means, let’s start with the probably simplest use case that sets up a two-way binding using an
input element that has a
ngModel directive applied:
Now, when typing something into the
input element, the model gets updated accordingly and then reflected back to the view, which displays the value in our
p element. Try it out yourself real quick.
The reason why the view is updated immediately, is that every time the
input element fires an
input event, Angulars
$digest loop is executed until the model stabilizes. And that’s nice because we don’t have set up any event listeners and update the DOM manually to reflect model values in the view; Angular takes care of that.
However, that also means that, because of the
$digest that happens to be triggered on every single keystroke, Angular has to process all registered watchers on the scope whenever you type something into the
input element. Depending on how many watchers are registered and of course how efficient the watcher callbacks are, this can be very expensive. So wouldn’t it be great if we could somehow manage to trigger a
$digest only after the user stopped typing for, let’s say, 300 milliseconds? Or only when the user removes the focus of the
Yes, and we can do so thanks to Angular 1.3 and the
ngModelOptions comes with a couple of options to control how
ngModel updates are done. With the
updateOn parameter, we can define which events our
input should be bound to. For example, if we want our model to be updated only after the user removed the focus of our
input element, we can simply do so by applying the
ngModelOptions with the following configuration:
This tells Angular that instead of updating the model immediately after each keystroke, it should only update when the
input fires an
onBlur event. Here’s an example to show what it looks like in action.
If we do want to update the model with the default events that belong to that control and add other events on top of that, we can use a special event called
default. Adding more then just one event can be done with a space delimited list. The following code updates the model whenever a user types into the input, or removes the focus of it.
Alright, now that we know how that works, let’s take a look at how we can update the model after a timer expires.
Delaying the model update with
We can delay the model update with
ngModelOptions in order to reduce the amount of
$digest cycles that are going to be triggered when a user interacts with our model. But not only that this ensures fewer
$digest cycles, it’s also a powerful feature that can be used to implement a nice user experience when dealing with asynchronous code execution.
Just imagine an
input[type="search"] element, where every time a user types into the field, the model gets updated and an asynchronous request is made to a server to get a response with search results depending on the given query. This works. However, we probably don’t want to update the model on every keystroke but rather once the user has finished typing a meaningful search term. We can do exactly that with
debounce defines an integer value which represents a model update delay in milliseconds. Which means, if we take the example mentioned above, that we want to update our model 300 milliseconds after the user stopped typing, we can do so by defining a debounce value of
300 like this:
Now, when typing into the
input field, there’s a slight delay until the model updates. You can try it out right here:
We can go even further and configure how the update delay should be done for certain events. Controlling the debounce delay for specific events can be done by defining an object literal instead of a primitive integer value, where keys represent the event name and values the debounce delay. A delay of
0 triggers an immediate model update.
The following code generates a model update delay of 300 milliseconds when the user types into our
input, but an immediate update when removing the focus:
Super powerful right? There are a few other options that are worth to checkout out. You can read about them in the official docs.
Synchronizing model and view with
Due to the fact that we are able to control with
ngModelOptions how and when model updates are done, the model and the view can get out of sync. For example, when we configure our
input element to update the model only when it loses its focus, the moment when the user types into the field, the
input value differs from the actual value in the model.
There might be situations, where you want to roll the view value back to what it was, before the change has been made. For such cases, Angular introduces a so called
$rollbackViewValue method that can be invoked to synchronize the model and view. Basically what this method does is, it takes the value that is currently in the model and reflects it back to the view. In addition, it cancels all debounced changes.
To demonstrate this use case, we can setup a
form that has an
input element that updates the model when the user removes the focus. As long as the user didn’t remove the focus of the
input element, he can hit the
Esc key to discard his changes and get the value of the model back. Try it out yourself:
So it turns out that
ngModelOptions is a super powerful directive that helps us making our apps more intuitive. Go and check out the docs about the
getterSetter options, to see what else is possible!
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: One-time bindingsRead 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