Introduction to $apply() and $digest()
AngularJS provides a very cool feature called Two-way Data Binding, which greatly simplifies how we write our code. Data binding means that when any data in the View changes, the change will be automatically fed back to the scope data, which means that 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 do this? When you write down an expression like , AngularJS will set a watcher for you on the scope model, which is used to update the view when the data changes.
The watcher here is the same as the watcher you will set in AngularJS:
$scope.$watch('aModel', function(newValue, oldValue) { //update the DOM with newValue });
Incoming to$watch()
The second parameter in this is a callback function, which will be called when the value of aModel changes. When aModel changes, it is not difficult to understand that this callback function will be called to update the view, but there is still a very important problem! How does AngularJS know when to call this callback function? In other words, how did AngularJS call the corresponding callback function when it knows that aModel has changed? Will it run a function periodically to check whether the data in the scope model has changed? Well, that's where the $digest loop comes in.
In the $digest loop, watchers will be fired. When a watcher is triggered, AngularJS will detect the scope model. If it changes, the callback function associated with the watcher will be called.
So, the next question is when does the $digest loop start in various ways?
Called$scope.$digest()
After that, the $digest loop begins. Suppose you change a data in scope in the handler function corresponding to an ng-click directive, AngularJS will automatically trigger a $digest loop by calling $digest(). When the $digest loop starts, it triggers each watcher. These watchers check whether the current model value in scope is different from the model value calculated last time. If it is different, the corresponding callback function will be executed. The result of calling this function is that the content of the expression in the view (translation note: such as) will be updated. In addition to the ng-click directive, there are some other built-in directives and services to let you change models (such as ng-model, $timeout, etc.) and automatically trigger a $digest loop.
So far it's not bad! However, there is a small problem. In the above example, AngularJS does not call $digest() directly, but calls$scope.$apply()
, the latter will call$rootScope.$digest()
. Therefore, a $digest loop starts at $rootScope, which will then access all children scope watchers.
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 will wrap this function into a wrapping function and pass it in$scope.$apply()
. Therefore, your function will be executed normally and models are modified (if necessary), and a $digest loop will also be triggered to ensure that the view will also be updated.
$scope.$apply()
Will be called automatically$rootScope.$digest()
. The $apply() method has two forms. The first one will accept a function as a parameter, execute the function and trigger a $digest loop. The second type will not accept any parameters and just trigger a $digest loop. We'll see right away why the first form is better.
When to manually call the $apply() method?
If AngularJS always wrap our code into a function and pass it in$apply()
, to start a $digest loop, then when will we need to call it manually$apply()
What about the method? In fact, AngularJS has a very clear requirement for this, that it is only responsible for automatically responding to changes that occur in the AngularJS context (i.e.$apply()
changes to models that occur in the method). This is how AngularJS's built-in directive does it, so any model changes will be reflected in the view. However, if you modify the model anywhere outside the AngularJS context, you need to call it manually$apply()
to notify AngularJS. It's like telling AngularJS that you have modified some models and hope that AngularJS can help you trigger watchers to respond correctly.
For example, if you use JavaScriptsetTimeout()
To update a scope model, then AngularJS has no way to know what you have changed. In this case, call$apply()
It's your responsibility to trigger a $digest loop by calling it. Similarly, if you have a directive to set a DOM event listener and modify some models in that listener, you also need to call $apply() manually to ensure that the changes will be correctly reflected in the view.
Let's look at an example. Join you have a page that once the page is loaded, you want to display a message after two seconds. Your implementation might look like this:
<body ng-app="myApp"> <div ng-controller="MessageController"> Delayed Message: {{message}} </div> </body>
/* What happens without an $apply() */ ('myApp',[]).controller('MessageController', function($scope) { $ = function() { setTimeout(function() { $ = 'Fetched after 3 seconds'; ('message:'+$); }, 2000); } $(); });
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 the reason, that is, we forgot to call the $apply() method. Therefore, we need to modifygetMessage()
, as follows:
/* What happens with $apply */ ('myApp',[]).controller('MessageController', function($scope) { $ = function() { setTimeout(function() { $scope.$apply(function() { //wrapped this within $apply $ = 'Fetched after 3 seconds'; ('message:' + $); }); }, 2000); } $(); });
If you run the example above, you will see that the view will also be updated after two seconds. The only change is that our code has now been wrapped$scope.$apply()
It will automatically trigger$rootScope.$digest()
, so that watchers are triggered to update the view.
By the way, you should use$timeout service
Let's replacesetTimeout()
, because the former will help you call it$apply()
, so you don't need to call it manually.
Moreover, note that in the above code, you can also manually call without parameters after modifying the model.$apply()
, like this:
$ = function() { setTimeout(function() { $ = 'Fetched after two seconds'; ('message:' + $); $scope.$apply(); //this triggers a $digest }, 2000); };
The above code has been used$apply()
The second form of , that is, the form without parameters. It should be remembered that you should always use a function that accepts as a parameter$apply()
method. This is because when you pass in a function to$apply()
When in, this function will be wrapped into atry…catch
In the block, so once an exception occurs, the exception will be$exceptionHandler service
deal with.
How many times will the $digest loop run?
When a $digest loop is running, watchers will be executed to check whether the models in scope have changed. If a change occurs, the corresponding listener function will be executed. This involves an important issue. What if the listener function itself will modify a scope model? How will AngularJS handle this situation?
The answer is that the $digest loop won't run only once. After the current loop ends, it will execute another loop to check whether models have 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 will continue to run until the model no longer changes, or the $digest loop reaches 10 times. Therefore, try not to modify the model in the listener function as much as possible.
The $digest loop will also run at least twice, even if no model is changed in the listener function. As discussed above, it will run once more to ensure that the models are not changing.
Summarize
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, then you need to call $apply() manually. The above is the entire content of this article. Have you learned it? I hope the content of this article will be of some help to everyone's study or work. If you have any questions, you can leave a message to communicate.