כתיבת אפליקצית 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 כבר מחר אני לא יודע, אני חושב שכדאי לבצע התנסות נוספת עם אפליקציה קצת יותר גדולה ומורכבת על מנת לקבוע זאת – אבל נכון לעכשיו זה עושה רושם מצויין.

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