Study on the bidirectional binding mechanism of data in Angularjs _angularjs

Source: Internet
Author: User
Tags html tags

Angular JS (angular.js) is a set of frames, templates, and data binding and rich UI components that are used to develop Web pages. It supports the entire development process, providing a framework for Web applications without the need for manual DOM operations. Angularjs is very small, only 60K, compatible with the mainstream browser, and jQuery with good. Bidirectional data binding may be the coolest and most practical feature of ANGULARJS, and the principle of MVC will be vividly demonstrated.

Angularjs's work is that HTML templates will be parsed into the DOM by the browser, and the DOM structure becomes the input of the Angularjs compiler. Angularjs will traverse the DOM template to generate the corresponding NG instruction, all of which are responsible for setting data bindings for view (that is, Ng-model in HTML). Therefore, the NG framework does not begin to work until the DOM load is complete.

In HTML:

<body ng-app= "Ngapp" >
 <div ng-controller= "Ngctl" >
  <label ng-model= "MyLabel" ></label >
  <input type= "text" ng-model= "Myinput"/> <button ng-model= "MyButton"
  ng-click= "btnclicked" ></button>
 </div>
</body>

In JS:

Angular app
var app = Angular.module ("Ngapp", [], function () {
 console.log ("Ng-app:ngapp");
});
Angular controller
app.controller ("Ngctl", [' $scope ', function ($scope) {
 console.log ("Ng-controller: Ngctl ");
 $scope. MyLabel = "text for label";
 $scope. Myinput = "text for input";
 $scope. btnclicked = function () {
  Console.log ("Label is" + $scope. MyLabel);
 }
}]);

As above, we first define a angular app in HTML, specifying a angular controller, the controller corresponds to a scope (you can use the $scope prefix to specify the properties and methods in the scope, and so on). Then the HTML tag in the scope of the Ngctl, whose value or operation can be bound to the properties and methods in JS in a $scope way.

This enables Ng's bidirectional data binding: That is, the view presented in HTML is consistent with the data in Angularjs. If you modify one, the other end will change accordingly.

In this way, it's really convenient to use. We only care about the style of HTML tags and their corresponding properties and methods that are bound under the angular controller scope in JS. That's all, omitting the many complex DOM operations.

This idea, in fact, with jquery Dom Query and operation is completely different, so there are many people suggest to use ANGULARJS, do not mix jquery. Of course, both have their pros and cons, use which depends on their choice.

The app in Ng is the equivalent of a modular module, where multiple controller can be defined in each app, and each controller will have its own scope space and will not interfere with each other.

How the binding data is effective
Beginner Angularjs may step into such a pit, assuming there is an instruction:

var app = Angular.module ("Test", []);

App.directive ("Myclick", function () {return
  function (scope, element, attr) {
    element.on ("click", Function () {
      scope.counter++;
    });

}; App.controller ("Counterctrl", function ($scope) {
  $scope. counter = 0;
});
<body ng-app= "Test" >
  <div ng-controller= "Counterctrl" >
    <button myclick>increase</ button>
    <span ng-bind= "Counter" ></span>
  </div>
</body>

This time, click the button, the number on the interface will not increase. Many people will be puzzled, because he looked at the debugger, found that the data has indeed increased, angular is not two-way binding, why the data changes, the interface does not follow the refresh?

Try to add a scope.digest () after scope.counter++ this sentence, and see if it's okay?

Why would you do that, and when would you do that? We found no digest in the first example, and if you write digest, it throws an exception and says it's doing other digest, what's going on?

Let's think about it, but what if we don't have angularjs and we want to do it ourselves?

<! DOCTYPE html>  

As you can see, in such a simple example, we do some two-way binding things. From the click of the two buttons to the change of the data, this is well understood, but we do not use the DOM's OnClick method directly, but we have a ng-click, and then in bind the corresponding function of the Ng-click is taken out and bound to the OnClick event handler function. Why would you do that? Because the data has changed, but not yet populated to the interface, we need to do some additional operations here.

On the other hand, when the data changes, you need to apply the change to the interface, which is three spans. But since angular uses dirty detection, it means that when you change the data, you do something to trigger the dirty check and then apply it to the DOM element that corresponds to the data. The problem is, how do you trigger dirty detection? When does it trigger?

We know that some setter-based frameworks can reassign the binding variables on the DOM elements when setting values for the data. The mechanism of dirty detection is not at this stage, it has no way to be notified immediately after the data change, so only apply () can be manually invoked in each event entry to apply the data changes to the interface. In the real angular implementation, this is the first dirty detection, to determine the data changes, and then the interface to set the value.

So, we encapsulate the real click in the Ng-click, the most important function is in order to append once to apply (), the data changes to apply to the interface up.

So, why in Ng-click inside call $digest, will the error? Because the design of angular, the same time only allow one $digest to run, and ng-click this built-in instruction has triggered the $digest, the current has not gone out, so the error.

$digest and $apply
in angular, there are $apply and $digest two functions, which we have just used to get this data into the interface by $digest. But this time, also can not $digest, but use $apply, the effect is the same, then, what are their differences?

The most direct difference is that $apply can take a parameter, it can accept a function, and then call the function after applying the data. So, generally in the integration of Angular framework code, you can write code in this call.

var app = Angular.module ("Test", []);

App.directive ("Myclick", function () {return
  function (scope, element, attr) {
    element.on ("click", Function () {
      scope.counter++;
      Scope. $apply (function () {
        scope.counter++;
      });
    });
  }

; App.controller ("Counterctrl", function ($scope) {
  $scope. counter = 0;
});

Besides, is there any other difference?

In a simple data model, there is no essential difference between the two, but when there is a hierarchy, it is different. Given the two-layer scope, we can call the two functions up in the parent scope, or we can invoke them on the child scope, and this time we can see the difference.

For $digest, there is a difference between calling on a parent scope and a child scope, but for $apply, the two are the same. Let's construct a special example:

var app = Angular.module ("Test", []);
      App.directive ("Increasea", function () {return function (scope, element, attr) {Element.on ("click", Function () {
      scope.a++;
    Scope. $digest ();
  });
};

});
      App.directive ("Increaseb", function () {return function (scope, element, attr) {Element.on ("click", Function () {
      scope.b++;  Scope. $digest ();
  This can be replaced by $apply});
};

});

  App.controller ("Outerctrl", ["$scope", function ($scope) {$scope. A = 1;
  $scope. $watch ("A", function (newval) {Console.log ("A:" + newval);

  });
  $scope. $on ("Test", function (evt) {$scope. a++;
});

}]);

  App.controller ("Innerctrl", ["$scope", function ($scope) {$scope. B = 2;
    $scope. $watch ("B", function (newval) {Console.log ("B:" + newval);
  $scope. $emit ("Test", newval);
});
}]); <div ng-app= "Test" > <div ng-controller= "Outerctrl" > <div ng-controller= "Innerctrl" > <butt On Increaseb>increase b</button> <span ng-bind= "b" ></span> </div> <button increasea>increase a</button> <span ng-bind

 = "a" ></span> </div> </div>

At this point, we can see the difference, the increase B button on the click, at this time, the value of a and B has actually changed, but the interface of a does not update, until the click of a increase a, the sum of a just now will be updated. How to solve this problem? Only in the implementation of INCREASEB this instruction, $digest replaced $apply can be.

When $digest is invoked, only monitoring on the current scope and its child scopes is triggered, but when $apply is invoked, all monitoring on the scope tree is triggered.

Therefore, in terms of performance, if you can determine the scope of the impact of this data change, you should try to call $digest, only when you can not accurately know the extent of the impact of data changes, to use $apply, very violent traverse the entire scope tree, call all of the monitoring.

From another angle, we can also see why it is recommended to put in $apply when calling an external frame, because this is the only place where all data changes are applied, and it is possible to temporarily lose data changes with $digest.

The pros and cons of dirty detection
Many people are dismissive of angular's dirty detection mechanism, advocating the observation mechanism based on setter,getter, it seems to me that this is just the different ways to achieve the same thing, and no one is better than who, both of them have their pros and cons.

As you all know, when you add DOM elements in a loop, it's recommended that you use DocumentFragment, because if you make changes to the DOM every time, it's going to change the structure of the DOM tree, and the performance impact is great, if we can first create the DOM structure in the document fragment, Then the whole is added to the main document, and the change of the DOM tree is done once and the performance improves a lot.

Similarly, in the angular framework, consider such a scenario:

function Testctrl ($scope) {
  $scope. numofcheckeditems = 0;

  var list = [];

  for (var i=0; i<10000; i++) {
    List.push ({
      index:i,
      checked:false
    });
  }

  $scope. list = list;

  $scope. togglechecked = function (flag) {for
    (var i=0; i<list.length; i++) {
      list[i].checked = flag;
      $scope. numofcheckeditems++
    }}
  ;
}

What happens if a text on the interface binds to this numofcheckeditems? Under the mechanism of dirty detection, this process is no pressure, once all data changes are done, then the whole application to the interface. At this point, the setter based mechanism is miserable, unless it is also like angular to delay the bulk operation to an update, otherwise the performance will be lower.

So, two different monitoring methods have their advantages and disadvantages, the best way is to understand the differences in their use, considering their performance differences, in different business scenarios, avoid the most likely to cause bottlenecks in the use of performance.

Related Article

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.