Thursday, April 9, 2015

Improving Angular.js performance using ng-model-options

Every app has a search ability. Some apps wait for the user to hit “Enter” and then send the search string to the server, others (especially when using Angular) bind every user’s key stroke to a request to the server. In my opinion the second option has better user experience since the user starts to receive results while he is typing and most likely will get what he is searching for before he finished typing the whole search string and he is no more required to press another button after he finished typing.

However if you have a large Database and your search request take more than several milliseconds then there is no use to send a request to the server after every keystroke – the user will not get the relevant results. So how do you control when to send the request to the server?

Ng-model-options

Ng-model-options is a directive that was introduced in Angular 1.3. It has some really nice features:

updateOn:

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:

<input type="search"
ng-model
="searchQuery"
ng-model-options
="{ updateOn: 'blur' }"/>

Debounce:


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:


<input type="search"
ng-model
="searchQuery"
ng-model-options
="{ debounce: 300 }" />

Advanced options: we can configure how the update delay should be done for different events. Controlling the debounce delay for specific events can be done by defining an object instead of a 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:


<input type="search"
ng-model
="searchQuery"
ng-model-options
="{ updateOn: 'default blur',
debounce: { 'default': 300,
'blur': 0 } }"
>

How does it work?

The ng-model-options controls how the ngModel is updated.

By default Angular automatically registers a watch for every scope variable for updating the binding, the update itself happens due to the $digest loop that is triggered at every key stroke. The $digest loop checks if the model has a new value and if so it updates every model instances in the app without making us add event listeners or manually update the DOM.

As you can see this means that the $digest loop has to go through all the registered watches on the scope at every key stroke and depending on the watches callbacks that can be very expensive. So the debounce triggers the $digest loop after the defined milliseconds allowing the user to type a meaningful search phrase and reduce the amount of the $digest loops.

In the next post we’ll see how ‘one time binding’ also increases our app’s performance.





2 comments: