How Data Binding works in AngularJS getting started

Source: Internet
Author: User
This article mainly introduces AngularJS data binding principles and analyzes in detail the principles, usage skills, and precautions of AngularJS data binding, for more information about how AngularJS Data Binding works, see the following example. We will share this with you for your reference. The details are as follows:

Note

This article is intended for new users who are new to Angular and want to know how data help works. If you already know Angular, you are strongly advised to read the source code directly.

Angular users want to know how data binding works. You may see a variety of words: $ watch, $ apply, $ digest, dirty-checking... what are they? How do they work? Here I want to answer these questions. In fact, they have already been answered in the official documents, but I still want to combine them for details, but I just want to explain it in a simple way. If you want to know the technical details, check the source code.

Let's start from scratch.

Browser event loop and Angular. js Extension

Our browser is always waiting for events, such as user interaction. If you click a button or enter something in the input box, the event callback function will be executed in the JavaScript interpreter, and then you can perform any DOM operations, after the callback function is executed, the browser changes the DOM accordingly. Angular expands this event loop and generates an execution environment that sometimes becomes an angular context (Remember, this is an important concept). To explain what context is and how it works, we also need to explain more concepts.

$ Watch Queue ($ watch list)

Every time you bind something to your UI, you will insert a $ watch into the $ watch queue. Imagine $ watch is the one that can detect changes in the model it monitors. For example, you have the following code:

Index.html

User: Password: 

Here we have a $ scope. user, which is bound to the first input box and has a $ scope. pass, it is bound to the second input box, and then we add two $ watch

Next, let's look at the following example:

Controllers. js

app.controller('MainCtrl', function($scope) { $scope.foo = "Foo"; $scope.world = "World";});

Index.html

Hello, {{ World }}

Here, even if we add two things to $ scope, but only one is bound to the UI, only one $ watch is generated here.

Let's look at the following example:

Controllers. js

app.controller('MainCtrl', function($scope) { $scope.people = [...];});

Index.html

 
 
  • {{person.name}} - {{person.age}}

How many $ watches are generated here? Each person has two (one name and one age), and ng-repeat has one. Therefore, the total number of 10 persons is (2*10) + 1, that is to say, there are 21 $ watches. Therefore, a $ watch is generated for each data bound to the UI. Yes. When did $ watch be generated? When our template is loaded, that is, in the linking stage (Angular is divided into the compile stage and the linking stage-Translator's note), Angular interpreter will find every directive, then, generate the $ watch for each request. That sounds good, but what then?

$ Digest Loop

Do you still remember the extended Event loop I mentioned earlier? When the browser receives an event that can be processed by angular context, the $ digest loop is triggered. This loop is composed of two smaller loops. One processes the evalAsync queue and the other processes the $ watch queue. This is also the topic of this blog post. What is this? $ Digest will traverse our $ watch and then ask:

Hey, $ watch, what is your value?
It is 9.
Okay. Has it changed?
No, sir.
(This variable hasn't changed, so next one)
What about you? What is your value?
Report: Foo.
Have you changed?
Changed. It was Bar.
(Good. We have DOM to update)
Continue to ask and know that the $ watch queue has been checked.

This is the so-called dirty-checking. Since all the $ watch has been checked out, I have to ask: Have you updated $ watch? If at least one update exists, this loop will be triggered again until all $ watches remain unchanged. This ensures that every model will no longer change. Remember, if the loop is more than 10 times, it will throw an exception to prevent infinite loops. When the $ digest loop ends, the DOM changes accordingly.

Example: controllers. js

app.controller('MainCtrl', function() { $scope.name = "Foo"; $scope.changeFoo = function() {   $scope.name = "Bar"; }});

Index.html

{{ name }}Change the name

Here we have a $ watch because ng-click does not generate $ watch (the function will not change ).

We press the button

The browser receives an event and enters angular context (which explains why ).

$ Digest starts to execute the loop and queries whether each $ watch changes.

Because $ watch that monitors $ scope. name reports changes, it will force another $ digest loop.

No changes are detected in the new $ digest loop.

The browser takes back control and updates the DOM corresponding to the new value of $ scope. name.

It is very important (it is also a pain point for many people) to execute a $ digest loop for every angular context event, that is to say, every time we enter a letter loop, we will check all $ watch on the entire page.

Use $ apply to enter angular context

Who decides what events will go into angular context, and what will not go? $ Apply!

If you call $ apply when an event is triggered, it enters angular context. If no call is made, it does not. Now you may ask: I didn't call $ apply in the example just now. Why? Angular has done it! Therefore, when you click an element with ng-click, the time will be encapsulated into a $ apply call. If you have an ng-model = "foo" input box, and then you press f, the event will call $ apply ("foo = 'F';") like this ';").

When Will Angular automatically apply for us?

This is a common pain point for Angular beginners. Why does my jQuery update my bound items? Because jQuery does not call $ apply, the event does not enter angular context, and the $ digest loop is never executed.

Let's look at an interesting example:

Suppose we have the following direve VE and controller

App. js

app.directive('clickable', function() {return { restrict: "E", scope: {  foo: '=',  bar: '=' }, template: '
 
 
  • {{foo}}
  • {{bar}}
', link: function(scope, element, attrs) { element.bind('click', function() { scope.foo++; scope.bar++; }); }}});app.controller('MainCtrl', function($scope) { $scope.foo = 0; $scope.bar = 0;});

It binds foo and bar from the controller to a list. Each time you click this element, foo and bar will increase by 1.

What will happen when we click an element? Can we see updates? The answer is no. Because click events are common events that are not encapsulated in $ apply, does this mean we will lose our count? No

The real result is: $ scope does change, but $ digest loop is not forced. $ watch that monitors foo and bar is not executed. That is to say, if we execute $ apply on our own, these $ watch will see these changes and update the DOM as needed.

Try it: http://jsbin.com/opimat/2/

If we click this directive (blue area), we will not see any changes, but when we click the button, the number of clicks will be updated. As mentioned earlier, $ digest loop is not triggered when you click directive. However, when the button is clicked, ng-click will call $ apply, then the $ digest loop will be executed, so all the $ watch will be checked. Of course, our foo and bar $ watch will be included.

Now you are thinking that is not what you want. What you want is to update the clicks when you click the blue area. It's easy to execute $ apply:

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

$ Apply is a function of $ scope (or the scope in the link function in vie cvie). Calling it forces a $ digest loop (unless the current execution loop is in progress, in this case, an exception will be thrown, which is the sign where we do not need to execute $ apply ).

Try: http://jsbin.com/opimat/3/edit

Useful! But there is a better way to use $ apply:

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

What's different? The difference is that in the first version, we update data outside angular context. Angular will never know if an error occurs. Obviously, there will be no big mistakes in this example like a small toy, but imagine if an alert box shows an error to the user, then we have a third-party library to make a network call and then fail. If we do not encapsulate it into $ apply, Angular will never know that the failure has occurred, the alert box will never pop up.

Therefore, if you want to use a jQuery plug-in and execute a $ digest loop to update your DOM, make sure you call $ apply.

Sometimes I want to say that some people will "Feel Bad" when they have to call $ apply, because they will feel that they have done something wrong. In fact, this is not the case. Angular is not a magician and he does not know that a third-party library wants to update the bound data.

Use $ watch to monitor your own things

You already know that any bound we set has its own $ watch. Update the DOM as needed, but what if we want to customize our own watches? Simple

Let's look at an example:

App. js

app.controller('MainCtrl', function($scope) { $scope.name = "Angular"; $scope.updated = -1; $scope.$watch('name', function() {  $scope.updated++; });});

Index.html

  Name updated: {{updated}} times.

This is how we create a new $ watch method. The first parameter is a string or function. Here it is just a string, that is, the name of the variable to be monitored. Here, $ scope. name (note that we only need to use name ). The second parameter is executed when $ watch says that the expression I monitored has changed. The first thing we need to know is that when the controller executes this $ watch, it will immediately execute it, so we set updated to-1.

Try: http://jsbin.com/ucaxan/1/edit

Example 2:

App. js

app.controller('MainCtrl', function($scope) { $scope.name = "Angular"; $scope.updated = 0; $scope.$watch('name', function(newValue, oldValue) {  if (newValue === oldValue) { return; } // AKA first run  $scope.updated++; });});

Index.html

  Name updated: {{updated}} times.

The second parameter of watch accepts two parameters: New Value and old value. We can use them to skip the first execution. Generally, you do not need to skip the first execution, but in this example you need it. Flexible.

Example 3:

App. js

app.controller('MainCtrl', function($scope) { $scope.user = { name: "Fox" }; $scope.updated = 0; $scope.$watch('user', function(newValue, oldValue) {  if (newValue === oldValue) { return; }  $scope.updated++; });});

Index.html

  Name updated: {{updated}} times.

We want to monitor any changes in the $ scope. user object. As before, Here we only use an object to replace the previous string.

Try: http://jsbin.com/ucaxan/3/edit

Er? No, why? Because $ watch compares the two objects by default to see if they are the same. In example 1 and 2, $ scope is changed each time. name creates a new basic variable, so $ watch executes the variable because the reference to this variable has changed. In the above example, we are monitoring $ scope. user, when we change $ scope. user. for $ scope. user references will not change, but every time we create a new $ scope. user. name, but $ scope. users are always the same.

Example 4:

App. js

app.controller('MainCtrl', function($scope) { $scope.user = { name: "Fox" }; $scope.updated = 0; $scope.$watch('user', function(newValue, oldValue) {  if (newValue === oldValue) { return; }  $scope.updated++; }, true);});

Index.html

  Name updated: {{updated}} times.

Try: http://jsbin.com/ucaxan/4/edit

It works now! Because we added the third parameter to $ watch, which is a bool parameter, indicating that we compared the value of the object rather than the reference. When we update $ scope. user. name, $ scope. user also changes, so it can be correctly triggered.

There are still many tips & tricks about $ watch, but these are the foundation.

Summary

Well, I hope you have learned how data binding works in Angular. I guess your first impression is that dirty-checking is very slow. It is lightning fast. But, yes, if you have-watches in a template, it will start to slow down. But I think if you reach this order of magnitude, You can consult a user experience expert.

In any case, with the advent of ECMAScript6, we will have Object. observe in future Angular versions, which will greatly improve the speed of the $ digest loop. At the same time, future articles will also involve some tips & tricks.

On the other hand, this topic is not easy. If you find that something important or something is totally wrong, please correct it (the original article is on github pr or report issue)

I hope this article will help you with AngularJS programming.

For more details about how data binding works in AngularJS getting started, refer to the PHP Chinese website!

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.