Angular about $watch, $apply and how $digest works

Source: Internet
Author: User

Our browser detects a wait event, such as a user's behavior, if you click on a button or write something in input,

The callback for the event will run in the built-in JavaScript and you will be able to do some DOM operations.

So when the callback occurs, there are some changes in the DOM in the browser.

and Angularjs extended This event poll, creating something called angular content (remember it, a very important concept),

To explain what this context is and how it works, we need to look at some other concepts first.

The $watch list

Every time you set something on the UI, you add a $wacth to the $watch list.

You can think of $watch as a detector that can detect changes in the model,

For example, your HTML code is

User: <input type= "text" ng-model= "user"/>password: <input type= "Password" ng-model= "Pass"/>

Here, we bind the $scope.user to the first input, bind the $scope.pass to the second input,

This means that we have added two $wacth to the $watch list.

Another example

App.controller (' Mainctrl ', function ($scope) {  $scope. foo = "Foo";  $scope. World = "World";});
Page Htnlhello, {{World}}

In this example, although we define Foo and world in the controller, only one is bound to the page,

So in this case, we only created a $watch.

Let's take a look at the following scenario:

App.controller (' Mainctrl ', function ($scope) {  $scope. People = [...];}); /html code <ul>  <li ng-repeat= "person in people" >      {{person.name}}-{{person.age}}  </li> </ul>

How many $watch of this example are created?

Here we assume that each element in the Peple array has 2 properties of name and age.

Because of our practical ng-repeat traversing $scope.people,

If there are 10 people, then we created a total of 10*2+1 = 22

Note: 1 of these are created for ng-repeat.

So it's important to note that when we use (BIND) the directives instruction on the UI, we create a $watch,

By the right, when was it created?

When our template is loaded (also called linking phase), compiler will find all the instructions and create the corresponding $watch.

$digest Loop

Do you remember the event-loop we discussed above?

When the browser sends an event, we can manage the event through angular context, when $digest is activated

The polling loop of this event consists of two small loops,

One is to process the $evalasync queue

One is to deal with the $watch list, which is the subject of this article.

What is the process of processing? $digest will poll us for some $watch list, probably like this

---"Hey, $watch, what is your value?

---"My value is 9.

---"Okay, have you changed?"

---"No.

(Nothing has changed and will skip to the next question)

---, what's your value?

---"is foo.

Have you changed---?

---"Yes, it was bar.

(Well, then we have a DOM to update now)

The process continues, knowing that all the $watch in the $watch list are being questioned ...

Now we're talking dirty value detection (dirty-checking), and now all the $watch have been polled,

Will ask again if all the $watch have been updated?

If one of these changes, it will be re-polled until all the $wacth have not changed. Doing this to make sure all the model is "clean."

Note: If you poll the loop more than 10 times, an exception will be thrown to exit the infinite polling.

When the $degest loop is complete, the DOM will change.

Look at the following example

App.controller (' Mainctrl ', function () {  $scope. Name = "Foo";  $scope. Changefoo = function () {      $scope. Name = "Bar";  }});
{{name}}<button ng-click= "Changefoo ()" >change the name</button>

Here we have only one $watch, because Ng-click does not create any $watch (function functions are not changed)

1 · When we click on the button

2 Browser sends event, enter angular context (explain this context later)

3 · Then execute $degest loop to poll all $watch for changes

4 · The new loop report says there's no new change.

5 · Then the browser takes control, and then updates the DOM, where the new value is updated, $scope. Name

The most important point here (and the difficulty) is that all events go into angular context and then execute $digest loop

This means that whenever we type a character in input, polling loops checks all the $watch on the page.

Entering the angular context with $apply

How did that get into the angular context? The answer is $apply.

When an event occurs, if you call $apply, you will enter angular context.

If you do not invoke it, it will only be executed externally (this refers to the difference between the angular context)

Then you may have a question,

The above example I did not call $apply, why does it enter the angular context? The answer is that angular did it for us.

All when you call Ng-click, the event is included in the $apply call.

If you have input on a page and the label says Ng-model= "Foo",

Then enter "F" and the event is like this $apply ("foo = ' F ';") is called, in other words, is called by the $apply containing the call.

When angular doesn ' t use $apply for us

This is a common question for many novice angular, I use jquery on the page, why does jquery not update my bindings?

Since jquery did not call $apply, all events did not enter the angular context, so $digest loop did not execute

Let's take a look at an interesting example:

If we have the following instruction in our code directive and controller controllers

1234567891011121314151617181920212223 app.directive(‘clickable‘function() {return{  restrict: "E",  scope: {    foo: ‘=‘,    bar: ‘=‘  },  template: ‘<ul style="<li>{{foo}}</li><li>{{bar}}</li></ul>‘,  link: function(scope, element, attrs) {    element.bind(‘click‘function() {      scope.foo++;      scope.bar++;    });  }} });app.controller(‘MainCtrl‘function($scope) {  $scope.foo = 0;  $scope.bar = 0;});

In the example we have bound Foo and bar to the LI in UL, we think that whenever we click, Foo and bar will add a

What happens when we click on the actual situation? Can we see Foo and bar changing according to our expectations?

The answer is no, because the click above is not included in the $apply call.

Does that mean we can't control the changes we want? Actually, it's not, we can do it.

In fact, the $scope foo and bar are changed, but it does not implement $digest loop, so it is not aware of $watch changes

This also means that if I call $apply after that, all $watch will know the change and then the DOM will be updated.

<! DOCTYPE html>
App = Angular.module (' app ', []); App.controller (' Mainctrl ', function ($scope) {  $scope. foo = 0;  $scope. bar = 0;    $scope. Hello = "Hello";    $scope. Sethello = function () {    $scope. Hello = "World";};  }); App.directive (' clickable ', function () {  return {    restrict: ' E ',    scope: {      foo: ' = ',      bar: ' = '    },    Template: ' <ul style= ' <li>{{foo}}</li><li>{{bar}}</li></ul> ',    link:function (scope, element, attrs) {      element.bind (' click ', Function () {        scope.foo++;        (scope.bar++;});}}  );

If you click on the blue area above, you will not see any changes, but then click on the button,

You'll suddenly see a 0 number turning into a number, which is the number that you just clicked on the blue area.

As I said above, the click in the command does not trigger the $digest loop, but when you click the button

The Ng-click on the button buttons will call $apply, and then the $digest loop will be executed,

All $watch will check for changes (which include Foo and bar, which are two $watch)

At this point, you may think that the result is not what you want, and you will see the change as soon as you click on that block of instruction.

In fact, this is very simple, only need to call the time to include $apply on it.

Element.bind (' click ', Function () {  scope.foo++;  scope.bar++;  Scope. $apply ();});

$apply is a functional function on $scope (or scope on the command link function), so calling it enforces $digest loop

Note that if a polling loop is already in place, calling it in this case throws an exception, which is an indication that we don't need to call $apply anymore.

In fact, the above usage is better to use

Element.bind (' click ', Function () {  scope. $apply (function () {      scope.foo++;      (scope.bar++;  });})

What is the difference between these two usages?

The difference is the first usage, we update the new value is outside the angular context, so when there is an error, angular will not know.

Obviously it doesn't make much of a difference in the small example above, but imagine that when we use multiple libraries in complex projects and then make mistakes, angular won't know his own mistakes.

So if you want to use JQuery plugin in your project, make sure you have called $apply to execute the $degest loop to update the DOM changes.

Angular about $watch, $apply and how $digest works

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.