Tuesday, August 6, 2013

AngularJS: $watch, $digest and $apply

While browsing reddit, I read an article I felt over-complicated an explanation of $watch, $digest and $apply, and perhaps gave people a little bit of the wrong idea about what it is.

What is a $watch? 

Let's talk about this first. $watch is arguably the most important internal feature of Angular. $watches can be used to watch any value, and trigger a function call when that value changes. A $watch can be set up from any $scope by calling $scope.$watch() as shown below.

Setting up a $watch

There are two ways to set up a watch, by expression, or by function. Technically, they both do the same thing. If you pass an expression (a string), it will be evaluated against $scope and converted to a function to be watched. If you pass a function, it just watches that function, with no conversion necessary.

By Expression

The following will watch 'foo'. Which is an expression evaluated against $scope.
//$scope.$watch(<function/expression>, <handler>);

$scope.$watch('foo', function(newVal, oldVal) {
    console.log(newVal, oldVal);

By Function

To set up a $watch by function, you can do the following, which is technically the same as what is shown above:
$scope.$watch(function() {
    return $scope.foo;
}, function(newVal, oldVal) {
    console.log(newVal, oldVal);

Facts about $watch:

  • A watcher can evaluate any value.
  • A watcher's handler can execute anything when aforementioned value has changed.
  • All watchers are evaluated when $digest() is called.
  • If the first argument of a $watch is a string, it is $eval'ed into a function prior to registration. It's functionally equivalent to passing a function as the first argument, just with an extra step internally.

What is $digest?

At it's core, the important thing to know about $digest is that it loops through all watchers on the scope it was called on and it's child scopes. and evaluates them to see if they've changed, executing their handlers if they have. That's the important part you need to know.

How to call $digest:


What is $apply?

Simply put, it's a wrapper around $rootScope.$digest that evaluates any expression passed to it prior to calling $digest(). That's it.  So, if you're calling it by itself without passing an argument to it, you may as well just call $digest().

How to call $apply:

$scope.$apply('foo = "test"');
$scope.$apply(function(scope) {
    scope.foo = 'test';
    $scope.foo = 'test';

So when does a $digest/$apply happen?

At key moments defined by the framework. The Angular framework has built in calls to $digest and $apply to it's services and directives as well as some internal calls. But basically it's like this, things like $timeout perform a setTimeout, then call $apply or $digest (it actual does a little more than that, but that's the basic idea). $http.get(), same deal, makes an AJAX call, returns it, then queues up a $digest.  Then there are directives, like inputs with ngModel for example. Updates to the input will also trigger a $digest. You get the idea.

How do $watch, $digest, and $apply relate to updating my view?

  • The directive registers a $watch that looks for a change in the model on the $scope. The handler will update the DOM element's value.
  • The directive registers an event handler of some sort in the DOM that will get a value from the DOM and apply it to the model in $scope. It will also call $apply or $digest.
  • When you update the model in the scope via some in-framework call... $http.get() for example, it kicks off a $digest after it completes.
  • The $digest checks the $watch the directive registered, sees the change and fires the handler associated to it, updating the DOM element.

Why does Angular work this way?

Since Angular wanted to use plain-old-JavaScript-objects (*POJSO© Ben Lesh 2013 all rights reserved!! Brought to you by Carl's Jr), a digest was the only real choice. Why? Well you can observe setter and getters on known properties real-time, but there's really no way to build an event into adding new values to or removing values from an object or array, particularly when it's done by an indexer, ala hash table or array index. So a digest becomes necessary. How do I know this? Because I tried to write my own framework and quickly found myself writing a half-assed version of Angular. The only other option that I know of would be to completely wrap the model observation up in a construct with set() and get() functions... think Knockout... which makes the JavaScript code uglier to deal with (IMO).

Some Guidelines For Use:

  • $watch
    • DO use $watch in directives to update the DOM when a $scope value changes.
    • DON'T use $watch in a controller. It's hard to test and completely unnecessary in almost every case. Use a method on the scope to update the value(s) the watch was changing instead.
  • $digest/$apply
    • DO use $digest/$apply in directives to let Angular know you've made changes after an asynchronous call, such as a DOM event.
    • DO use $digest/$apply in services to let Angular know some asynchronous operation has returned, such as a WebSocket update, or an event from a 3rd party library like Facebook API.
    • DON'T use $digest/$apply in a controller. This will make your code harder to test, and asynchronous operations outside of the Angular framework don't belong in your controllers. They belong in services and directives.
Remember these are guidelines, not hard, fast rules. More importantly, they're my guidelines, so take them with a grain of salt.

When to use $apply vs $digest?

[EDIT: for clarification on when these should be used] $scope.$digest should rarely be used outside of some very specific instances. Such as an isolated scope in a directive that might want to only update itself. Or, in an extreme edge case if a scope object has been created for some other isolated purpose. $scope.$apply or $rootScope.$digest should be favored most of the time, as the general desired effect is to update an entire view or model. 


I'm sure I butchered some part of the explanation above, but if you want to know more, get the information where I did... read the code!!!! You can get to all of this code right on Angular's repository on GitHub. It's extremely well commented, and extremely interesting code to look through.

EDIT: I'd like to thank Zeroto for his comment on reddit that pointed out a pretty serious omission on my part.


  1. You wrote that you don'T have to use $watch in controllers and use methods to updated values.

    But how do want to use a methos with ng-model? ng-model can't use methods/functions it needs a writable attribute. Because of this, one has to use $watches to get notified about changes made by the user.

    Or is there another way I don't see?

    1. Almost every directive (input, select, textarea, etc) that allows ng-model also has an ng-change. You can use ng-change to do what the $watch was doing. ... if that makes sense.

    2. You, Sir, are responsible for ... learning me something! Thank you!

    3. Regarding "Don't use $watch in controller", In the case I have 10 input binding to the same scope value, would it be nicer to watch this value in controller instead of spread out 10 ng-change in the template?

    4. @yi fu: That's probably not an issue. input bindings are setting up a $watch within their directives. You definitely wouldn't want to move that to a controller, because you'd need to access the DOM too, to accomplish the same thing. If anything you could create a directive that handled all ten bindings at once with one $watch. But it really depends on what you're trying to do.

      Either way, I wouldn't try to hyper-optimize like that unless you have a performance issue during $digest.

  2. Yup! I have watches in my controllers and it is resulting in some odd behavior. Wasn't sure why but at least now I have an idea.


  3. Object.observe() FTW! http://wiki.ecmascript.org/doku.php?id=harmony:observe

  4. This is incorrect and misleading. According to the Angular docs at http://docs.angularjs.org/api/ng.$rootScope.Scope:
    1. There is no reference to $apply() being a wrapper for $digest()
    2. You shouldn't use $digest() in calls that are made from outside the angular framework (eg in call back functions to DOM events, XHR requests, deferred functions, etc). The angular docs recommend using $apply in such instances even if it's from within a controller. The document I reference says "Usually, you don't call $digest() directly in controllers or in directives. Instead, you should call $apply() (typically from within a directives), which will force a $digest().

    This article did lead me in the right direction for solving my problem though, but you should update it to reflect more accurate details about how Angular JS works.

    1. Thanks for the feedback!

      If you read the documents you referenced above, the psuedo-code in the documents clearly indicates that $apply wraps $digest. Or you can always go check the code on GitHub which shows the same thing.

      As far as your statement "You shouldn't use $digest()..", that's an odd rule, especially considering right after that you cite documentation that says "usually" you wouldn't call it directly. I'm not at all saying that someone should always use $digest. Usually, I use $apply, honestly, but I'll use $digest if I want too, and you can't stop me! Mwuhahaha, mwuhahaha!

      If you still have concerns about the correctness of the article above, I'll be happy to update it so it's either more correct or easier to understand.

  5. Hi, can you make an example code on the rule:

    DON'T use $watch in a controller. It's hard to test and completely unnecessary in almost every case. Use a method on the scope to update the value(s) the watch was changing instead.

    As a newbie to angular and just having learned about $watch I may have been misusing it link in this plunk.

    1. Well, it's a largely situational thing, but most times you can use an event such as ngChange or ngClick, or even a callback from a service, to update a property on your scope. I just did a blog entry about this the other day, actually: It can be found right here

  6. Is there a way to scroll using anchorscholl while compensating for a header that sticks to the top of the page? (Basically, anchorscroll with an offset)

    1. Not really, since anchorScroll is using element.scrollIntoView(); You'd have to do some custom scrolling.

  7. so I have a clock in a controller that has a start time and time step (so you can start the clock at some point in the past or future and limit the updates to timeStep. If i wanted to move the clock to a service, do I put a $watch on the time in the controler or a $apply in the service that kicks in after each timeStep?

    1. So I presume you have a window.setInterval() that runs a function that updates the time? Do you have a JSFiddle or a Plunk or something I can see? Also, ask this on StackOverflow.com and link it here, it's a better format for Q/A about code. I'll have a look.

  8. Hi, regarding the effort to write a new framework and being stuck with observables. There are a few options. A current work in progress "Object.observe" mentioned by Alec LaLonde. Polymerjs. watch.js it uses `Object.prototype.__defineGetter__.call` explained in DailyJS. Also there are a few without modifying the base object for adding observable using events. jsviews.com has js observable which does not wrap the model object. difference from knockout data binding , ref1 examples: jsviews.com

  9. Really helpful article! Thank you.

    I try to ieverage a third party javascript library into my controller and a angular variable will be updated with its build-in callback function. How can I make data-binding work without calling $scope.$apply() if you strongly suggest that "DON'T use $watch/$digest/$apply in controller"?

    .controller('MainCtrl', function ($scope, jslib) {
    $scope.models = [];
    $scope.$apply(function() {
    jslib.callback( function (objects) {
    $scope.models = objects;

    1. It depends on the third party library. If it's anything doing DOM manipulation (a calendar widget, a lightbox, or things of that type) then it should be used and wired up in a directive, not a controller. If it's some other JavaScript library that doesn't touch the DOM really, like an AJAX library, or Moment.js or a BigInt implementation, then it should be wrapped in a service (in which you can handle the $apply or whatever) and then injected into your controller.

    2. Also, incidentally, you'd want the $apply inside of your callback. And you're probably going to benefit from having it return a $q promise after wrapping it in a service.

      So your service would return something like (roughly):

      return {
      getThing: function() {
      var deferred = $q.defer();
      $window.jsLib.getThing(function(result) {
      $rootScope.$apply(function () {
      return deferred.promise;


      jsLib.getThing().then(function(data) { $scope.thing = data; });

    3. Thank you very much! This is a great solution.

  10. I know you said you aren't doing this to teach,
    but I came by your blog scrounging around for an Angular lesson on watches.
    here's a tip on how to make this resource a little more valuable:
    Its good you thought to put a section called 'What is a $watch?' up front.
    The trouble is you never explained what a watch is.
    A huge problem in teaching coding is that its easy to not even think about what your readers might know or not know.
    Your answer to What is a $watch? includes what you already know about $watch
    There are new people who really have no idea of what watch does or there are people like me who know one way $watch can be used but are not aware of what $watch can be used for in general. I'm aware of how it can be used but not how it fits in with the Angular ecosystem.

    1. Doug, you're absolutely correct. Reading it, it looks like I meant to do a little more explanation, but got too excited about what I was writing about and forgot to add more detail. I'll add a little something to that section.

      Good thinking, and good catch!

  11. hi Ben,
    Nice article. I will keep this for reference. Just got a question.
    I have two combo-boxes in a parent-child-like relationship. When the model of the parent changes I also want that of the child to change. In other words, if the selected value of the parent is changed programmatically, I would like that of the child to change as well. Is there a way of achieving this in the controller without using $watch? The ngChange directive does not respond to model changes.

    1. You could use `ng-change` on the parent combo box. When that value changes, call a method on the $scope that updates the items the other combo box is bound to.

      Another option is to build out a tree of data ahead of time, and have the parent combo box selecting from the trunk branches of the tree, and the child combo box linked to the top branches of the selected branch... if that makes sense.

  12. Hi,
    I want to monitor change in width of a div but watch is not serving the purpose somehow. can you suggest how can it be done? Thanks

    1. You're probably better off asking this in StackOverflow. Be sure to include code and information about what you're tried in your question. It's too hard to answer questions like this in this comment section.

  13. Ben - first of all thank you for this comprehensive article - really good learning material!
    I have somewhere in a controller an Array in which i fetch & push some values from the back-end (some DTO objects to display on the UI); i ended up using $watch because i need to perform some actions in the controller whenever a new item is pushed in the Array. For some reason for the very first item pushed - the $watch does trigger but both oldValue / newValue / my actual array have zero elements in, the 2nd elements triggers the watcher again with new / old value having 2 elements. Any thoughts / experiences with this? Also - going via your tips - what else should i use instead of $watch?

    1. Hmm... I'd love to see a plunk demonstrating that, because on the surface it sounds like a bug. That said, the best thing to do, IMO, would be to find the point at which you're pushing elements onto the array (or popping them off), and at *that* point trigger your updates. In the end, it will do roughly the same thing, but it's just one less $watch to process during the $digest. If that makes sense.

      Also, there's a hidden danger in watching arrays, in that you might try a "deep" watch, which would be a little more expensive on a small array and a lot more expensive on a large array. I guess generally I'm against observation of things if there's a way to do the same thing with good composition.

    2. Thanks for the quick response - i'll setup a plunk tomorrow and share the details. In the interim, to add a bit more detail to it this is the end-to-end flow:
      1) when controller is loaded i initiate an http.get to retrieve some data (deferred)
      2) once that is ready, i push each item via a forEach; i have tried adding my additional operation in here (another http.get call), straight after the push but for some reason it was not working (and was rushing to make things work - heh)
      3) via the $watch over the array the intention calling the 2nd operation (from above)

      What i am trying to replicate is a behavior of loading some "bare-minimum" data to the UI and once that is loaded retrieve additional data

      I am still learning the guts of Angular (it is the first web app i develop using Angular) and i do discover daily things i do "Wrong" or just not as efficient as it could be - thus reaching out for advice.

      Thanks again,


    3. So it sounds like you could simply call the update function you mentioned in number 2 whenever your http.get() resloves... in some psuedo code (because I still don't fully understand the problem I think):

      $http.get(url).then(updateArray, getFailure).then(reconcileDifferences, updateFailure).then(reconsileSuccessful, reconcileFailure);

      Something like that would be just one way to skin this cat without using a $watch. There are likely many more.

    4. Yeah, that was my initial intention but got stuck for some reason (it looked like for no obvious reason my 2nd http.get was not fired. I'll take that route again and see where it gets me. Thanks again for your input!

  14. Hi,

    I am new to Angular. I have a search listing in a page based on a search input field filter.

    Now the search listing is not working when I move the search input field to another page / partial.

    Could you please give a little tip to help ?

    1. I'd love to help, but this isn't a good place for it. Post a question on StackOverflow.com... be sure to include an example in code of what you tried.

  15. can i make a real-time data record using this method?


This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)