Deep understanding of $apply () and $digest () in Angular.js _angularjs

Source: Internet
Author: User

$apply () and $digest () Introduction

ANGULARJS provides a very cool feature called bidirectional data binding (two-way), which greatly simplifies the way our code is written. Data binding means that when any data in a view changes, the change is automatically fed back to the scope data, meaning that the scope model is automatically updated. Similarly, when the scope model changes, the data in view is updated to the most recent value. So how did Angularjs do that? When you write down an expression like this, Angularjs will set up a watcher on the scope model for you to update the view when the data changes.

The watcher here are the same as the Watcher you will set in Angularjs:

$scope. $watch (' Amodel ', function (NewValue, oldValue) {
 //update the DOM with NewValue
});

The second parameter passed in to $watch() is a callback function that is invoked when the value of the Amodel changes. When the Amodel changes, this callback function will be called to update view this is not difficult to understand, but there is a very important question! How does Angularjs know when to call this callback function? In other words, how does angularjs know that the Amodel has changed before invoking the corresponding callback function? Does it periodically run a function to check that the data in the scope model has changed? Well, that's where the $digest loop comes in.

In the $digest loop, the watchers is triggered. When a watcher is triggered, Angularjs detects the scope model and how it changes so that the callback function associated with the watcher is invoked.

So the next question is, when did the $digest cycle start in a variety of ways?

After the call is made $scope.$digest() , the $digest loop begins. If you change a data in scope in a handler function corresponding to a ng-click instruction, ANGULARJS automatically triggers a round of $digest loops by calling $digest (). When the $digest loop starts, it triggers each watcher. These watchers check whether the current model value in scope differs from the last computed model value. If different, then the corresponding callback function is executed. The result of calling the function is that the content of the expression in the view is updated. In addition to the Ng-click instructions, there are other built-in directives and services that allow you to change models (such as Ng-model, $timeout, etc.) and automatically trigger a $digest loop.

So far so good! However, there is a small problem. In the above example, ANGULARJS does not call $digest () directly, but rather calls $scope.$apply() , which is invoked $rootScope.$digest() . Therefore, a round of $digest loops begins at $rootscope and then accesses all watchers in the children scope.

Now, suppose you associate the Ng-click instruction with a button and pass in a function name to the Ng-click. When the button is clicked, Angularjs wraps the function into a wrapping function and then passes it in $scope.$apply() . Therefore, your function will be executed normally, modify the models (if necessary), and a round of $digest loops will also be triggered to ensure that the view is also updated.

$scope.$apply()is invoked automatically $rootScope.$digest() . There are two forms of $apply () method. The first type takes a function as an argument, executes the function, and triggers a round of $digest loops. The second type will not accept any parameters, just triggering a round of $digest loops. We will soon see why the first form is better.

When do I manually invoke the $apply () method?

If Angularjs always wrap our code into a function and passes it in to $apply() start a round of $digest loops, when do we need to call the $apply() method manually? In fact, Angularjs has a very clear requirement that it is responsible for automatically responding to changes that occur in the context of angularjs (that is, changes made to models in the $apply() method). Angularjs's built-in instructions do this, so any model change is reflected in the view. However, if you modify model anywhere outside of the ANGULARJS context, you will need to notify Angularjs by a manual call $apply() . It's like telling Angularjs that you've modified some models, hoping ANGULARJS will help you trigger watchers to make the right response.

For example, if you use JavaScript setTimeout() to update a scope model, then ANGULARJS has no way of knowing what you've changed. In this case, the call $apply() is your responsibility, by invoking it to trigger a round of $digest loops. Similarly, if you have a command to set up a DOM event listener and modify some models in the listener, you also need to manually invoke $apply () to ensure that the changes are reflected correctly in the view.

Let's take a look at an example. Join you have a page, once the page has finished loading, you want to display a message after two seconds. Your implementation may be the following:

<body ng-app= "myApp" >
 <div ng-controller= "Messagecontroller" >
 delayed message: {{message}}
 </div>
</body>
/* What happens without an $apply () * * *

angular.module (' myApp ', []). Controller (' Messagecontroller ', function ($scope {

 $scope. getMessage = function () {
 settimeout (function () {
  $scope. Message = ' fetched after 3 seconds '; 
   console.log (' message: ' + $scope. message);
 }

 $scope. GetMessage ();

By running this example, you will see that after two seconds, the console does display the updated model, however, view is not updated. The reason maybe you already know, is that we forgot to call the $apply () method. Therefore, we need to modify getMessage() it as follows:

/* What happens with $apply *
/angular.module (' myApp ', []). Controller (' Messagecontroller ', function ($scope) {

 $scope. getMessage = function () {
 settimeout (function () {
  $scope. $apply (function () {
  //wrapped this within $apply
  $scope. Message = ' fetched after 3 seconds ';
  Console.log (' message: ' + $scope. message);}
  ;
 };

 $scope. GetMessage ();

If you run the example above, you will see that the view is updated after two seconds. The only change is that our code is now wrapped, and $scope.$apply() it automatically triggers $rootScope.$digest() , allowing watchers to be triggered to update view.

Incidentally, you should use $timeout service setTimeout() it instead, as the former will help you invoke $apply() it so that you do not need to call it manually.

Also, note that in the above code you can also manually call without parameters after modifying model $apply() , as follows:

$scope. getMessage = function () {
 settimeout (function () {
 $scope. Message = ' fetched after two seconds ';
 Console.log (' message: ' + $scope. message);
 $scope. $apply (); This triggers a $digest
 };

The above code uses $apply() the second form, which is a form without parameters. Keep in mind that you should always use a method that accepts a function as a parameter $apply() . This is because when you pass in a function to $apply() , the function is wrapped try…catch in a block, so the exception is processed once an exception occurs $exceptionHandler service .

How many times does the $digest loop run?

When a $digest loop runs, watchers is executed to check whether the models in the scope has changed. If a change occurs, the corresponding listener function is executed. This relates to an important issue. What if the listener function itself modifies a scope model? What will Angularjs do with this?

The answer is that the $digest loop does not run only once. At the end of the current loop, it performs a loop again to check if any models has changed. This is the dirty check (Dirty Checking), which is used to handle model changes that may result when the listener function is executed. Therefore, the $digest loop continues to run until model no longer changes, or the number of $digest cycles reaches 10 times. Therefore, try not to modify model in the listener function as much as possible.

The $digest loop will run at least two times, even if the listener function does not change any of the model. As discussed above, it will run more than once to ensure that models does not change.

Summarize

I hope this article explains $apply and $digest. The most important thing to remember is whether ANGULARJS can detect your changes to model. If it is not detected, then you need to manually invoke $apply (). This is the entire content of this article, we have learned it? Hope that the content of this article for everyone's study or work can bring certain help, if there is doubt you can message exchange.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

Tags Index: