$apply () and $digest () are two core concepts in angularjs, but sometimes they are confusing. To understand how ANGULARJS works, you first need to understand how $apply () and $digest () work. This article is intended to explain what $apply () and $digest () are, and how they are applied in everyday coding.
Explore $apply () and $digest ()
ANGULARJS provides a cool feature called two-way data binding, a feature that greatly simplifies how we write code. Data binding means that when any data in the 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 does Angularjs do that? When you write down an expression such as {{AModel}}, Angularjs will set up a watcher on the scope model for you to update the view when the data is changed. The Watcher here is the same as the Watcher you will set in the Angularjs:
[JavaScript] View plain copy print?
$scope. $watch (' AModel ', function (NewValue, oldValue) {
Update the DOM with NewValue
});
The second parameter passed in to $watch () is a callback function that is called when the value of Amodel changes. This callback function is not difficult to understand when the Amodel is changed, but there is a very important problem! How does Angularjs know when to invoke 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 if the data in the scope model has changed? Well, that's where the $digest cycle 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 to the watcher is called. So, the next question is, when did the $digest cycle start in various ways?
After the $scope. $digest () is called, the $digest loop begins. Suppose you change one of the data in a scope in the handler function corresponding to a ng-click instruction, and 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 model value that was last computed. If it is different, then the corresponding callback function will be executed. The result of calling the function is that the expression content in the view (such as {{AModel}}) is updated. In addition to the Ng-click directive, there are other built-in directives and services that let you change models (such as Ng-model, $timeout, etc.) and automatically trigger a $digest loop.
So far it's good! However, there is a small problem. In the example above, Angularjs does not call $digest () directly, but instead calls $scope. $apply (), which calls $rootscope. $digest (). Therefore, the round $digest cycle starts at $rootscope and then accesses all watchers in the children scope.
Now, suppose you associate the Ng-click directive to a button and pass in a function name to Ng-click. When the button is clicked, Angularjs wraps the function into a wrapping function and then passes in to the $scope. $apply (). As a result, your function will be executed normally, modifying the models (if necessary), and a round of $digest loops will also be triggered to ensure that the view is updated.
Note: $scope. $apply () will automatically call $rootscope. $digest (). There are two forms of the $apply () method. The first one takes a function as a parameter, executes the function and triggers a round of $digest loops. The second will not accept any arguments, just trigger a round of $digest loops. We'll see now why the first form is better.
When do I manually call the $apply () method?
If Angularjs always wrap our code into a function and pass in $apply () to 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 only responsible for automatically responding to changes that occur in the ANGULARJS context (that is, changes to models that occur in the $apply () method). Angularjs's built-in instructions are done in this way, so any model changes will be reflected in the view. However, if you modify the model anywhere outside of the ANGULARJS context, you need to notify Angularjs by manually calling $apply (). It's like telling Angularjs that you've modified some models and want ANGULARJS to help you trigger the watchers to make the right response.
For example, if you use SetTimeout () in JavaScript to update a scope model, there is no way for angularjs to know what you have changed. In this case, calling $apply () is your responsibility, by invoking it to trigger a round of $digest loops. Similarly, if you have an instruction to set up a DOM event listener and modify some models in the listener, you also need to manually call $apply () to ensure that the changes are correctly reflected in the view.
Let's take a look at an example. Join you have a page, once the page has been loaded, you want to display a message after two seconds. Your implementation might look something like this:
Html:
[HTML] View plain copy print?
<body ng-app= "MYAPP" >
<div ng-controller= "Messagecontroller" >
Delayed message: {{Message}}
</div>
</body>
Javascript:
[JavaScript] View plain copy print?
/* 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);
}, 2000);
}
$scope. GetMessage ();
});
By running this example, you will see that after two seconds, the console does show the updated model, however, the view is not updated. Maybe you already know that we forgot to call the $apply () method. Therefore, we need to modify the GetMessage () as follows:
[JavaScript] View plain copy print?
/* 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);
});
}, 2000);
}
$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 to $scope. $apply (), which automatically triggers the $rootscope. $digest (), allowing the watchers to be triggered to update the view.
Note: By the way, you should use the $timeout service instead of settimeout (), because the former will help you call $apply (), so you don't need to call it manually.
Also, note that in the above code you can manually invoke the $apply () without parameters after modifying the model, as follows:
[JavaScript] View plain copy print?
$scope. getMessage = function () {
SetTimeout (function () {
$scope. Message = ' fetched after seconds ';
Console.log (' message: ' + $scope. message);
$scope. $apply (); This triggers a $digest
}, 2000);
};
The code above uses the second form of $apply (), which is the form without parameters. It should be remembered that you should always use the $apply () method that takes a function as a parameter. This is because when you pass in a function to $apply (), the function is wrapped in a try...catch block, so that if an exception occurs, the exception is handled by the $exceptionhandler service.
How many times will the $digest loop run?
When a $digest loop runs, watchers is executed to check if the models in the scope has changed. If there is a change, the corresponding listener function will be executed. This relates to an important issue. What if the listener function itself modifies a scope model? What will angularjs do with the situation?
The answer is that the $digest loop does not run only once. At the end of the current cycle, it will perform a loop again to check if any models have changed. This is the dirty check (Dirty Checking), which is used to handle the model changes that can be caused when the listener function is executed. As a result, the $digest cycle runs until the model no longer changes, or the number of $digest loops reaches 10 times. Therefore, try not to modify the model in the listener function as much as possible.
Note: The $digest loop will run at least two times, even if the listener function does not change any model. As discussed above, it will run more than once to ensure that the models is unchanged.
Conclusion
I hope this article explains $apply and $digest. The most important thing to remember is whether ANGULARJS can detect your modifications to the model. If it can't be detected, then you need to call $apply () manually.
Original address
http://www.sitepoint.com/understanding-angulars-apply-digest/
"Reprint" $apply () and $digest ()