For me who is a pure novice in the front end, Javascript is still a little bit clear about it. If you want to get started with angular JS directly, there are really a lot of resistance. But I believe that as long as you work hard, even anti-human design will not be a big problem.
Okay, don't say much nonsense. To figure out what angular JS is, I started with Scope. So what is Scope? Borrowing a passage from the official document:
The code copy is as follows:
“scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events.”
After reading it, I compared it to other programming languages and felt that Scope is like the scope of Data Model, providing context for the execution of Expressions. Let's understand it this way for now.
Features of Scope
Next, let’s see what features Scope have?
Scope provides the $watch method to monitor the changes in the Model.
Scope provides $apply method to propagate changes in Model.
Scope can be inherited and isolates different application components and attribute access permissions.
Scope provides context for the calculation of Expressions.
For these four features, because I have learned ActionScript, C++, and Java before, it is not difficult to understand the first, third and fourth points, but the second point feels a bit confusing. Based on the principle of breaking the casserole and asking the end, I still found some things through Google. For experienced veterans, please pat the bricks!
Origin of Javascript
First of all, at first glance, scope.apply() seems to be an ordinary method to make bindings update. But think a little more, why do we need it? When do you usually use it? To understand these two issues, we have to start with Javascript. In Javascript code, they are executed in a certain order. When it is the turn of a code snippet to execute, the browser will only execute the current snippet and will not do anything else. So sometimes some web pages that are not well-made will get stuck when clicking on something. Javascript works one of the reasons why this phenomenon is caused! Here we have a piece of code to experience it:
The code copy is as follows:
var button = document.getElementById('clickMe');
function buttonClicked () {
alert('the button was clicked');
}
button.addEventListener('click', buttonClicked);
function timerComplete () {
alert('timer complete');
}
setTimeout(timerComplete, 5000);
When loading Javascript code, first find a button with an id called "clickMe", then add a listener, and then set the timeout. Wait for 5 seconds and a dialog box will pop up. If you refresh the page and click the clickMe button immediately, a dialog box will pop up. If you don't click OK, the timerComplete function will never have a chance to execute.
How to update bindings
Okay, after talking about something that seems irrelevant, let's get back to the topic. How does angular JS know when data changes and pages need to be updated? The code needs to know when the data has been modified, but now there is no way to directly inform the data on an object that has changed (although ECMAScript 5 is trying to solve this problem, it is still in the experimental stage). Currently, there are two solutions for the more mainstream strategies. One is to use special objects so that all data can only be set by calling the object method, rather than directly specifying it through property. In this way, all the modifications can be recorded and you will know when the page needs to be updated. The disadvantage of doing this is that we must inherit a special object. For assignment, it can only be done by object.set('key', 'value') instead of object.key=value. In frameworks, this is what EmberJS and KnockoutJS do (although I haven't touched it before). Another method is the method adopted by angular JS, which checks whether there is any data change after each Javascript code execution sequence is executed. This doesn't seem to be efficient and even seriously affects performance. However, angular JS uses some clever means to solve this problem (it has not been studied yet, and it is not yet clear). The advantage of doing this is that we can use any object at will, there is no limit on the assignment method, and we can also be aware of data changes.
For this solution adopted by angular JS, we care about when the data changes, and that's where scope.apply() comes in handy. Checking whether the bound data has changed, it is actually done by scope.digest(), but we have almost never called this method directly, but instead called the scope.apply() method because in the scope.apply() method, it will call the scope.digest() method. The scope.apply() method takes a function or an expression, then executes it, and finally calls the scope.digest() method to update bindings or watchers.
When to use $apply()
The same question is, so when do we need to call the apply() method? There are very few cases. In fact, almost all of our code is wrapped in scope.apply(), such as ng-click, controller initialization, http callback functions, etc. In these cases, we do not need to call ourselves, and in fact we cannot call ourselves, otherwise calling the apply() method in the apply() method will throw an error. If we need to run the code in a new execution sequence, we really need to use it, and if and only if the new execution sequence is not created by the angular JS library method, we need to wrap the code in scope.apply(). Here is an example to explain:
The code copy is as follows:
<div ng:app ng-controller="Ctrl">{{message}}</div>
The code copy is as follows:
functionCtrl($scope) {
$scope.message ="Waiting 2000ms for update";
setTimeout(function () {
$scope.message ="Timeout called!";
// AngularJS unaware of update to $scope
}, 2000);
}
After the above code is executed, the page will display: Waiting 2000ms for update. Obviously, the update of the data was not detected by angular JS.
Next, we slightly modify the Javascript code and wrap it with scope.apply().
The code copy is as follows:
functionCtrl($scope) {
$scope.message ="Waiting 2000ms for update";
setTimeout(function () {
$scope.$apply(function () {
$scope.message ="Timeout called!";
});
}, 2000);
}
The difference this time is that the page will first display: Waiting 2000ms for update. After waiting for 2 seconds, the content will be changed to: Timeout called! . Obviously, the update of the data was detected by angular JS.
NOTE: We should not do this, but use the timeout method provided by angular JS, so that it will be automatically wrapped with the apply method.
Science is a double-edged sword
Finally, let’s take a look at the scope.apply() and scope.apply(function) methods again! Although angular JS did a lot for us, we also lost some opportunities. You can see from the following pseudocode:
The code copy is as follows:
function$apply(expr) {
try {
return$eval(expr);
} catch(e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
It will catch all exceptions and will not throw them again, and will eventually call the $digest() method.
Let's summarize
The $apply() method can execute angular JS expressions outside the angular framework, such as: DOM events, setTimeout, XHR or other third-party libraries. This is just the beginning, the water is still deep, welcome to deep dive together!