RSS

Scope.$apply Life Cycle

29 מאי

Scope.$apply Life Cycle

בפוסט זה אני רוצה להסביר את סדר האירועים כאשר מבצעים $apply. בנוסף אני אעמוד על ההבדלים בין $evalAsync, $$postDigest ו- $timeout. חשוב לדעת את ההבדלים בין הפונקציות בעיקר כאשר בונים directives שמשתמשים הרבה ב- jQuery. בפוסט זה אני לא מסביר מה $apply עושה, כי על זה יש פוסט מעולה.

1. מתי קוראים ל- $apply?

כאשר נורה האירוע DOMContentLoaded אנגולר מתחיל את העבודה שלו ( פירוט מלא יותר אתם יכולים לקורא בפוסטים קודמים ) בלבצע compile על הדף ואז לקרוא ל- $apply. ראו קוד מתוך מתודה doBootstrap.

 

injector.invoke(function(scope, element, compile, injector, animate) {

     scope.$apply(function() {

                    element.data('$injector', injector);

                    compile(element)(scope);

        });

      });

 

המשמעות של הקוד הזה שהפעלת כל ה- directives על הדף מתבצעת תחת $apply.

2. אז מתי צריך לקרוא ל- $apply באופן יזום?


כאשר צריך להגיב לאירועים, למשל של ממשק משתמש או תקשורת צריך בסוף האירוע להפעיל את
$apply. דוגמת קוד מ- ng-event.

forEach(

  'click dblclick mousedown mouseup mouseover mouseout mousemove
mouseenter mouseleave keydown keyup keypress submit focus blur copy
cut paste'
.split(' '),

  function(name) {

    var directiveName = directiveNormalize('ng-' + name);

    ngEventDirectives[directiveName] = ['$parse', function($parse) {

      return {

        compile: function($element, attr) {

          var fn = $parse(attr[directiveName]);

          returnfunction(scope, element, attr) {

            element.on(lowercase(name), function(event) {

              scope.$apply(function() {

                fn(scope, {$event:event});

              });

            });

          };

        }

      };

   }];

  }

);

כאן אנחנו רואים קוד מתוחכם שמייצר directive לכל אירוע, למשל ng-click. בכל ה- directive יש רישום לאירוע (element.on()) והפונקציה שנקראת בעת האירוע קוראת ל- $apply. זאת הסיבה שכאשר אתם משתמשים ב- ng-click אתם לא צריכים לקורא ל- $apply.
אנגולר בשירותים  
$http ו- $timeout גם מפעיל את $apply
אחרי ביצוע הפונקציות שנרשמו לאירועים אסיכרונים.

אז מתי כן צריך לקרוא ל- $apply? כאשר אנחנו נרשמים לאירועים, לא דרך directive או שרות של אנגולר, צריך לבצע הפעלת $apply, לדוגמא עבודה עם $.ajax.

 

3. מה סדר הפעולות כאשר מבצעים $apply?

הקוד של $apply מאוד פשוט. אתם יכולים לראות שהוא מבצע את הקוד שלכם ואחרי זה מבצע את $digest שבודק מה השתנה בעץ של ה- scope. שוב ממליץ בחום לקורא את הפוסט הזה.

 

clip_image0024. מה סדר הפעולות ב- $digest?

 

אפשר לחלק את הפונקציה $digest לשלושה חלקים עיקריים:

1. $evalAsync Queue

2. Traverse Scope Loop (TSL)

3. $$postDigest

 

הסברים:

החלק העיקרי של $digest זה TSL, החלק הזה אחראי בקוד למצוא מה השתנה בעץ של Scopes ולהפעיל את הפנקציות שנרשמו לשינוי במידה ויש ($watch.). אם אנגולר מזהה שינוי בעץ, השדה dirty יהיה במצב true ואז צריך לחזור לבצע שוב את הסריקה על העץ ( בציור זה שני החצים הסגולים ). הסיבה שחוזרים לסרוק שוב את העץ כי יכול להיות שהפונקציה שנרשמה לשינוי ביצעה בעצמה שינוי באחד ה- Scopes. במידה ואנגולר יגיעה לסריקה ה-10 של העץ ברצף, הוא יזרוק טעות ויצא מהפונקציה של ה- $digest.

5. מתי משתמשים ב- $evalAsync?

השימוש ב-$evalAsync לא נפוץ אך במצבים מסוימים זה מאוד יכול לעזור. תחשבו שאתם רוצים למשל לבנות directive שרוצה לבצע את הפעולות שלו על ה-DOM אחרי שכל ה-directives סיימו לבצע את עבודתם. למשל כאשר בניתי Group Validation in ng-repeat הייתי חייב לבדוק אם יש כפילות רק אחרי ש- ng-repeat סיים לייצר את כל הילדים שלו ובצע את כל ה- directives על כל ילד, ולכן כתבתי את הקוד בתוך $evelAsync.

6. מתי משתמשים ב- $$postDigest ?

מאוד לא נפוץ, הדולר הכפול מסמן פונקציה פרטית. אחרי שה- $digest סיים לעבור על כל העץ של ה- scopes, השורות האחרונות של הפונקציה זה לבצע את כל המתודות שרשומות ב- $$postDigest.

דוגמא לשימוש ב- $$postDigest:

angular.module("app", [])

  .directive('postDigest', function ($document) {

      return {

        template : '<div ng-if="test">
<div id="eyal"></div>
</div>'
,

        link : function(scope, element) {

           // Not Work

           var eyal = $('#eyal', element);

           eyal.append($('<div>Link</div>'));


           // Work

           scope.$$postDigest(function() {

                var eyal2 = $('#eyal', element);

                eyal2.append($('<div>$$postDigest</div>'));

           });

         }

       }

   });

הסבר:

ב- template יש ng-if והוא מיצר את הילדים רק כאשר ה- $digest עובד ואז מופעל ה- $watch של ה- ng-if. כמו שהסברתי בסעיף 1, אחרי שאנגולר מקמפל את הדף הוא מבצע את $apply. ה- div עם ה-id='eyal' לא יהיה בעץ עד שה-$digest יעבוד וזה קורה אחרי ביצוע של הפונקציה link של postDigest directive, מכאן שאנחנו חייבים לעטוף את הקוד ב- $$postDigest כדי לבצע את הקוד אחרי שכל ה- wachers עבדו. היה אפשר להשתמש גם ב- $timeout אך זה פחות יעיל.

 

סיכום

בפוסט זה סקרתי את סדר הפעולות של $digest ו- $apply. מי שאהב את העומק של הדברים ורוצה להמשיך להעמיק בנושא מוזמן לבוא לקורס שלי AngularJS Deep Dive Course.

image

מודעות פרסומת
 
תגובה אחת

פורסם ע"י ב- מאי 29, 2014 ב- AngularJS

 

תגובה אחת ל-“Scope.$apply Life Cycle

  1. Lee Elenbaas (@LeeElenbaas)

    יוני 1, 2014 at 5:51 am

    לשימוש ב-$timeout במקום ב-$$postDigest יש ייתרון של שימוש ב-API מוצהר
    בעוד ש-$$postDigest איננו נועד לשימוש וייתכן שהשימוש הפנימי בו ישתנה בגרסאות חדשות

     

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s

 
%d בלוגרים אהבו את זה: