es6

A 4-post collection

Angular's Directive Wrapper (ES6)

ความเดิมตอนที่แล้วที่ผมแนะนำวิธีการเขียน Angular Directive ด้วย ES6 นั้น ... ผมพบว่าตัวผมเอง ผิดถนัด และตอนนี้ผมได้เตรียมตัวมาแก้ตัว เอาเป็นว่าเป็นความพยายามครั้งที่สองของผมในการทำให้การใช้งาน directive มีเหตุผลมากขึ้น อ่านง่ายขึ้น เขียนง่ายขึ้น จัดการง่ายขึ้นแล้วกันนะครับ

โค้ดหลักอยู่ที่ https://github.com/phizaz/angular-directive-wrapper

วิธีการของผมก็คือผมสร้างไฟล์ directive.js ขึ้นมาเพื่อเป็น Singleton สำหรับจัดการงานเกี่ยวกับ สร้าง directive (โดยเฉพาะ isolated scope ซึ่งเป็นเป้าหมายหลักของการใช้งาน; ใครใช้ shared-scope โปรดเลือกเขียนตามใจชอบ ๕๕๕) เพราะว่าโดยปกติ directive ประกอบไปด้วย controller และ link ซึ่งไม่ได้ทำงานพร้อมกัน และไม่ได้มีเป้ามหายเหมือนกันซะทีเดียว แต่ส่วนตัวแล้วผมเห็นว่ามันเป็นการยากโดยใช่เหตุที่จะต้องแยกสองสิ่งเหล่านี้ออกจากกันโดยสิ้นเชิง และผมยังเห็นอีกว่าเมื่อ directive ของเราใหญ่ขึ้น ๆ การจัดการให้โค้ดต่าง ๆ อยู่ในร่องในรอยก็ทำได้ยากขึ้น จึงเสนอวิธีจัดการโค้ดออกมาจากใน directive.js นี้ด้วย

โดยผมจะสร้างตัวแปร private พิเศษ ไว้สำหรับเชื่อม ตัวแปร this ของ controller และ this ของ link อันที่จริงแล้วผมเชื่อมมันทุกอย่างด้วยตัวแปร this เลย ทำให้ทุก ๆ ที่สามารถเรียกของจากที่ ๆ หนึ่งได้เลย หากว่ามันมีอยู่

ผมว่าพูดไปก็คงเข้าใจได้ยาก ดูตัวอย่างเลยดีกว่ามาหน้าตา wrapper นี้เป็นอย่างไร แล้วมันจะช่วยเราได้จริง ๆ หรือไม่ ผมว่ามันรอให้ทุก ๆ คนตัดสินอยู่

ตัวอย่างการใช้งาน

import angular from 'angular';  
//importing directive.js
import Directive from './directive';

angular  
  .module('TestDirectiveModule', [])
  .directive('Test',
    () => {

      return Directive.new({

        controllerAs: 'my',
        template: '<test></test>',

        // this replaces the normal 'scope'
อ่านต่อ »

Angular's Directive ES6 Era

ปกติ directive ที่เขียนอยู่ใน AngularJS official documentation จะแนะนำให้เราเขียนแบบนี้

var app = angular.module('app', [])  
app.directive('directiveName',  
  function () {
    return {
      restrict: 'E',
      // isolate scope
      scope: {},
      controller: function () {
        ...
      },
      controllerAs: 'loginBox',
      link: function ($scope, element, attrs) {
        ...
      },
      template: '<div>template here</div>',
    };
  });

แต่นั่นก็คือวิธีการเขียนแบบ es5 ซึ่งตอนนี้ยุคของ es6 กำลังมาถึง ก็อาจจะถึงเวลาที่เราจะปรับตัวเองไปสู่สิ่งที่ดีกว่า แน่นอนว่า es6 จะช่วยให้เราเขียนได้งดงามขึ้น และสมเหตุสมผลมากขึ้น โดยอาศัย class เข้าช่วย

import _ from 'lodash';  
import angular from 'angular';

class SomeDirective {  
  constructor() {
    _.extend(this, {
      restrict: 'E',
      // create its isolate scope that will not interfere with
      // the outside world
      // scope is equivalent to `this` in the class
      scope: {},
      // always use bindToController
      // so that the code will work as expected
      bindToController: true,
      // this is var's name to be used in template
      // to talk about controller's `this` or scope
      controllerAs: 'ctrl',
      template: loginBoxTemplate,
    });
  }

  controller() {
    ...
  }

  link($scope, element, attr) {
    ...
  }
}

let app = angular.module('app', []);  
app.directive('directiveName', () => new SomeDirective());  

โดยภาพรวมแล้วโค้ดอันใหม่ทำงานเหมือนโค้ดอันเก่าทุกประการ แต่ว่าตอนนี้โค้ดของ directive ทั้งก้อนรวมเป็นก้อนเดียวกันแล้ว ในรูปที่เป็นระเบียบมากขึ้น คือ class

ที่ต้อง new SomeDirective() ด้านล่างก็เพราะว่า directive คาดหวังว่าจะได้ function ที่จะถูกรันด้วย การ execution ไม่ใช่การ new ดังนั้นก็ต้อง new ให้เสียก่อน

อ่านต่อ »

Adding babel-polyfill with Browserify

ปกติเราใช้ babel เมื่อเราต้องการใช้ฟีเจอร์บางอย่างของ es6 (อ่านเพิ่ม) ซึ่งวิธีการทำงานของ babel ก็คล้าย ๆ กับ coffeescript ซึ่งก็คือแปลงนั้น ๆ กลับมาเป็น javascript ES5 ให้สามารถรันได้ทาง web browser ทั่ว ๆ ไป (ไม่ใช่ทุก browser จะรองรับ es6 ในปริมาณที่เท่ากัน) ซึ่งในเว็บไซต์ documentation ของ babel เองก็จะระบุไว้ว่าแต่ละฟีเจอร์นั้นจะต้อง setup babel ไม่เหมือนกันบางฟีเจอร์อาจจะต้องการการติดตั้งมากกว่าอันอื่นเล็กน้อย บางอันอาจจะต้องอาศัยโปรแกรมอื่นช่วยเช่น คำสั่ง import ที่ต้องอาศัยเครื่องมือตัวกลางมาคอย require ไฟล์ต่าง ๆ ให้เพราะว่า browser babel ไม่ได้ใส่เครื่องมือพวกนี้มาให้

สถานการณ์เป็นแบบนี้ครับ ผมติดตั้ง babel (babelify) ร่วมกับ browserify ที่จริงก็มี watchify (แต่ว่าไม่สำคัญในที่นี้เท่าไหร่) และเพราะการตั้งค่าไม่ครบถ้วนของผมทำให้ babel แปลโค้ดให้ผมไม่ได้เป็นภาษา es5 ที่รองรับทุก ๆ browser และส่งผมให้โค้ดของผมบึ้มในหลาย ๆ browser บึ้มแบบไม่รู้ว่าผิดตรงไหนเลยล่ะ ฮ่ะ ๆ

ผมลองใช้ for .. of loop ซึ่งมาพร้อมกับ es6 มีลักษณะแบบนี้

let schools = [... list of schools ...];  
for (let school of schools) {  
    console.log('school name:', school);
}

แล้ว error ที่ผมเห็นถ้าเปิดด้วย browser เช่น safari จะพบแบบนี้

Error: Can't find variable: Symbol  

ซึ่งตอนแรกผมก็ไม่เข้าใจว่าหมายถึงอะไรแน่ แต่สุดท้ายมันก็คือบอกว่ามันไม่รู้จัก Symbol ซึ่งเป็น class ที่มีใน es6 (for .. of จะถูกแปลงด้วย babel ไปใช้ Symbol.iterate แทน ซึ่งก็ไม่มีใน es5 อยู่ดี) ซึ่งการจะเสริมความสามารถ Symbol ให้ browser ทั่ว ๆ ไปก็คือต้องมีการใช้ polyfill ที่จริ

อ่านต่อ »

Using ES6 Promises in Angular Apps

ที่จริงคอนเซ็ปต์เรื่อง promise ไม่ใช่เรื่องใหม่ (อ่านเพิ่มเติมเรื่อง promise) และ Angular เองก็ได้รับเอาคอนเซ็ปต์นี้เข้ามาเป็นส่วนหนึ่งของ framework ซึ่งเราจะเรียกใช้งานได้จากการ inject $q (วิธีการใช้ $q) โดยผมพบด้วยตัวผมเองว่าการใช้ promise ทำให้ผมสามารถทำงานกับ Angular ได้อย่างมีความสุขขึ้นอย่างมาก โค้ดที่เขียนออกมานั้นอ่านและเข้าใจได้ง่ายกว่าเดิมมาก จากเดิมต้องเขียนด้วยการใช้ callback ซึ่งก็เข้าใจยากด้วยตัวเองอยู่แล้ว ยังยากต่อการอ่านให้รู้เรื่องด้วยเนื่องจากการเขียน callback ไม่ได้ถูกออกแบบมาให้อ่านด้วยมนุษย์ได้ง่าย ๆ

แต่สำหรับคนที่ไม่เคยได้ยินคำว่า promise มาก่อน ผมจะอธิบยไว้ตรงนี้เสียหน่อยว่ามันก็เหมือนกับ "สัญญา" ดังความหมายของมันนั่นแหละ มันคือสิ่งที่จะมี "ค่า" ในอนาคต แม้ว่ายังไม่ใช่ตอนนี้ ลักษณะการใช้งานทั่ว ๆ ไปที่เราจะเห็นได้ก็เช่น

let a = some promise;  
a.then(  
    function afterA(promisedValue) {
        ... do something here ...
    });

จากตัวอย่างนี้ ... ฟังก์ชัน afterA จะถูกเรียกเมื่อ "สัญญา" ได้กลายเป็นความจริงแล้ว (ไม่เกิดขั้นทันที) และจะถูกส่งค่าที่ได้สัญญาเอาไว้ใส่ไปใน promisedValue ด้วย ถ้าดูตรงนี้แค่นี้ก็อาจจะคิดว่ามันก็ไม่ได้ต่างกับ callback หนิ แค่มีตัวอักษร .then ซึ่งก็จริง เพราะว่า .then เองก็ยังต้องใช้ callback เลย แต่เชื่อหรือไม่ว่าแค่มี .then ก็ทำให้อ่านโค้ดได้ง่ายขึ้นเป็นโขแล้ว

อีกหนึ่งตัวอย่างที่แสดงว่า promise มันน่าจะดีกว่า callback แน่ ๆ ก็เช่น

let a = some promise;  
a  
.then(
    function afterA(promisedValue) {
        ... do something here ...
        let b = some promise;
        return b;
    })
.then(
อ่านต่อ »