ความเดิมตอนที่แล้วที่ผมแนะนำวิธีการเขียน 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'
        // I personally think 'interfaces' is a better word
        interfaces: {
          // public is a special interface that let outsider to peek inside into props and methods freely!
          // like...
          // <test name="abc"></test>
          // $scope.abc.x => 0
          public: '=name',
        },

        // all the properties are here!
        props: {
          x: 0,
          y: 0,
          // element and attrs (from link) are automatically included here as well
          // be advised: link is invoked after the controller, then element and attrs
          // will not be available at the starter() and watcher() time
        },

        // all the $watch code should all be put here!
        // you shall write the watch destroyer here as well
        watcher($scope) {
          $scope.$watch('my.x', (x) => {
            console.log('x has changed! to:', x);
          });
        },

        // this block of code will be invoked first!
        // starter -> watcher; link is invoked normally according to angular's directive
        // if you like to use the scope, it is provided.
        starter($scope) {
          this.moveDiagonal();
        },

        // just the conventional link
        link($scope, element, attrs) {
          element.fadeIn(200);
        },

        // list all the methods
        methods: {

          moveDiagonal() {
            this.x += 10;
            this.y += 10;
          }

        }

      });

    });