Angularjs two-way binding mechanism analysis

Source: Internet
Author: User
Tags tojson

Article turned from: http://www.2cto.com/kf/201408/327594.html

AngularJs element and model two-way binding depends on the loop to detect the value between them, this practice is called dirty detection, these days to study the source, the implementation of Angular to share.   First look at how to update model changes to Ui angular model is a scope type, each scope belongs to a Directive object, such as $rootScope belong to Ng-app.   from Ng-app down, each Directive created Scope will be a layer of links down, forming a $rootScope as the root of the linked list, note that Scope also has the concept of the same class, described more appropriate I think it should be a tree.   We'll probably look at what members of scope have:   function scope () {      this. $id = Nextuid ();     / /: Stage, parent scope, Watch function set, next sibling scope, previous sibling scope, first child scope, last child scope      this.$ $phase = this. $paren t = this.$ $watchers =                     this.$ $nextSibling = this . $ $prevSibling =                     this.$ $childHead = this.$ $chil Dtail = null;         //override this property to support prototype chain       this[' this '] = this. $root = &nbsp ;this;      this.$ $destroyed = false;     //on current ScopeThe following asynchronous evaluation queue, which is a bunch of Angular expressions       this.$ $asyncQueue = [];      this.$ $postDigestQueue = [];&N Bsp     this.$ $listeners = {};      this.$ $listenerCount = {};      this.$ $isolateBind ings = {};} Scope. $digest, which is the interface that Angular provides from the model update to the UI, which scope you are calling from, then it will start traversing from this scope, notifying the model to change to the individual watch functions, to see $digest Source:  $ Digest:function () {    var watch, value, last,        watchers,        Asyncqueue = this.$ $asyncQueue,        Postdigestqueue = this.$ $postDigestQueue,        length,        dirty, ttl = ttl,        Next, current, target = this,        Watchlog = [],        LOGIDX, logmsg, asynctask;    //logo stage, prevents multiple Enter     beginphase (' $digest ');    //Last watch function to detect dirty values     Lastdirtywatch = null;&nbsp ;  &NBsp Start dirty detection, as long as there is dirty value or asynchronous queue is not empty will always loop     do {      dirty = false;     ///current traversal to Scope&nbs P     current = target;      //handles All tasks in the asynchronous queue, this queue by scope. $evalAsync method input       while (asyncqueue.length) {        Try {          Asynctask = Asyncqueue.shift ();          Asynctask.scope $eval (asynctask.expression);       } catch (E) {          clearphase ();          $exceptionHandler (e);    &N Bsp  }        Lastdirtywatch = null;     }       TRAVERSESCOPESL oop:      Do {       //Take out all the watch functions in the current Scope         if (Watche rs = current.$ $watchers) {          length = watchers.length;          while (length--) {            Try {              Watch = Watchers[len gth];               if (watch) {              & nbsp 1. Take the new value of the watch function, directly compare with the last value of the Watch function                //2. If the comparison fails, try calling the Watch function's Equal function, if there is no equal function then directly compare whether the old and new values are number                if (value = Watch.get (c urrent)!== (last = watch.last) &&                   ! (watch.eq                        equals (value, last)   & nbsp                    : (typeof value = = ' Number ' && typeof last = = ' number '                            && IsNa N (value) &&IsNaN (last))) {                 //detected value change, set some logo                   dirty = true;                  LASTDIRTYW Atch = watch;                  Watch.last = Watch.eq? Copy (value, NULL): value;                 //Call the change notification function of the watch function, which means that each dir Ective update from here ui                  Watch.fn (value, (last = = = Initwatchval) v Alue:last), current);                  //When Digest calls are greater than 5 (default 10) , documented for developer analysis.                   if (TTL < 5) {                    LOGIDX = 4-ttl;                    if (!wa TCHLOG[LOGIDX]) Watchlog[logidx] = [];                    logmsg = (isfunction (watch.exp))                        ? ' fn: ' + (Watch.exp.name | | watch.exp.toString ())                     &NB Sp  : watch.exp;                    logmsg + = '; newval: ' + toJson (value) + '; Oldval: ' + ToJson (last);                    Watchlog[logidx].push (logms g);                 }                } else if (watch = = = Lastdirtywatch) {                 //if the most Recen tly Dirty Watcher is now clean, short circuit since the remaining watchers                 /already been tested.    &NBsp             dirty = false;                  BRE AK traversescopesloop;               }            & nbsp }           } catch (e) {              clearphase () &nbs P             $exceptionHandler (e);           }    &N Bsp    }       }        //Forgive me, I don't understand, but what's the bottom line?       &NBS P Insanity Warning:scope Depth-first traversal       //Yes, this code was a bit crazy, but it works And we have tests to prove it!       //This piece should being kept in sync with the traversal in $bro adcast        //No child Scope, no sibling scope        if (! ( Next = (current.$ $childHeAd | | (Current!== target && current.$ $nextSibling)))) {         //And again I don't know why, but this time next = = = Undefined, also quit the current Scope of Watch traversal     &NBSP ;     while (current!== target &&!) ( Next = current.$ $nextSibling) {            current = current $parent;      & nbsp  }       }     } while (current = next);       //when The TTL is exhausted, there are still dirty values unhandled and the async queue throws an exception       if ((Dirty | | asyncqueue.length) &&! ( ttl--) {        clearphase ();        throw $rootScopeMinErr (' Infdig ',  &nbs P         ' {0} $digest () iterations reached. aborting!\n ' +            ' watchers fired in the last 5 iterations: {1} ',    &nbsp ;       TTL, ToJson (watchlog));     }    } while (Dirty | | asyNcqueue.length);    //Exit digest stage, allow others to call     Clearphase ();     while ( Postdigestqueue.length) {      Try {        Postdigestqueue.shift () ();    &N Bsp } catch (E) {        $exceptionHandler (e);     }   } } Although it looks long, it's easy to understand , the default is to start from the $rootScope, to evaluate each watch function, the new value is called the notification function, the notification function updates the UI, we look at Ng-model is how to register the:  $scope of the notification function. $watch (function Ngmodelwatch () {    var value = Ngmodelget ($scope);    //Ng-model if Modelvalue current record is not equal to scope The latest value     if (ctrl. $modelValue!== value) {       var formatters = Ctrl. $formatters,  &N Bsp       IDX = formatters.length;      //Use formatter format new values, such as Number,email     &NBS P Ctrl. $modelValue = value;      while (idx--) {        value = Formatters[idx] (value); &nb Sp    }&NBSP;&NBSp    //update new values to ui      if (ctrl. $viewValue!== value) {        CTRL. $viewValue = value;        CTRL. $render ();     }   }     return value; }); How does UI change update to Model   very simple, by Directive compile-time binding event, such as Ng-model bound to an input box when the event code is as follows:  var ngeventdirectives = {};for Each (  ' click DblClick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress su Bmit focus Blur copy cut Paste '. Split ('), function (name) {     var directivename = directivenormalize (' ng-' + name); Ngeventdirectives[directivename] = [' $parse ', function ($parse) {     return {       compile:function ($element, attr) {        &NBS P;VAR fn = $parse (Attr[directivename]);         return function (scope, element, attr) {//trigger above specified event, the scope and event objects of the element are sent to direcive           element.on (lowercase (name), function (even T) {             scope $apply (function () {            &NB Sp &NBSP;FN (Scope, {$event: event});             });          &NBS p;});          };       }         };   }];&NBSP ; });D irective receive the input event and then Update the Model as needed.   believe that after the above research should be on the Angular binding mechanism quite understand it, now do not talk to others about the dirty detection is a while (true) has been in the evaluation of the efficiency of a good low what, and you usually use the event no different, a few more cycles.   Finally note that you usually do not need to call scope manually. $digest, especially when your code is being called back in a $digest because it has entered the digest phase so you can throw an exception if you call it again. We're only in no Scop.The code inside the e context needs to call Digest, because at this point you are Angular unaware of the UI or Model changes.

Angularjs bidirectional binding mechanism parsing

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.