TypeScriptで書くAurelia 最小構成

期待の新MV*(MVW)フレームワーク**Aurelia**を、**TypeScript**で書いてみたいと思います。
折角なので、Aurelia公式のGet StartedやNavigation Skeletonよりもシンプルなスケルトンをフルスクラッチで作成します。

この解説の成果物は[こちら](https://github.com/ryohpops/Aurelia-TypeScript-Simple-Skeleton)のリポジトリに用意してあります。
もっともフルスクラッチが目標なので、この解説を通して同じものが簡単に作れることを確認していただきたいです。

##追記 2015/07/23
Visual Studio 2015の公開と、Aurelia型定義ファイルのテスト配布開始に伴い記事を更新しました。
なお色々試した結果として、以下の理由からASP.net 5 プロジェクトの採用は推奨しません。

* Aureliaは基本的にjspmを利用するが、新しい依存性管理システムはnpm/bowerにしか対応していないうえ、コマンドプロンプトより手間がかかる。
* Aureliaをbower経由で入手する場合、SystemJS用の依存性設定ファイルは自作となり、更に手間がかかる。
* tsconfigなど一応TypeScriptにも対応しているが、当然TypeScriptプロジェクトに比べて利便性は落ちる。
* そもそも今までコマンドで作っていた各種jsonファイルを手で書けというのは明らかに時代に逆行して（略

#環境
* Visual Studio Community 2015
* TypeScript 1.5（VS2015に同梱）
* Node.js v0.12.5
* Internet Explorer 11
* Web Essentials 2015（任意：HTMLに対してAureliaのインテリセンスが多少効きます）

ライブラリ管理はjspmを用い、Visual Studioの外部（コマンドプロンプトなど）で行います。

#プロジェクト
Visual StudioにてTypeScript HTML アプリケーション プロジェクトを作成します。
`プロジェクト > プロパティ > TypeScriptビルド`からECMAScriptバージョンを6に設定しておきます。
暗黙的なanyの許可からチェックを外しておくことをオススメします。

#パッケージマネージャー
gitが必要となりますので、[git公式](http://git-scm.com/)からインストールしておいてください。

npmを使って**jspm**をグローバルインストールします。

```text:cmd
npm install -g jspm
```

jspmはnpmのようなパッケージマネージャの1つで、npmデータベースに加えGitHubからもダウンロードを行うことができます。

続いてjspmを用いてプロジェクトの設定ファイルを作成します。

```text:cmd
cd <プロジェクトのディレクトリ>
jspm init
<略>
Which ES6 transpiler would you like to use, Traceur or Babel? [traceur]: babel
```

`npm init`と同じような画面に従って項目を埋めていきます。
基本的に全てデフォルトでOKですが、ES6互換性ライブラリのみAureliaが推奨する**Babel**に切り替えておきます。
必要モジュールを指定するpackage.json(npmと兼用)、モジュールが格納されるjspm_packagesディレクトリ、モジュールのマッピングが記述されるconfig.jsが生成されます。

#ライブラリ
jspmを用いてAureliaのモジュールを導入します。

```text:cmd
jspm install aurelia-bootstrapper
```

最小構成において指定の必要があるモジュールは`aurelia-bootstrapper`のみです。
Aureliaには数多くのモジュールがありますが、`aurelia-bootstrapper`の依存関係がjspmを通じて必要なものを引っ張ってくれます。

##※jspmのマッピングについて
jspmのマッピングサーバーにAureliaが登録されたようで、githubなどの指定も無く通名だけでインストール出来るようになりました！
~~jspm経由のインストールを推奨しているAureliaですが、実は命名規則が違っており、悪いことに似通っています。
Aureliaはモジュールに`aurelia-***`、jspmは識別子に`作者名/リポジトリ名`を用いています。
Aureliaの作者名は`aurelia`でリポジトリ名はモジュール名と同じなので、一見ハイフンとスラッシュの違いだけに見えてしまいますが全くの別物であり、当然意を汲む動作はしてくれません。
そのためインストール時に`=`繋ぎで関連付けを行っています。
npmのように`github:...`だけでインストールしてしまうと手動でconfig.jsを調整することになるので注意してください。~~

##※型定義ファイルについて
同梱されている型定義ファイルには問題があり、`core-js`についてコンパイルエラーが出ます。
本来は`core-js`の型定義ファイルを追加導入すれば解決するのですが、`core-js`の型定義ファイルにも問題があるようです。
応急措置として、Aureliaに含まれる.d.tsファイルから、一括置換か何かで`core-js`のimport宣言の行を消去してください。

#index.html
スタートアップページとなるindex.htmlです。

```html:/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="app.css" type="text/css" />
</head>
<body aurelia-app>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
        System.import("aurelia-bootstrapper");
    </script>
</body>
</html>
```

headタグ内でのapp.jsのロードを削除します。読み込みはAureliaが推奨するモジュールローダーの**SystemJS**に任せるためです。

```html
<body aurelia-app>
```

bodyタグに`aurelia-app`属性を付与し、Aureliaのルートを指定します。
値を指定しない場合、`app`モジュールのロードを試みます。
モジュールとは同名のhtmlファイルと（tsファイルがコンパイルされた）jsファイルのペアです。

```html
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
    System.import("aurelia-bootstrapper");
</script>
```

そしてbodyタグ内でSystemJSと、jspmが生成したconfig.jsをロードし、SystemJSを利用して`aurelia-bootstrapper`をロードします。

忘れずにindex.htmlをスタートページに設定しておきます。

#appモジュール
ルーティングの土台となる`app`モジュールを作ります。
具体的にはルーティングテーブルの設定と対応したコンテンツが入るコンテナを用意します。
`app`と名の付くモジュールは`aurelia-app`のデフォルト値として指定されているため、最初に読み込まれます。

```html:/app.html
<template>
    <router-view></router-view>
</template>
```

単純明快、`router-view`というコンテンツコンテナがあるだけです。
このコンテナにはrouterモジュールによってコンテンツが自動的に読み込みます。
モジュールのhtmlは全て`<template>`タグ内に記述します。

```ts:/app.ts
import {Router, RouterConfiguration} from "aurelia-router";

export class App {
    configureRouter(config: RouterConfiguration, router: Router) {
        config.title = "Aurelia";
        config.map([
            { route: ["", "welcome"], name: "welcome", moduleId: "app/welcome", nav: true, title: "Welcome" }
        ]);
    }
}
```

ルーティングテーブルの作成はここで行います。

```ts
import {Router, RouterConfiguration} from "aurelia-router";
```

ES6のインポート宣言はこのように書きます。`import {} from "***"`だけ先に書いてしまうと、インポート対象についてインテリセンスを効かせることができます。
今回Aureliaに同梱が開始されたファイルにはinterfaceが含まれていないため、それほどインテリセンスを活かすことは出来ません。

```ts
{ route: ["", "welcome"], name: "welcome", moduleId: "app/welcome", nav: true, title: "Welcome" }
```

ルートなし（初期状態）または`welcome`というルートに遷移した際に`app/welcome`モジュールをロードし、タイトルにWelcomeと表示するよう指定しています。
今回のようなシングルページアプリケーションの場合、上記ルーティングはクライアントサイドで行われるため、URLは常に`.../index.html`となりルートはindex.html#welcomeのように示されます。
さらにこのルートのルート名を`welcome`とし、ルーターのルート一覧に遷移可能として登録されるようにしています。

index.html、app.html、app.tsの3ファイルはプロジェクトルートに作成しましょう。

#welcomeモジュール
`app`モジュール上のコンテナに`router`モジュール経由で読み込んでもらう`app/welcome`モジュールを作ります。
先の3ファイル以外はappディレクトリに纏めることにします。
welcomeモジュールについてはnavigation skeletonとほぼ同じものです。

```html:app/welcome.html
<template>
    <section class="au-animate">
        <h2>${heading}</h2>

        <form role="form" submit.delegate="welcome()">
            <div class="form-group">
                <label for="fn">First Name</label>
                <input type="text" value.bind="firstName" class="form-control" id="fn" placeholder="first name">
            </div>
            <div class="form-group">
                <label for="ln">Last Name</label>
                <input type="text" value.bind="lastName" class="form-control" id="ln" placeholder="last name">
            </div>
            <div class="form-group">
                <label>Full Name</label>
                <p class="help-block">${fullName}</p>
            </div>
            <button type="submit" class="btn btn-default">Submit</button>
        </form>
    </section>
</template>
```

`${}`の片方向バインディング、`<イベント名>.delegate`のイベントハンドラ、`<属性名>.bind`の双方向バインディングが確認できます。
非常にスマートで、Angular1の進化系としてはAngular2よりもAureliaの方がしっくり来ます。

```ts:app/welcome.ts
export class Welcome {
    heading: string = "Welcome to the Aurelia Simple Skeleton!";
    firstName: string = "John";
    lastName: string = "Doe";

    get fullName(): string {
        return `${this.firstName} ${this.lastName}`;
    }

    welcome() {
        alert(`Welcome, ${this.fullName}!`);
    }
}
```

片方向/双方向バインディングされる変数やプロパティと、submit時に呼び出されるイベントハンドラです。
アノテーションを使った方が丁寧ですが、コンパイラにVisual Studioから設定できないフラグを渡す必要があるなど発展途上なので、今回は省略しています。

#実行
Visual Studioからデバッグを実行すると、ルーティングテーブルに指定した`<ドメイン名>/`のパターンにマッチするため`app/welcome`モジュールが読み込まれます。
双方向バインディングが正しく機能していることを確認してください。

#まとめ
以上、AureliaのTypeScriptスケルトンをフルスクラッチで作ることができました。
個人的にnavigation skeletonはスケルトンの割に機能が多すぎて、理解しづらいとの思いで挑戦しました。
型定義ファイルの生成について、現在`route-recognizer`でテストを行っているようです。かなり良い感じなので、他のモジュールについても期待しています。