Superfast Angular use ngModelOptions to limit $digest cycles blog post

Superfast Angular use ngModelOptions to limit $digest cycles

Todd Motto

24 Oct, 2015


4 minutes read

The $digest cycle is the critical entity for keeping our Angular applications fast: the faster the cycle, the faster the two-way data binding. JavaScript has a single thread of execution, which means if our $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-click, ng-change and so on. When something triggers these internal events, such as a keypress that triggers an <input> with ng-model bound to it, Angular will run the $digest loop to see if anything has changed. If something has changed, Angular will update the bound JavaScript Model. If something in the Model changes, 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.


Using 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 updateOn property:

    'updateOn': 'blur'

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:

    'updateOn': 'default blur'

With the 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.

    'updateOn': 'default blur',
    'debounce': {
      'default': 250,
      'blur': 0

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 immediately.

$digest tracking Directive

If anyone is interested in using the code from the Directive for testing purposes feel free:

 * trackDigest.js
 * Simple counter for counting when $digests occur
 * @author Todd Motto
function trackDigests($rootScope) {
  function link($scope, $element, $attrs) {
    var count = 0;
    function countDigests() {
      $element[0].innerHTML = '$digests: ' + count;
  return {
    restrict: 'EA',
    link: link

  .directive('trackDigests', trackDigests);

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[0] reference to the updated count after incrementing with count++.

About the author

Todd Motto profile picture

Todd Motto

GDE Google Developer Expert

Todd is the Founder of Ultimate Courses. With a passion for Angular, TypeScript and JavaScript, Todd leads the online courses creation and has written hundreds of articles on front-end web development and beyond. He specialises in breaking down complex topics and understands the critical mission of learning new technology fast, comprehensively and the right way.

Love the post? Share it!

Lots of time and effort go into all our blogs, resources and demos,
we'd love it if you'd spare a moment to share them!

Explore our AngularJS courses

Get started today and join over 50,000 developers.