Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

I don't understand how to use scope.$watch and scope.$apply. The official documentation isn't helpful.

What I don't understand specifically:

  • Are they connected to the DOM?
  • How can I update DOM changes to the model?
  • What is the connection point between them?

I tried this tutorial, but it takes the understanding of $watch and $apply for granted.

What do $apply and $watch do, and how do I use them appropriately?

share|improve this question
up vote 1007 down vote accepted

You need to be aware about how AngularJS works in order to understand it.

Digest cycle and $scope

First and foremost, AngularJS defines a concept of a so-called digest cycle. This cycle can be considered as a loop, during which AngularJS checks if there are any changes to all the variables watched by all the $scopes. So if you have $scope.myVar defined in your controller and this variable was marked for being watched, then you are implicitly telling AngularJS to monitor the changes on myVar in each iteration of the loop.

A natural follow-up question would be: Is everything attached to $scope being watched? Fortunately, no. If you would watch for changes to every object in your $scope, then quickly a digest loop would take ages to evaluate and you would quickly run into performance issues. That is why the AngularJS team gave us two ways of declaring some $scope variable as being watched (read below).

$watch helps to listen for $scope changes

There are two ways of declaring a $scope variable as being watched.

  1. By using it in your template via the expression <span>{{myVar}}</span>
  2. By adding it manually via the $watch service

Ad 1) This is the most common scenario and I'm sure you've seen it before, but you didn't know that this has created a watch in the background. Yes, it had! Using AngularJS directives (such as ng-repeat) can also create implicit watches.

Ad 2) This is how you create your own watches. $watch service helps you to run some code when some value attached to the $scope has changed. It is rarely used, but sometimes is helpful. For instance, if you want to run some code each time 'myVar' changes, you could do the following:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$apply enables to integrate changes with the digest cycle

You can think of the $apply function as of an integration mechanism. You see, each time you change some watched variable attached to the $scope object directly, AngularJS will know that the change has happened. This is because AngularJS already knew to monitor those changes. So if it happens in code managed by the framework, the digest cycle will carry on.

However, sometimes you want to change some value outside of the AngularJS world and see the changes propagate normally. Consider this - you have a $scope.myVar value which will be modified within a jQuery's $.ajax() handler. This will happen at some point in future. AngularJS can't wait for this to happen, since it hasn't been instructed to wait on jQuery.

To tackle this, $apply has been introduced. It lets you to start the digestion cycle explicitly. However, you should only use this to migrate some data to AngularJS (integration with other frameworks), but never use this method combined with regular AngularJS code, as AngularJS will throw an error then.

How is all of this related to the DOM?

Well, you should really follow the tutorial again, now that you know all this. The digest cycle will make sure that the UI and the JavaScript code stays synchronised, by evaluating every watcher attached to the all $scopes as long as nothing changes. If no more changes happen in the digest loop, then it's considered to be finished.

You can attach objects to the $scope object either explicitly in the Controller, or by declaring them in {{expression}} form directly in the view.

I hope that helps to clarify some basic knowledge about all this.

Further readings:

share|improve this answer
34  
"Angular checks if there are any changes to all the variables attached to all the $scopes" -- I don't think that's quite right. I believe Angular only (dirty) checks $scope properties that have had $watches set up (note that using {{}} in a view will create a $watch automatically). See also section "Scope $watch Performance Considerations" on the Scope page. – Mark Rajcok Feb 28 '13 at 0:18
4  
That might be the case. I'll try to find some time to read more about it and edit my answer. – ŁukaszBachman Feb 28 '13 at 15:22
6  
@MarkRajcok, you were right. I altered my reply and pointed out to an article that shows nicely how this is implemented. – ŁukaszBachman Jun 23 '14 at 8:29
2  
what about using this? ("Control as" method) – Leandro Tupone Sep 8 '14 at 18:56
1  
Using "Control as" should have no impact on the information above. Using this.myVar puts myVar on the scope. – Marcus Nielsen Oct 3 '14 at 12:15

In AngularJS, we update our models, and our views/templates update the DOM "automatically" (via built-in or custom directives).

$apply and $watch, both being Scope methods, are not related to the DOM.

The Concepts page (section "Runtime") has a pretty good explanation of the $digest loop, $apply, the $evalAsync queue and the $watch list. Here's the picture that accompanies the text:

$digest loop

Whatever code has access to a scope – normally controllers and directives (their link functions and/or their controllers) – can set up a "watchExpression" that AngularJS will evaluate against that scope. This evaluation happens whenever AngularJS enters its $digest loop (in particular, the "$watch list" loop). You can watch individual scope properties, you can define a function to watch two properties together, you can watch the length of an array, etc.

When things happen "inside AngularJS" – e.g., you type into a textbox that has AngularJS two-way databinding enabled (i.e., uses ng-model), an $http callback fires, etc. – $apply has already been called, so we're inside the "AngularJS" rectangle in the figure above. All watchExpressions will be evaluated (possibly more than once – until no further changes are detected).

When things happen "outside AngularJS" – e.g., you used bind() in a directive and then that event fires, resulting in your callback being called, or some jQuery registered callback fires – we're still in the "Native" rectangle. If the callback code modifies anything that any $watch is watching, call $apply to get into the AngularJS rectangle, causing the $digest loop to run, and hence AngularJS will notice the change and do its magic.

share|improve this answer
4  
I understand the idea, what I don't understand is how the data actually gets transferred. I have a model which Is an object with lots of data, I use some of it to manipulate the DOM. then some of it gets changed. How do I put the changed data in the right place in the model? In the example I used he makes the manipulation and in the end simply uses scope.$apply(scope.model), I don't understand what data gets transferred and how is it transferred to the right place in the model? – ilyo Feb 28 '13 at 16:16
3  
There is no magical data transfer taking place. Normally with Angular apps, you should change the Angular models, which then drive view/DOM updates. If you update the DOM outside of Angular, you'll have to manually update the models. scope.$apply(scope.model) will simply evaluate scope.model as an Angular expression, and then enter a $digest loop. In the article you reference, probably scope.$apply() would be sufficient, since the model is already being $watch'ed. The stop() function is updating the model (I believe toUpdate is a reference to scope.model), and then $apply is called. – Mark Rajcok Feb 28 '13 at 16:43

There are $watchGroup and $watchCollection as well. Specifically, $watchGroup is really helpful if you want to call a function to update an object which has multiple properties in a view that is not dom object, for e.g. other view in canvas, webGL or server request. Here, the documentation link.

share|improve this answer
    
I would have commented about the $watchCollection but I see you already did. Here is documentation about it from the AngularJS site. They provide a very nice visual of the $watch depth. Note the information is close to the bottom of the page. – JabberwockyDecompiler Apr 1 '15 at 15:18

protected by Pankaj Parkar Jun 11 '15 at 19:00

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.