$digest cycle is packed full of data to be dirty-checked, the user is going to see lag in the UI whilst (for instance) typing inside an
$digest cycles run from internal Angular events (yes and
$scope.$apply()) built into their Directives, such as
ng-change and so on. When something triggers these internal events, such as a
keypress that triggers an
ng-model bound to it, Angular will run the
$digest again to update the View. Basics of “dirty-checking” - simple.
The problem with dirty-checking is that larger
$digest loops will take longer to complete, which means the user could see some lag whilst using the application. Understanding the performance impacts upfront can help you build better applications using the right APIs.
For this article, I’ve written a tiny Directive that logs our
$digest cycle counts, so we can actually see the impact that simple UI interactions may have on our applications.
Let’s take a simple
<input> for this example with
ng-model bound to it:
The live output:
Type away, you’ll see the
$digest count rockets into double and triple figures. Now, with data heavy applications this is going to cause some severe lag for the end user.
ngModelOptions, a Directive introduced in Angular 1.3, we can add a debounce to
<input> triggers so we have complete control over how and when
$digest cycles occur.
Let’s add the
ng-model-options attribute and tell Angular to update our Model on the
blur event only using the
Type a bunch of characters in, and then click/tab out the
<input> to see the
$digest count increase.
This is a massive performance improvement as we’re not continuously running
$digest. We’re not limited to a single event, so let’s add
'default' as an option:
default value also added, we’re back to square one where
$digest will run each time the user types. Time to get smart! Let’s tell Angular to periodically update the Model, so that other bindings that may rely on the Model being persisted will be updated too. We can introduce the
debounce property inside the
ng-model-options Object to tell Angular exactly when to update the Model after specific events occur.
The above illustrates that
default will be updated
250ms after the event stops, and
blur will update immediately as the user leaves the input (if this is the desired behaviour we want).
Start typing again, then stop and note the
$digest count is severely lower than the initial demonstration. You can then click/tab out the
<input> to call another
$digest tracking Directive
If anyone is interested in using the code from the Directive for testing purposes feel free:
Doesn’t really need annotating, the key ingredient here is
$rootScope.$watch with just a function inside, which will run every
$digest. Inside the callback I’m simply setting the
innerHTML of the
$element reference to the updated count after incrementing with