Aurelia hack 02 ～Aureliaの構造～

[前回](http://qiita.com/sukobuto/items/cf2c54c64d8f617fd11c) に引き続きひな形について、今回は構造を見ていきましょう。

# 何を書けばどう動くのか

まずソースコードの全体的な構造です。
実際にソースコードをのぞきながら読み進めてみてください。

![aurelia-hack02-001.png](https://qiita-image-store.s3.amazonaws.com/0/25145/0116f1eb-dc89-49b5-d084-b53f48b12ae5.png)

## index.html -> main.js

index.html がブラウザに読み込まれると aurelia-bootstrapper によって aurelia-app 属性に従ってエントリポイントが呼び出されます。今回は `aurelia-app="main"` の記述に従って main.js が読み込まれます。

## main.js -> app

続いて画面の外枠と、内側に表示する画面のルーティングを行う app モジュールが読み込まれます。

```js:src/main.js
import 'bootstrap';

export function configure(aurelia) {
  aurelia.use
    .standardConfiguration()
    .developmentLogging();

  //Uncomment the line below to enable animation.
  //aurelia.use.plugin('aurelia-animator-css');

  //Anyone wanting to use HTMLImports to load views, will need to install the following plugin.
  //aurelia.use.plugin('aurelia-html-import-template-loader')

  aurelia.start().then(a => a.setRoot());
}
```

この `a.setRoot()` が最初の画面を決定しますが、引数がなければ自動的に app が選択されるようになっています。
例えば welcome から起動したい場合は `a => a.setRoot('welcome')` と書きます。

> 各モジュールの記述はコンパイル済みファイルが展開される `dist` ディレクトリからの相対パスになっています。
> 例えば `src/routes/sample.js (&.html)` を指定する場合は `routes/sample` と書いてください。

## app -> welcome

### ルーティング

app.js を見てみると、App クラスに `configureRouter()` というメソッドが定義されています。
ViewModel に `configureRouter()` が定義されていると、Aurelia は「この画面ではルーティングをやりたいんだな？」と判断してくれます。

ひな形では最初のルートに対して welcome を設定していますので、app.html の `<router-view></router-view>` の部分に welcome が表示されるという流れになります。

### View と ViewModel

Aurelia は [MVVM](https://ja.wikipedia.org/wiki/Model_View_ViewModel) (Model View ViewModel) という発想に基づいています。

app や welcome はそれぞれ View (.htmlファイル) と ViewModel (.js) ファイルに分かれています。

- View welcome.html (表示内容のテンプレート)

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

- ViewModel welcome.js (表示するデータやユーザの操作に対応するアクション)

```js:src/welcome.js
export class Welcome {
  heading = 'Welcome to the Aurelia Navigation App!';
  firstName = 'John';
  lastName = 'Doe';
  previousValue = this.fullName;
  // ～省略～
}
```

これらは基本的に同じ名前を付けることでペアとなります。ファイル名はそれぞれハイフン区切りのスネークケース `snake-case.js, snake-case.html` で、ViewModel のクラス名はパスカルケース `export class PascalCase` にします。
もちろん、別の名前を付けたり別の場所にある View を設定したりすることも可能です。

> Aurelia は Angular2 によく似ていますが、この「特に注文がなければ名前だけで役割を決定する」という規約志向なところが Aurelia の特徴となっています。

これは各画面だけでなく、その中で使う部品についても同様です。

# 画面に表示する部品を作ってみよう

ひな形ではCSSフレームワークとして [Twitter Bootstrap](http://getbootstrap.com/) が使われています。Twitter Bootstrap は CMS なんかを作るときにとくに助かるのですが、構造的な意味の薄い（見た目のためだけの）コードによって HTML が冗長になりがちです。

## [dropdown](http://getbootstrap.com/components/#dropdowns) custom element を作る

[こちら](http://getbootstrap.com/components/#dropdowns-example) の例を引用します。そのまま記述するとしたら以下のようになります。

```html:dropdownメニュー部品だけでこのボリューム
<div class="dropdown">
  <button class="btn btn-default dropdown-toggle" type="button"
    data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
    サンプル
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu">
      <li><a href="#">Welcome</a></li>
      <li><a href="#users">Github Users</a></li>
      <li><a href="#child-router">Child Router</a></li>
  </ul>
</div>
```

これを部品 (custom element) として定義することで、以下のように使うことができます。

```html:src/welcome.html
<template>
  <!--作った部品を読み込む-->
  <require from="dropdown"></require>
  <section class="au-animate">
    <h2>${heading}</h2>

    <!--作った部品を使う-->
    <dropdown label="サンプル">
      <li><a href="#">Welcome</a></li>
      <li><a href="#users">Github Users</a></li>
      <li><a href="#child-router">Child Router</a></li>
    </dropdown>
  </section>
</template>
```

![aurelia-hack02-002.png](https://qiita-image-store.s3.amazonaws.com/0/25145/872d6b0b-1013-03fe-6749-8e026119caa7.png)

ボタン部分を表現するためのコードがごそっとなくなって、ドロップダウンメニューの内容に集中できるようになります。

- View 新規作成: src/dropdown.html

```html:src/dropdown.html
<template>
	<div class="dropdown">
		<button class="btn btn-default dropdown-toggle" type="button"
		        data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
			${label}
			<span class="caret"></span>
		</button>
		<ul class="dropdown-menu">
			<content></content>
		</ul>
	</div>
</template>
```

- ViewModel 新規作成: src/dropdown.js

```js:src/dropdown.js
import {bindable} from 'aurelia-framework'

export class Dropdown {

	@bindable label;

}
```

ドロップダウンボタンのタイトル部分に表示するラベルと、それからドロップダウンメニューの内容を外部から設定できるようになっています。
[knockout.js Components](http://kojs.sukobuto.com/docs/component-registration) などでは作った custom-element はフレームワークに登録する処理を書く必要がありますが、それらは Aurelia では必要ありません。

### @bindable

ViewModel のプロパティに @bindable を付けることで、プロパティを外部から設定・バインドすることができるようになります。
つまり、`<dropdown label="サンプル">` のように書けばOKです。
動的に変更するなら `<dropdown label="${sampleLabel}">` でもいいですし、`<dropdown label.bind="sampleLabel">` でもOKです。

### `<content></content>`

custom element の中身を使うには、中身を配置したい場所に `<content></content>` を記述します。下記の「ここから～ここまで」の部分が上記 dropdown.html の `<content></content>` の部分に配置されます。

```html
<dropdown label="サンプル">
  <!--ここから-->
  <li><a href="#">Welcome</a></li>
  <li><a href="#users">Github Users</a></li>
  <li><a href="#child-router">Child Router</a></li>
  <!--ここまで-->
</dropdown>
```

### ViewModel を省略することもできるよ

今回の ViewModel は bindable なプロパティが一つあるだけです。ロジックがまったくいらないので、ViewModel を省略することもできます。

まずは dropdown.html の template エレメントに `bindable="label"` を追加します。ViewModel の `@bindable label` と同義です。

```html:src/dropdown.html
<template bindable="label">
複数ある場合は bindable="label,foo,bar" のようにカンマ区切りで記述します。
～省略～
</template>
```

src/dropdown.js は消してしまって大丈夫です。その代わり custom element としてちゃんと見つけられるように、読み込み部分を `from="dropdown"` から `from="dropdown.html"` に変更します。

```html:src/welcome.html
<template>
  <!--作った部品を読み込む-->
  <require from="dropdown.html"></require>
  ～省略～
</template>
```

## 部品をどこでも使えるようにするには

追加した部品を使うためには `<require from="dropdown"></require>` のようにモジュール参照を書く必要がありますが、以下の方法で `<require>` を書かなくても、いつでもどこでも custom element を使えるようにすることができます。

まず、いろんな場所で使いたい部品は１か所にまとめましょう。さらにまとめたフォルダに index.js を新規作成します。

```text
src/
 └─ resources/   ...共通部品置き場
     ├─ dropdown.html
     ├─ ...
     └─ index.js
```

新規作成した index.js は以下のように記述します。

```js:src/resources/index.js
export function configure(aurelia) {
	aurelia.globalResources('dropdown.html');    // ViewModel 省略したなら .html をつけよう
    // 部品が増えたら同じようにここに追加していく
}
```

最後に main.js に1行追加します。

```js:src/main.js
import 'bootstrap';

export function configure(aurelia) {
  aurelia.use
    .standardConfiguration()
    .developmentLogging()
    .feature('resources');    // feature として resources/index.js を登録する

  aurelia.start().then(a => a.setRoot());
}
```

これで `<require from="dropdown.html"></require>` は不要になり、管理が楽になりました。
今後のサンプルも resources ディレクトリを使っていきます。