AngularJSのcomponentをTypeScriptで実装してみる

#はじめに
　AngularJSの1.5.xより component が実装され、巷の噂ではAngularJSはTypeScriptを相性が良いとのことで、実装方法を研究したメモを残す。

#開発環境
- VisualStudio 2015 
- プロジェクトのプロパティの「TypeScriptビルド」の「出力」項目、「Javascript 出力をファイルに統合する」にチェックを入れ、値を「application.js」にする
- NuGetで、以下のパッケージを読み込む
 1. es6-promise.TypeScript.DefinitelyTyped (v0.1.3)
 1. angularjs.TypeScript.DefinitelyTyped (v5.4.2)
 1. jquery.TypeScript.DefinitelyTyped (v3.0.4)

#Viewの実装
単純にcomponentで作成されたタグを表示する

```html:index.html
<!DOCTYPE html>
<html lang="ja" ng-app="app">
<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>

    <script src="application.js"></script>
</head>
<body ng-controller="MainController as main">
    <h1 ng-bind="main.title"></h1>
    <element-test param-title="main.subTitle" call-back-event="main.callback1(value)"></element-test>
</body>
</html>
```
##element-testの仕様
1. param-title : element-testの中で表示するタイトル
1. call-back-event : コントローラ側のイベントを呼び出す

#モジュール定義
angularのappを定義します

```ts:app.ts
/// <reference path="_include.ts" />
module app {
    var module = angular.module('app', []);
    module.controller('MainController', MainController);
    module.component('elementTest', ElementComponentFactory());
}
```

```ts:_include.ts
/// <reference path="scripts/typings/jquery/jquery.d.ts" />
/// <reference path="scripts/typings/angularjs/angular.d.ts" />
/// <reference path="scripts/typings/es6-promise/es6-promise.d.ts" />

/// <reference path="main.controller.ts" />
/// <reference path="element.component.ts" />
```

#メインコントローラ

```ts:main.controller.ts
/// <reference path="_include.ts" />
module app {
    /**
     * メイン　コントローラ
     */
    export class MainController {
        // タイトル
        title: string;
        // サブタイトル
        subTitle: string;
        // コールバック
        callback1: Function;
        /**
         * コンストラクタ
         */
        constructor() {
            this.title = "AngularJS(1.5)でcomponent実装";

            this.subTitle = "コントローラから設定したパラメタ";

            // コールバックの設定
            this.callback1 = (value : any) => {
                // コールバックが呼び出される。valueは「1001」
            }
        }
    }
}
```

#コンポーネント

```element.component.ts
/// <reference path="_include.ts" />
module app {
    /**
     * Element コンポーネント作成
     */
    export function ElementComponentFactory(): ng.IComponentOptions {
        return {
            bindings:
            {
                //コントローラパラメタ
                paramTitle: '=',
                //コールバック
                callBackEvent: '&'
            },

            template: [
                    "<div>",
                    "<p ng-bind='_my.displayItem'></p>",
                    "<button ng-click='_my.clickButton(123)'>ボタン</button>",
                    "</div>"
                ].join(""),
            controllerAs: '_my',
            controller: ElementComponent
        };
    }
    /**
     * コントローラ
     * @returns
     */
    class ElementComponent {
        public static $inject = ['$http'];  // httpサービスの注入

        /**
         * 表示項目
         */
        displayItem: string;
        /**
         * コールバック
         */
        callBackEvent: Function;

        // タイトル
        private _paramTitle: string;  
        get paramTitle (): string {
            return this._paramTitle;
        }
        /**
         * コントローラとのI/O
         * @param {string} value
         */
        set paramTitle(value: string) {
            if (!angular.isDefined(value)) {
                return;
            }
            this._paramTitle = value;
            // パラメタが設定された処理
            this.displayItem = value;
        } 
        /**
         * コンストラクタ
         * @param {ng.IHttpService} public $http
         */
        constructor(public $http: ng.IHttpService) {
            // this.$http で httpサービスが使用可能
        }
        /**
         * ボタンが押された
         * @param {number} value 値
         */
        clickButton(value: number): void {
            // valueは「123」が設定されている
            this.callBackEvent({ 'value': 1001 });
            this.paramTitle = "ボタン押下";
        }
    }
}
```
**ポイント**

- component の controller は class で実装する
- 従来 $scope 経由で操作していたプロパティ類は、controller class のプロパティで宣言する
- コントローラからの値受信は、 setter の中で処理を行う
- コントローラへの callback は Function型 で定義したプロパティを呼び出す

#最後に
自分は、Javascriptと言う言語が気持ち悪くて、コード実装時はモチベーションが事が下がったままでしたが、TypeScriptの場合は綺麗なコードになり、書いてて楽しくなりました。