2013-10-15

שלום, אנגולר! (Angular.js)

לפני כשנה, כתבתי סדרת הפוסטים על MVC בצד הלקוח, והתמקדתי ב 3 ספריות:
  • Backbone
  • Knockout
  • Ember
הזכרתי, כדרך אגב, את Angular.js של גוגל, שנראתה לי אז עוד אחת מיני רבות.

במהלך השנה האחרונה, Angular הפכה לסופר-פופולרית: שנייה לאחר Backbone ע"פ Github, וכנראה ראשונה בקצב הצמיחה והחשיפה התקשורתית. ייתכן והיא הייתה מספיק משמעותית גם לפני שנה - ופספסתי זאת.

במשך זמן רב, הייתי שרוי תחת מסתורין: שמעתי חצאי-אמירות לגבי אנגולר, אך לא הצלחתי להרכיב מהן תמונה מציאותית / יציבה: מהי בעצם אנגולר ומה היא עושה. 

בפוסט זה אני רוצה לבצע הכרות זריזה עם אנגולר (Angular), הספרייה שכולם מדברים עליה, ולהציג תמונה מציאותית (אפילו אם חלקית) על אופייה.


משמעות המילה Angular = בעל פינות או זוויות חדות. אולי השם בא לבטא של Angular יש תפיסות חד-משמעיות?!

אז מה היא אנגולר (AngularJS)?


בכדי לענות על השאלה לעומק, נדרשים כנראה 3-4 פוסטים, אבל הנה כמה נקודות מעניינות:
  • אנגולר היא ספריית MVC בצד הלקוח. היא כוללת מודל, View, Controller ו Services.
  • בניגוד ל Backbone וכמו אמבר - זוהי ספרייה בעלת דעה ברורה, שלקחה החלטות עבור המשתמש. המשמעות: פחות שורות קוד שיש להקליד - אך גם פחות גמישות.
  • בניגוד לספריות אחרות, ובצורה דיי ייחודית, אנגולר משתלבת בצורה "טבעית" למדי בתוך ה HTML, במקום לייצר (כמו ספריות אחרות) הפשטות מעל ה HTML.
  • אנגולר נכתבה בראייה מרכזית של בדיקתיות (Testability). ניתן לכתוב לה בקלות בדיקות יחידה[א], ניתן לכתוב לה בקלות בדיקות אינטגרציה/מערכת. זו לא טענה שקל לטעון בעד אמבר (EmberJS) - למשל.
    אנגולר היא ספריית-האם של Karma (כלי מצוין להרצת בדיקות בג'אווהסקריפט, שאנו משתמשים בו בלי-קשר).
    בכדי לתמוך בבדיקות יחידה, אנגולר כוללת מנגנון Dependency Injection [ב] מובנה.
    כל אלה = כבר סיבה טובה להעריך את אנגולר!
  • אנגולר אינה תלויה בשום ספריה אחרת, והקהילה שלה בעצם מעודדת אי-שימוש ב jQuery. היא כוללת תת-ספריה שנקראת jQuery Light המספקת כמה יכולות בסיסיות מקבליות ליכולותיה של jQuery ובתחביר דומה מאוד.
  • אנגולר תואמת AMD, אם כי לא ל require.js.
  • לאנגולר יש אינטגרציה עם ספרייה "לוהטת" אחרת: Twitter Bootstrap.
    אנגולר לא מייצרת UI עשיר ויפה (אלא רק Markup - ממש כמו ספריות MVC אחרות). Bootstrap משלימה גם את היופי וגם מתאימה לפילוסופיה של אנגולר. השלמה זו צריכה לעבוד יפה גם עם אלטרנטיבות ל Bootstrap כמו Foundation, Neat, Pure או Semantic.
  • לאנגולר מנגנון Two-Way Data-Binding יעיל (פרטים בהמשך).
  • לאנגולר יש מנגנון Templating ייחודי משלה (פרטים בהמשך).
  • ה "Sweet Spot" של אנגולר היא אפליקציות עסקיות, ואפליקציות CRUD בפרט (הזנת ושליפת נתונים). אנגולר לא מתאימה, למשל, למשחקים או ל UI לא-שגרתי.
  • אנגולר פופולרית: אנגולר היא ה "A" ב "MEAN" - סט טכנולוגיות פופולרי שמתתעד להיות התחליף המודרני ל LAMP.
  • אנגולר איננה פשוטה כמו Backbone, לוקח זמן ללמוד אותה ולהתמצא בפרטים.
  • אנגולר טוענת שהיא "Forward Looking" ומיישמת היום תקני ווב עתידיים / נראית כמו שהווב יראה עוד כמה שנים.
    יש דיבורים על כך שספרייה חדשנית אחרת של גוגל, Polymer, תשתלב לבסוף באנגולר.
    אני לא מתייחס לטיעונים האלו ברצינות, אנו יודעים למי ניתנה הנבואה...
  • אנגולר היא מבית גוגל, מה שאומר שיש מאחוריה מפתחי ווב מוכשרים אבל גם מנגנון יחסי-ציבור חסר-תקדים. פרויקטים של גוגל נוטים לעשות המון באזז עוד לפני שהם באמת הצליחו, מה שמותיר ספק אם לאנגולר צפוי מחזור חיים דומה לזה שהיה ל GWT.


הנה דוגמה המשבחת את אנגולר ומראה כיצד מעבר מ BB לאנגולר צמצם בצורה משמעותית את כמות הקוד. הגיוני.



שלום עולם!


בואו נראה דוגמת קוד קטנה באנגולר:


  1. שימו לב שדף ה HTML באנגולר הוא משמעותי. ראשית הוספנו סקריפטים - כמו כל דף HTML.
    נכון, היה יעיל יותר להוסיף את תגיות ה script לאחר ה <body>, דילגתי על שיפורי ביצועים לצורך פשטות הקוד.
  2. בתוך ה body הוספתי H1 והגדרתי את HelloWorldCtrl כקונטרולר האחראי על אזור זה (תג ה H1). הסיומת Ctrl מקובלת באנגולר לסימון קונטרולרים. צירוף האותיות ng משמש לכל ה properties / תגיות שאנגולר מוסיף ל HTML.
    אנגולר, במקום לבצע הפשטה של ה DOM/HTML - מרחיב אותו. קידומת ה data היא רשות אם אתם רוצים לשמור את קובץ ה HTML שלכם תקני. ניתן (ומקובל) להשתמש פשוט בקיצור, כגון ng-controller, וכך לחסוך קצת הקלדה [ג].
  3. בתוך תגית ה HTML הכנסנו ביטוי בשם message. אם עבדתם פעם עם mustache - אתם מכירים את השפה: סוגריים מסולסלים כפולים עוטפים ביטוי שיחושב.
  4.  באופן דומה למדי, גם הביטוי {{1 + 1}} יחושב. הבדל קטן: הוא לא דורש context (באנגולר נקרא: scope) בעוד הביטוי {{ message }} דורש context - והוא יקבל אותו מה controller האחראי על האזור.
  5. קוד הג'אווהסקריפט שלנו פשוט למדי: הגדרנו קונטרולר (התאמה ע"פ שם) שברגע ההפעלה שלו רושם על ה context/scope ערך בשם message. קידומת $ למשתנים (קרי scope$) מסמלת שאלו משתנים מיוחדים של אנגולר.
  6. כשספריית אנגולר נטענת, אנגולר מפענח את ה DOM ומחפש אחר תוית data-ng-app/ng-app. תוית זו מסמנת את אזור האפליקציה של אנגולר. אפליקציות אנגולר יכולות להתארח או לחיות זו לצד זו.
    לאחר מכן אנגולר ימשיך לפענח את ה DOM וימצא את data-ng-controller.
    בשלב הבא הוא יאתחל את ה controller ואז בעזרתו יחליף את {{ message }} בביטוי הרצוי.

הנה התוצאה המדהימה של האפליקציה שלנו:


כפי שאתם רואים, אנגולר "התעסקה" לנו ב HTML markup:


זו חלק מהשיטה.

סיכום ביניים:
  • ה HTML הוא מרכזי בפתרון. לא כמו Backbone שמעדיפה HTML עם DIV אחד ריק.
  • Controller הוא מעין "מנהל אזורי"
  • חלק משמעותי מהכתיבה באנגולר (ה View / HTML) - הוא דקלרטיבי.


שלום עולם 2: Data-Binding דו-כיווני

קשה להגדיר את הדוגמה הקודמת כדוגמה מייצגת אפליקציית אנגולר - רק בגלל שאנגולר היא מקיפה ולה כלים רבים. הדוגמה הבאה תשלים אזור חשוב מאוד באנגולר: ה 2WDB (קיצור של Two-Way Data Binding).

2WDB אומר בקיצור:
  • ברגע שמעדכנים את המודל - ה View מתעדכן אוטומטית.
  • ברגע שמעדכנים את ה View - המודל מתעדכן אוטומטית.
  • (אין מה לדאוג: אין לופ אינסופי)
מנגנון זה הוא אחד מ"חוסכי הקלדת ה boilerplate code" הגדולים של אנגולר באפליקציות CRUD: רישום לאירועים ועדכון מודלים או Views - קוד טכני שאין בו הרבה חוכמה.

בואו נתבונן בדוגמה הבאה:


  1. קישרנו את שדה ה input ל מודל בשם message. היכן "מחלקת המודל" בקוד ג'אווהסקריפט? - לא קיימת, במקרה זה היא implicit.
  2.  הביטוי {{message}} נמצא בתחום השיפוט של השליט האזורי - הרי הוא ה HelloWorldCtrl. המודל שהגדרנו בשלב 1, מתיישב על ה scope$ של הקונרטולר כפרמטר ובו הערך של שדה ה input. הגדרת HTML זו בלבד (ללא קוד הג'אווהסקריפט) מספיקה בכדי לייצר databinding עובד. הנה לינק לדוגמה עובדת, ללא כל קוד ג'אווהסקריפט.
    כאשר לא מוגדר controller, אזי ה binding ייעשה על ה scope$ הגלובאלי.
  3. ביצעו אתחול של הטקסט לערך התחלתי.
  4. הוספנו האזנה (watch$) על המודל של ה message. כל פעם שיש שינוי נמיר מופעים של האות e ל UpperCase. שימו לב שבעקבות ה 2WDB, הערך ההתחלתי שקבענו ("!Hello World") כבר עובר טרנספורמציה. הנה דוגמת קוד חיה. נסו להקליד e קטנה בתיבת הטקסט ותראו מה קורה.

כל שינוי שנעשה ב View (תיבת הטקסט) משפיע על המודל (אובייקט ג'אווהסקריפט בזיכרון, במקרה שלנו הוא בלתי-מפורש). בקונטרולר אנו מנטרים את השינוי במודל (watch$) ומבצעים בו שינוי נוסף - שחוזר ומעדכן את ה View (תיבת הטקסט). זהו ה 2WDB של אנגולר.



ה Directive

אחד המרכיבים המסתוריים של אנגולר הם ה Directives, בואו נציץ בהם ונראה במה מדובר.
בוודאי שמתם לב שהשתמשנו בתגיות HTML ו Properties שאינם קיימים ב HTML5, כל ה *-ng.
אנגולר מרחיבה את שפת ה HTML [ד] בשפה משלה, ובעצם מעניקה למפתח את היכולת להמשיך ולהרחיב את שפת ה HTML.

איך בדיוק זה עובד? - מעקב אחר ה LifeCycle המלא הוא מעבר להיקף של פוסט זה [ה], אך אספק הסבר קצר, ואני מניח שרוב הקוראים יצליחו להרכיב תמונה מהירה ולא רחוקה מהמציאות של מה שמתרחש.

לאנגולר יש Compiler (רכיב מרכזי) שסורק את ה HTML (הקיים והמתעדכן) ומבצע בו שינויים. אם הוא מזהה תגית לא סטנדרטית, למשל "lb-superlink", הוא יחפש רכיב (מעין "plugin" לקומפיילר) שמתאר את האלנמט הזה ויאמר לו מה לעשות. רכיב כזה נקרא Directive.

מספר directives מסופקים, מהקופסא, עם אנגולר: למשל ng-repeat - רכיב שיכפיל קטע markup לכל רכיב ברשימה שמופיעה במודל (למשל: lineItems).

מתכנתים יכולים להרחיב ולכתוב directives בעצמם. הנה קוד של directive פשוט למדי, ה (Superlink (tm - תגית המספקת טקסט אסתטי שניתן ללחוץ עליו :



  1. lb-superlink הוא הדירקטיב (directive) שמיד נגדיר. בעת לחיצה על הלינק - תופעל הפונקציה ()foo.
    כפי שניתן לראות, ה IDE מזהיר אותי שזה אלמנט לא סטנדרטי ב HTML.
  2. כפי שציינו בהקדמה, אנגולר תואמת למערכת המודולים של AMD. בחרתי בדוגמה זו להראות קוד קצת יותר "מציאותי", אשר מארגון את הקוד סביב המודול myModule.
  3. על דירקטיב (directive) להיות מוגדר עם שם camelCase, כאשר אנגולר מפרק את השם ליחידות ותאפשר מספר צורות של תחביר ב HTML לגשת לדירקטיב שהוגדר. למשל, שמו של ng-repeat הוא בעצם "ngRepeat" (בצורת camelCase) וניתן גם לקרוא לו בצורות כגון ng_repeat, ng:repeat או data-ng-repeat.
    באופן דומה יהיה ניתן להפעיל את הדירקטיב שלנו בצורת lb-superlink, lb_superlink וכו'.
  4. התחביר ליצירת אובייקטים שהם instantiated באנגולר הם ע"י פונקציה המחזירה אובייקט Prototype (כלומר: תבנית העיצוב בשם Prototype), המוגדר בתוך ה return. זהו תחביר קיים (אם כי לא מאוד נפוץ) להגדרת מחלקות בג'אווהסקריפט.
  5. restrict מגדיר כיצד יהיה ניתן להשתמש בתוך ה HTML בדירקטיב שלנו. "E" הוא קיצור של אלמנט, קרי <lb-superlink>. אם היינו כותבים "AE" היה ניתן להגדיר את הדירקטיב גם כ attribute על אלמנט קיים קרי <span lb-superlink>.
  6. ה template מתאר את ה markup שהדרקטיב שלנו ייצר. בחרנו ב div פשוט עם label עבור accessibility. עוד פרטים - בהמשך.
  7. כאשר replace הוא אמת - התג המקורי <lb-superlink> יוסר מתוך ה HTML ע"י הקומפיילר. ניתן להשאיר את התג הקיים או להחליף אותו.
  8. transclude אומר שאנו רוצים לקחת ערכים מתוך התג המקורי <lb-superlink> ולהציב אותם בתוך התג כ ng-transclude ב template. במקרה שלנו: תג ה label.
  9. link הוא ערך חשוב. הוא בעצם פונקציית ה processing שתופעל בנוסף לפעולות הסטנדרטיות שהוגדרו ע"י ה properties הנ"ל. כאן אנו יכולים לכתוב קוד ג'אווהסקריפט חופשי משלנו. במקרה שלנו אנו מבצעים bind לארוע לחיצה על האלמנט שנוצר. השתמשנו כאן ב jQuery Light שמגיע עם אנגולר ("bind"). כפי שניתן לראות - התחביר דומה מאוד.
  10. קוד זה (eval=evil) נראה קצת פחות אסתטי, אך הוא מקובל באנגולר. אנו קוראים את ערך ה attribute בשם click (במקרה שלנו: "()foo"), ומבצעים לו eval, במסגרת ה scope הרלוונטי לאלמנט. בת'כלס אנו מפעילים את פונקציית foo שהוגדרה על ה scope$ של הקונטרולר.

הנה ה markup שנוצר (כפי שאתם זוכרים, אנגולר "מעשיר" את ה markup בביטויים כגון ng-scope):


סיכום

עשינו סקירה + צלילה-מהירה לתוך אנגולר, ספרייה שתופסת לאחרונה תשומת לב וקהל מעריצים רב. ברור שנותר רב נסתר על הגלוי, אל אני מקווה שהצלחתי בקריאה של 5 עד 10 דקות להסיר חלק מהמסתורין מסביב לאנגולר לאלו שאינם מכירים. נראה לי חשוב ומעניין להכיר, אפילו במעט, ספרייה שמשפיעה במידה לא-מבוטלת על עולם ה FrontEnd.


שיהיה בהצלחה!

----

[א] אם אתם רוצים לקרוא מעט יותר על בדיקות יחידה: על בדיקות יחידה.

[ב] אם אתם רוצים לקרוא מעט יותר על Dependency Injection: להבין Dependency Injection.

[ג] בעצם, אנגולר הפרה את תקן ה HTML5 לאורך זמן, ורק לאחרונה הוסיפה את האופציה להוסיף קידומת -data ולציית לתקן.

[ד] אנגולר מתבססת תקן עתידי של Custom Elements, שאם יתוקנן כמתוכנן - יהפוך את התחביר של אנגולר ל HTML5 תקני.

[ה] תוכלו להתעמק בנושא לא-פשוט זה בעזרת מדריך ה Directives של אנגולר: http://docs.angularjs.org/guide/directive


לינקים אחרים

הרבה תוכן מעמיק על אנגולר, גם בעברית, בבלוג של אייל ורדי

סיכום של שנתיים עבודה עם אנגולר, ע"י Alexey Migutsky

Weekly על אנגולר, שכולל בערך אינסוף לינקים (לא סתמיים) בנושא: http://syntaxspectrum.com/




16 תגובות:

  1. אנונימי15/10/13 14:19

    תודה. מעניין מאוד.

    השבמחק
  2. אנונימי18/10/13 21:20

    תודה רבה :)
    נשמח מאוד לראות פוסטים נוספים בנושא!

    השבמחק
  3. אנונימי19/10/13 14:41

    פוסט מעולה ! (כמו יתר הפוסטים),
    ישר כח !

    השבמחק
  4. אנונימי23/10/13 11:55

    פוסט לעניין!
    ישר כוח!

    השבמחק
  5. אנגור די מורכבת. לזרוק הכל על השולחן לפי דעתי זה לא פתרון טוב, יש ללמד אנגולר כשלב אחרי שלב.

    השבמחק
  6. אנונימי9/1/14 14:28

    ניסיתי ללמוד אנגולר מכמה מקומות , זה המקום שהיה לי הכי ברור ומובן :)
    תודות

    השבמחק
  7. אנונימי20/5/14 20:52

    This post gave me good start point in Angular..

    השבמחק
  8. אני מצטרץ לאנונימי, זה המדריך הראשון הנורמלי בעברית
    אם תרחיב מעט יותר, תבורך

    השבמחק
  9. אנונימי19/10/14 10:28

    תודה רבה על הפוסטים,
    קראתי את המדריכים בנושאי javascript ואנגולר והם מצויינים.
    אשמח אם תרחיב עוד בנושא אנגולר.

    השבמחק
  10. אנונימי22/10/14 12:52

    תודה רבה לעזרה
    פוסט ברור, מסודר ,פשוט מצוין...

    השבמחק
  11. זמן לפוסט כזה על אנגולר 2/4...

    השבמחק
    תשובות
    1. היי דני,

      כתיבת פוסט היא השקעה לא קטנה, כפל כפליים - אם זה נושא שהכותב לא מתמצא בו ונדרש ללמוד על בוריו (מעולם לא נגעתי באנגולר 2/4). בזמן המוגבל שעומד לרשותי - אני כותב על נושאים שקשורים לעבודה שאני בכל מקרה עושה, או מעניינים אותי באופן מיוחד, ולכן אני משקיע ולומד אותם.

      אם אתה מוכן לתרום פוסט בנושא, או מכיר מישהו שמוכן - אשמח לפרסם אותו (בהנחה שהוא עומד בסטנדרטים של הבלוג).

      ליאור

      מחק
  12. אנונימי29/11/17 13:34

    תודה!!
    רק קצת חפיף מידי.......
    נגעת במספר נקודות מאוד "מלמעלה".
    לא עדיף להתמקד בפחות, אך לעשות זאת באופן יסודי?!
    בכ"א, ממש תודה!!

    השבמחק