- Switching to Angular(Third Edition)
- Minko Gechev
- 485字
- 2021-07-02 15:23:34
Change detection in AngularJS
Most beginners are fascinated by the data binding mechanism in AngularJS. The basic Hello world! example looks similar to this:
function MainCtrl($scope) { $scope.label = 'Hello world!'; }
<body ng-app ng-controller="MainCtrl"> {{label}} </body>
If you run this, Hello world! magically appears on the screen. However, that is not the only most impressive thing! If we add a text input and we bind it to the label property of the scope, each change will reflect in the content displayed by the interpolation directive:
<body ng-controller="MainCtrl"> <input ng-model="label"> {{label}} </body>
How awesome is that! This is one of the main selling points of AngularJS: the extreme ease of achieving data binding. We add a few attributes in our markup, interpolation directive, the label property to a mystical object called $scope, which is magically passed to a custom function we define, and everything simply works!
The more experienced Angular developer has a better understanding of what is actually going on behind the scenes. In the preceding example, inside the ng-model and ng-bind directives (in our case, the interpolation directive, {{}}), Angular adds watchers with a different behavior associated with the same expression: label. These watchers are quite similar to the observers in the classical MVC pattern. On some specific events (in our case, change of the content of the text input), AngularJS will loop over all such watchers, evaluate the expressions associated with them in the context of a given scope, and store their results. This loop is known as the digest loop.
In the preceding examples, the evaluation of the label expression in the context of the scope will return the text, Hello world!. On each iteration, AngularJS will compare the current result of the evaluation with the previous result and will invoke the associated callback in case the values differ. For instance, the callback added by the interpolation directive will set the content of the element to be the new result of the expression's evaluation. This is an example of the dependency between the callbacks of the watchers of two directives. The callback of the watcher added by ng-model modifies the result of the expression associated with the watcher added by the interpolation directive.
This approach has its own drawbacks. We said that the digest loop will be invoked on some specific events, but what if these events happen outside the framework; for example, what if we use setTimeout, and inside the callback, passed as the first argument, we change properties attached to the scope that we're watching? AngularJS will be unaware of the change and won't invoke the digest loop, so we need to do that explicitly using $scope.$apply. But, what if the framework knew about all the asynchronous events happening in the browser, such as user events, the XMLHttpRequest events, and the WebSocket-related events? In such a case, Angular would be able to intercept the event's handling and could invoke the digest loop without forcing us to do so!