In-depth understanding of $ apply (), $ digest (), and angulardigest in Angular
$ Apply () and $ digest () are two core concepts in AngularJS, but sometimes they are confusing. To understand how AngularJS works, you must first understand how $ apply () and $ digest () work. This article aims to explain what $ apply () and $ digest () are and how to apply them in daily coding.
Explore $ apply () and $ digest ()
AngularJS provides a very cool feature called Two-way Data Binding, which greatly simplifies code writing. Data Binding means that when any data changes in the View, this change will be automatically reported to the data of the scope, that is, the scope model will be automatically updated. Similarly, when the scope model changes, the data in the view is updated to the latest value. So how does AngularJS achieve this? When you write an expression such as {aModel}, AngularJS will set a watcher for you on the scope model behind the scenes to update the view when the data changes. Watcher here is the same as the watcher you set in AngularJS:
[Javascript]View plaincopyprint?
- $ Scope. $ watch ('amodel ', function (newValue, oldValue ){
- // Update the DOM with newValue
- });
The second parameter passed in to $ watch () is a callback function, which is called when the value of aModel changes. When aModel changes, this callback function will be called to update the view. However, there is still a very important issue! How does AngularJS know when to call this callback function? In other words, how does AngularJS know that aModel has changed before it calls the corresponding callback function? It periodically runs a function to check whether the data in the scope model has changed? Well, this is where the $ digest loop is used.
In the $ digest loop, watchers is triggered. When a watcher is triggered, AngularJS detects the scope model and calls the callback function associated with the watcher. Then, the next question is when the $ digest loop starts in various ways?
After $ scope. $ digest () is called, the $ digest loop starts. Assume that you have changed a piece of data in the scope in the handler function corresponding to the ng-click Command, AngularJS will automatically trigger a $ digest loop by calling $ digest. When the $ digest loop starts, it triggers each watcher. These watchers checks whether the current model value in the scope is different from the model value obtained in the previous calculation. If they are different, the corresponding callback function will be executed. The result of calling this function is that the expression content (such as {aModel}) in the view will be updated. In addition to the ng-click command, there are other built-in commands and services that allow you to change models (such as ng-model and $ timeout) and automatically trigger a $ digest loop.
Not bad so far! However, there is a small problem. In the preceding example, AngularJS does not directly call $ digest (), but $ scope. $ apply (). The latter calls $ rootScope. $ digest (). Therefore, a $ digest loop starts at $ rootScope and then accesses watchers in all children scope.
Now, assume that you associate the ng-click command with a button and input a function name to ng-click. When the button is clicked, AngularJS packs the function into a wrapping function and passes it to $ scope. $ apply (). Therefore, your function is executed normally. If you need to modify models, a $ digest loop is triggered to ensure that the view is updated.
Note: $ scope. $ apply () automatically calls $ rootScope. $ digest (). The $ apply () method has two forms. The first type accepts a function as a parameter, executes the function, and triggers a $ digest loop. The second method does not accept any parameters, but triggers a $ digest loop. We will immediately see why the first form is better.
When can I manually call the $ apply () method?
If AngularJS always transfers our code wrap to a function and passes in $ apply () to start a $ digest loop, when does it need to be manually called $ apply () how? In fact, AngularJS has a very clear requirement for this, that is, it is only responsible for automatically responding to changes that occur in the AngularJS Context Environment (that is, at $ apply () method ). AngularJS's built-in command does this, so any model change will be reflected in the view. However, if you modify the model anywhere outside the AngularJS context, you need to manually call $ apply () to notify AngularJS. This is like telling AngularJS that you have modified some models and hope AngularJS will help you trigger watchers to make the correct response.
For example, if you use setTimeout () in JavaScript to update a scope model, AngularJS cannot know what you have changed. In this case, calling $ apply () is your responsibility. You can call it to trigger a $ digest loop. Similarly, if you have a command to set 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 look at an example. Add a page. Once the page is loaded, you want to display a message after two seconds. Your implementation may look like this:
HTML:
[Html]View plaincopyprint?
- <Body ng-app = "myApp">
- <Div ng-controller = "MessageController">
- Delayed Message: {message }}
- </Div>
- </Body>
JavaScript:
[Javascript]View plaincopyprint?
- /* 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 will indeed display the updated model. However, the view is not updated. The reason may be that you have forgotten to call the $ apply () method. Therefore, we need to modify getMessage () as follows:
[Javascript]View plaincopyprint?
- /* 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 preceding example, you will see that the view will be updated after two seconds. The only change is that our code is now wrapped to $ scope. $ apply (), it will automatically trigger $ rootScope. $ digest (), so that watchers is triggered to update the view.
Note: By the way, you should use $ timeout service instead of setTimeout (), because the former will help you call $ apply () so that you do not need to call it manually.
In addition, note that you can manually call $ apply () without parameters after modifying the model in the above Code, as shown below:
[Javascript]View plaincopyprint?
- $ Scope. getMessage = function (){
- SetTimeout (function (){
- $ Scope. message = 'fetched after two seconds ';
- Console. log ('message: '+ $ scope. message );
- $ Scope. $ apply (); // this triggers a $ digest
- },2000 );
- };
The above Code uses the second form of $ apply (), that is, the form without parameters. Remember that you should always use the $ apply () method that accepts a function as the parameter. This is because when you pass in a function to $ apply (), this function will be encapsulated into a try... Catch Block, so once an exception occurs, the exception will be handled by $ exceptionHandler service.
$ How many times does a digest loop run?
When a $ digest loop is run, watchers is executed to check whether models in the scope has changed. If a change occurs, the corresponding listener function will be executed. This involves an important issue. What if the listener function will modify a scope model? How does AngularJS handle this situation?
The answer is that the $ digest loop will not run only once. After the current cycle ends, it executes another cycle to check whether models has changed. This is Dirty Checking, which is used to handle model changes that may occur when the listener function is executed. Therefore, the $ digest loop continues until the model does not change, or the number of $ digest loops reaches 10. Therefore, try not to modify the model in the listener function.
Note: $ digest runs at least twice, even if no model is changed in the listener function. As discussed above, it runs once more to ensure that models does not change.
Conclusion
I hope this article explains $ apply and $ digest clearly. The most important thing to remember is whether AngularJS can detect your modifications to the model. If it cannot be detected, You need to manually call $ apply ().
Original article address
Http://www.sitepoint.com/understanding-angulars-apply-digest/