כתיבת אפליקצית AngularJS 1.3 ב- ES6

כבר כמה חודשים שאני נלחם בחשק מוזר להתחיל לכתוב קוד ב- ECMAScript 6. היות ובשנתיים האחרונות אני עובד בעיקר עם AngularJS ולאור ההכרזות על AngularJS 2.0, זה בדיוק הזמן להתחיל ואפילו לקחת את האתגר צעד אחד קדימה – לכתוב אפליקצית AngularJS ב- ES6.
אז קדימה, מתחילים.

על מה נדבר?

אפשר לדבר על המון דברים בנוגע ל- AngularJS, באופן אישי אני מעדיף מאמרים פרקטיים ועניניים, אז מאמר בנוסח "למה אנגולר 2.0 זה אסון" לא תמצאו כאן, זה גם לא "מבוא ל- ES6". בחרתי לדבר על מיגרציה מ- AngularJS 1.x ל- AngularJS 2.0, או בעיקר על הכנה מקדימה למיגרציה שתבוא לידי ביטוי בשכתוב האפליקציה שלנו ב- ES6. כן, לכתוב ב- ES6 כבר היום, עם AngularJS 1.3 ובדפדפנים שאפילו לא לגמרי תומכים בתקן. אני רוצה לבדוק האם זה אפשרי, ואם כן – האם זה גם כדאי.

למה עכשיו?

מי שמפתח ב- AngularJS וסביר להניח שגם מי שלא, בוודאי שמע את ההכרזות האחרונות על AngularJS 2.0. מעבר לעובדה שרוב הדברים שאנחנו מכירים כיום כבר לא קיימים בגרסה החדשה, מסתבר שהפיתוח של הספריה כבר לא יהיה ב- JavaScript כפי שאנו מכירים אותה כיום – אלא ב- AtScript, שזו מעין גרסה משופרת של השפה, המתוארת כ-

AtScript = ES6 + Types + Annotations + Introspections

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

מה המוטיבציה?

המטרה העיקרית היא לאפשר מעבר חלק ופשוט יותר בעתיד הן ל- AngularJS 2.0 והן ל- AtScript (במידה ונרצה) או לפחות ל- ES6. מתישהו יהיה כדאי לעבור ל- ES6 בכל מקרה, אז בואו נבדוק האם זה אפשרי כבר היום.

היכון, הכן, צא!

אתחיל עם העובדה שהדפדפנים היום לא לגמרי תומכים ב- ES6 בצורה רחבה, כך שאנחנו לא יכולים פשוט לכתוב ב- ES6 ולהריץ כמו שאנחנו רגילים, אנחנו זקוקים לאיזשהו כלי שיגשר על הפער הזה. ישנם מס' כלים המאפשרים לנו לכתוב ב- ES6 והם למעשה דואגים להמיר את הקוד שלנו (כל אחד בצורה שלו) לקוד שיכול לרוץ כבר היום, חלקם עושים את זה ב- runtime ואת חלקם צריך לשלב בתהליך ה- build, לא אכנס לזה יותר מדי כרגע – רק אגיד שאני בחרתי ב- Traceur ובמקרה הזה אני מריץ אותו ב- runtime.

כ- PoC בחרתי באפליקציה די פשוטה המכילה View, Controller ו- Service בסיסיים.
אני יוצא מנקודת הנחה שאתם כבר מכירים את התחביר של ES6, אם לא – למה אתם מחכים? בכל אופן, במקרה הזה שילוב של היכרות עם שפות נוספות והיגיון בריא אמור להספיק גם למי שלא מכיר את השפה.

נתחיל עם ה- Service (קובץ: alerts.service.js):

export class Alerts {
  
  constructor($window) {
    this.$window = $window;
  }
  
  fire(alert) {
    this.$window.alert(alert);
  }
}

ה- Controller (קובץ: main.controller.js):

export class MainController {
  
  constructor(Alerts) {
    this.Alerts = Alerts;
  }
  
  get headerTitle() {
    return 'Hello ECMAScript 6!';
  }
  
  buttonClick() {
    this.Alerts.fire('Awesome!');
  }
}

איתחול האפליקציה (קובץ: app.js):

import {MainController} from 'main.controller';
import {Alerts} from 'alerts.service';

angular.module('app', [])
  .controller('MainController', MainController)
  .service('Alerts', Alerts);

ולבסוף ה- View (קובץ: index.html):

<!DOCTYPE html>
<html ng-cloak>

  <head>
    <title>AngularJS 1.3 application written in ECMAScript 6</title>
  </head>
  
  <body ng-controller="MainController as Main">
  
    <h1 ng-bind="Main.headerTitle"></h1>
    <button type="button" ng-click="Main.buttonClick()">Click me!</button>
    
    <!-- traceur -->
    <script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
    <script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
    <script>traceur.options.experimental = true;</script>
    
    <!-- angularjs -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
    
    <!-- bootstrap our application -->
    <script>
      System.import('app').then(function(){
        angular.element(document).ready(function() {
          angular.bootstrap(document, ['app']);
        });
      });
    </script>
    
  </body>
  
</html>

זה עובד, אבל אתם לא חייבים להאמין לי – תראו בעצמכם ב- Plunker הבא.

מסקנות

כפי שניתן לראות, עשינו את זה די בקלות. חדי העין מביניכם אפילו ישימו לב שמלבד שמות הארגומנטים שאנחנו מעבירים ל- constructor, וגם את זה ניתן לשנות בקלות אם רק רוצים – ברמת ההגדרה למחלקות שלנו אין שום מושג שהן רצות בתוך AngularJS, השיוך היחיד שלהן לספריה נעשה ב- app.js ששם אנחנו מעבירים אותן ל- AngularJS בתור Controllers/Services בהתאם לצורך. אפשר לעשות את זה גם ב- ES5 – אך לדעתי ב- ES6 זה הרבה יותר אינטואיטיבי, פשוט ומתבקש.

ניתן לשים לב שאני נותן ל- Traceur להמיר את הקוד ל- ES5 ב- runtime, יש לזה השלכות מבחינת ביצועים וסביר להניח שחלקכם ירצו לבצע את ההמרה הזו מבעוד מועד. התייחסתי לזה רק בחצי משפט במהלך הפוסט, אז אזכיר שוב שניתן לבצע את ההמרה בשלב ה- build של הפרויקט ולהעביר לדפדפן את הקובץ הסופי.

לסיכום

מבחינתי האתגר עבר בהצלחה – זה אכן אפשרי ואפילו די פשוט, אני באופן אישי גם מוצא בזה מס' יתרונות בלי קשר ל- AngularJS 2.0 ולמיגרציה עתידית, יתרונות שבאים לידי ביטוי כבר היום.
אם כדאי להעביר פרויקטים ל- ES6 כבר מחר אני לא יודע, אני חושב שכדאי לבצע התנסות נוספת עם אפליקציה קצת יותר גדולה ומורכבת על מנת לקבוע זאת – אבל נכון לעכשיו זה עושה רושם מצויין.

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

2 תגובות בנושא “כתיבת אפליקצית AngularJS 1.3 ב- ES6”

  1. אחלה פוסט! מסביר את כל הדברים המעניינים בפשטות.

    כמה שאלות לי:
    1. האם אכן ה DI מתבצע אך ורק לפי הפרמטרים ל constructor, והאם לא היית אמור להשתמש באיזשהן annotations בשביל זה
    2. אתה מבצע את ה bootstrap מתוך ה HTML, האם יש איזשהי דרך טובה יותר ב Angular 2

    תודה!

    1. אהלן,

      1. שים לב שזה עדיין אנגולר 1.3 ולכן כמו שאמרת הוא קורא את שמות הפרמטרים ומבין באמצעותם מה הוא צריך להעביר להם. לצרכי מיניפיקציה או אם הייתי רוצה להגדיר את התלויות בעצמי, הייתי יכול עדיין להגדיר משהו כזה בדיוק כמו היום (לדוג' ב- MainController):

      export class MainController {
      ....
      }
      
      MainController.$inject = ['Alerts'];
      

      באנגולר 2.0, ניתן להגדיר את התלות בצורה דומה כך:

      MainController.parameters = [{is:Alerts}];
      

      או כך, במידה ועובדים עם AtScript:

      constructor(alerts:Alerts) {
      ..
      }
      

      – עד כמה שידוע כרגע לפחות, זה עוד עשוי להשתנות.

      2. ניתן להעביר את ה- callback שאני מעביר ל- System.import לתוך הקובץ app.js עצמו וזה יעבוד גם כן, ואז מה שנשאר ב- html זה הקריאה לסקריפט עצמו כמו שאנחנו רגילים היום.
      לגבי אנגולר 2.0, אני לא לגמרי סגור איך מאתחלים אפליקציה, הבנתי שהם הורידו לגמרי את המודולים שלהם וזה יעבוד עם המודולים של ES6, ראיתי איזו דוגמה פעם אבל אני לא זוכר איך בדיוק זה היה.

      יצרתי דוגמה נוספת המדגימה את 2 השינויים הללו: http://plnkr.co/edit/NdfC1d?p=preview

      תודה 🙂

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *