Aurelia Todoアプリの作成

# はじめに
## 今回やること
今回は、Aureliaフレームワークを使って、Todoアプリの作成を行います。
本記事で扱うTodoアプリのチュートリアルは、Aureliaの公式サイトで提供されているものと同じです。
英語で問題ない方は、下記の公式サイトを見て作成した方が良いです。

[Quick Start | Aurelia](https://aurelia.io/docs/tutorials/creating-a-todo-app)

# aureliaのプロジェクト作成
## [aurelia-cli](https://www.npmjs.com/package/aurelia-cli)を利用
Aureliaのスタンダードなプロジェクトを作成する`aurelia-cli`を利用します。
`aurelia-cli` については、下記のページを参照ください。

[Basics | Aurelia](https://aurelia.io/docs/cli/basics#machine-setup)

## コマンド実行順
コマンドの実行順を簡単に記載します。

1. 下記コマンドで、npmを使用して、 `aurelia-cli` をインストールします。  
`npm install aurelia-cli -g`
2. 下記コマンドを実行すると、プロジェクトを作成できます。  
`au new`
3. 作成したプロジェクトに移動します。  
`cd <作成したプロジェクト名>`
4. 下記コマンドを実行します。  
`au run`
5. http://localhost:8080 にアクセスすると、`Hello Aurelia!` と表示されます。

## 事前準備
以降のチュートリアルをスムーズに行うために、
今回作成するTodoアプリには必要のない記述を修正・削除しましょう。
まず、下記ファイルは削除してください。
`src/resources/index.ts `

次に、下記ファイルを以下のように修正してください。

```diff:src/main.ts
import {Aurelia} from 'aurelia-framework'
-import environment from './environment';
import {PLATFORM} from 'aurelia-pal';

export function configure(aurelia: Aurelia) {
-  aurelia.use
-    .standardConfiguration()
-    .feature(PLATFORM.moduleName('resources/index'));
-
-  aurelia.use.developmentLogging(environment.debug ? 'debug' : 'warn');
-
-  if (environment.testing) {
-    aurelia.use.plugin(PLATFORM.moduleName('aurelia-testing'));
-  }
-
+  aurelia.use.basicConfiguration();
  aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app')));
}
```

# Chapter1 TodoクラスとAppクラスの作成

## Todoクラスの追加
まず、一つ一つのTodoを表すオブジェクトのための `Todoクラス` を作成します。
以下のファイルを作成してください。

```TypeScript:src/todo.ts
export class Todo {
  done = false;
  constructor(public description: string) { }
}
```

`done`は、そのTodoをやったかどうかを管理するためのプロパティです。
例) タスクA：「醤油を買って帰る」があります。
　醤油を買って帰ると、タスクAのdoneはtrueに変更できます。  
`description`は、タスクの説明です。
上記の例でいうと、「醤油を買って帰る」の部分です。

## Appクラスの修正
Appクラスから、以下のように1行削除して保存しましょう。

```diff:src/app.ts
export class App {
-  public message: string = 'Hello World!';
}
```
上記の行を消したため、
ブラウザ( http://localhost:8080 )をみると、`Hello World!`は表示されなくなっています。
`src/app.html`で表示していた`message`を消してしまったからですね。

それでは、AppクラスにTodoリストの中身と、Todoを操作するメソッドを追加しましょう。

```TypeScript:src/app.ts
import {Todo} from './todo';

export class App {
  heading = "Todos";
  todos: Todo[] = [];
  todoDescription = '';

  addTodo() {
    if (this.todoDescription) {
      this.todos.push(new Todo(this.todoDescription));
      this.todoDescription = '';
    }
  }

  removeTodo(todo) {
    let index = this.todos.indexOf(todo);
    if (index !== -1) {
      this.todos.splice(index, 1);
    }
  }
}
```

上から順番に説明していきましょう。

```TypeScript
import {Todo} from './todo';
```
このコードは、 `src/todo.ts` から、Todoクラスをインポートしています。

```TypeScript
heading = "Todos";
todos: Todo[] = [];
todoDescription = '';
```

`heading` は画面に表示するタイトルです。`"Todos"`の部分は好きな言葉にしてもらって問題ありません。
`todos` は、画面に表示するTodoリストです。Todoクラスのオブジェクトの配列になっています。
`todoDescription` は、画面で入力するそれぞれのTodoの説明です。(「醤油を買って帰る」のことです。)  

次に、各メソッドを紹介します。
`addTodo` メソッドは、`todos`の配列に新しいTodoを追加するための関数です。
`removeTodo` メソッドは、選択したTodoをTodoリストから削除するための関数です。
それぞれのメソッドについての細かい説明は、また後ほどします。  

ここまでで注目すべき点は、我々はまだTypeScriptのソースしか書いていないということです。

次のチャプターでは、作成したAppクラスの内容を画面に表示してみます。

# Chapter2 画面のレンダリング
それでは、画面のレンダリングをしましょう。
しかし、お気づきだとは思うのですが、画面のレンダリングはすでに行われています。
(最初に`au run`した時に、すでに画面には`Hello World!`が表示されていましたよね。)

それでは、どのようにして画面のレンダリングが行われているのかを確認しましょう。  

## Aureliaフレームワークの設定ファイル

```HTML:index.ejs
<body aurelia-app="main">
</body>
```

`index.ejs`の`body`タグは、上記のようになっています。
`aurelia-app`という属性に渡された`"main"`は、`src/main.ts`のことです。
(`aurelia-app="src/main"` と書いても、同じように`src/main.ts`を見ます。)

この属性は、Aureliaフレームワークのconfiguration(設定)が、
どのファイルに記載されているのかを示しています。

それでは、`src/main.ts`を見てみましょう。

```TypeScript:src/main.ts
import {Aurelia} from 'aurelia-framework'
import {PLATFORM} from 'aurelia-pal';

export function configure(aurelia: Aurelia) {
  aurelia.use.basicConfiguration();
  aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app')));
}
```

これが、Aureliaフレームワークにおけるメインファイルです。
メインファイルで`configure`メソッドをエクスポートすることで、Aureliaの設定ができます。
Aureliaフレームワークは、Aureliaオブジェクトのインスタンスを作成します。
そして、そのAureliaオブジェクトに対して使用できる、様々な設定やプラグイン、拡張などがあります。

上記のファイルの内容を簡単に説明します。

```TypeScript
aurelia.use.basicConfiguration();
```

上記の部分は、基本的なconfigurationを設定するメソッドを実行しています。

```TypeScript
aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app')));
```

`aurelia.start()`は、必要なプラグインやリソースを読み込んで、Aureliaのインスタンスを開始します。
`aurelia.setRoot`ですが、ルートになるコンポーネントを指定しています。
ここでは、`./app.ts`を指定しています。

## Todoリストの表示

### Todo追加フォームの表示

さて、それでは、app.htmlを以下のように変更しましょう。

```diff:src/app.html
<template>
-  <h1>${message}</h1>
+  <h1>${heading}</h1>
</template>
```

これを保存すると、画面には`Todos`と表示されているかと思います。
この`${heading}`は、先ほど`src/app.ts`で定義しましたね。
(ちなみに`template`タグについてですが、すべてのviewは`template`タグで囲む必要があります。)  

次に、Todoを画面から追加するためのフォームを作ってみましょう。

```diff:src/app.html
<template>
  <h1>${heading}</h1>
+
+  <form submit.trigger="addTodo()">
+    <input type="text" value.bind="todoDescription">
+    <button type="submit">Add Todo</button>
+  </form>
</template>
```

これで画面にフォームが表示されたかと思います。

#### Todoの入力

```HTML
<input type="text" value.bind="todoDescription">
```

`value`属性に`.bind`を追加することで、
`app.html`に対応するビューモデル(`app.ts`のこと)に`"todoDescription"`をバインドすることができます。
Aureliaでは、`.bind`を追加するだけで、どのようなHTMLの属性もバインドすることができます。
(この`.bind`は、双方向のバインディングです。バインド用のコマンドは、他にもあります。
例： `.one-way` `.two-way` `.one-time`)

#### 「Add Todo」ボタン

```TypeScript
<form submit.trigger="addTodo()">
```

ここでは`submit`というDOMイベントに`.trigger`を追加しています。
`submit`イベントが起きると`addTodo`メソッドが実行されるようになっています。
Aureliaでは、あらゆるDOMイベントをキャッチして、`.trigger`を追加することができます。
ここで、`addTodo`メソッドの中身を振り返ってみましょう。

```TypeScript:src/app.ts
addTodo() {
  if (this.todoDescription) {
    this.todos.push(new Todo(this.todoDescription));
    this.todoDescription = '';
  }
}
```

`addTodo`メソッドでは、todoDescriptionが入力されている場合、
入力値を`description`に持つ新しいTodoオブジェクトを作成します。
作成したTodoオブジェクトを`todos`(Todoの配列)に追加します。
そして、入力値(`value`)をクリアします。

こうして、入力した値からTodoリストがどんどん作成されます。

### Todoリストの表示
今のままではTodoが本当に追加されているのかが分かりません。
次は、作成したTodoリストを画面に表示しましょう。

```diff:src/app.html
<template>
  <h1>${heading}</h1>

  <form submit.trigger="addTodo()">
    <input type="text" value.bind="todoDescription">
    <button type="submit">Add Todo</button>
  </form>
+
+  <ul>
+    <li repeat.for="todo of todos">
+      <input type="checkbox" checked.bind="todo.done">
+      <span>
+        ${todo.description}
+      </span>
+      <button click.trigger="removeTodo(todo)">Remove</button>
+    </li>
+  </ul>
</template>
```

ブラウザを確認してみましょう。
入力して「Add Todo」ボタンを押すと、フォームの下にどんどんリストが追加されるはずです。

#### Todoリストのループ

```HTML
<li repeat.for="todo of todos">
```

このシンタックスは、`for...of`のループから出来ています。
`todos`の配列から要素の数だけ`li`タグとその中身を生成しているのです。
`todo of todos`の`todo`は、配列から取り出したTodoオブジェクトにつけた名前です。

以下の部分を見てください。

```HTML
<input type="checkbox" checked.bind="todo.done">
<span>
  ${todo.description}
</span>
```

1つのTodoオブジェクトの`done`と`description`の値がそれぞれ参照されています。
チェックボックスに関しては、`checked`に対して`done`の値がバインドされています。
そのため、チェックボックスにチェックを入れると、`done`の値が`true`に切り替わります。

#### 「Remove」ボタン

```HTML
<button click.trigger="removeTodo(todo)">Remove</button>
```

「Remove」ボタンをクリックすると、`removeTodo`メソッドが実行されます。
ここで、`removeTodo`メソッドの中身を振り返ってみましょう。

```TypeScript:src/app.ts
removeTodo(todo: Todo) {
  let index = this.todos.indexOf(todo);
  if (index !== -1) {
    this.todos.splice(index, 1);
  }
}
```

`removeTodo`メソッドでは、
todosの配列の中から、削除する対象のTodoオブジェクトのindexを探し、
todosからそのindexの要素を取り除きます。

そのため、 「Remove」ボタンを押すと、そのTodoは削除されます。

#### doneチェックボックス

doneチェックボックスの変化がわかりづらいので、
doneの状態によってCSSを変化させてみましょう。

`app.html`を以下のように変更します。

```diff:src/app.html
<ul>
  <li repeat.for="todo of todos">
    <input type="checkbox" checked.bind="todo.done">
-    <span>
+    <span css="text-decoration: ${todo.done ? 'line-through' : 'none'}">
      ${todo.description}
    </span>
    <button click.trigger="removeTodo(todo)">Remove</button>
  </li>
</ul>
```

これで、`done`が`true`であれば取り消し線が表示されるようになります。
`done`の値は、チェックボックスを変更することで切り替えられます。  

以上で、Todoアプリの完成です!

# おわりに Aureliaについて
下記は、公式サイトからAureliaのアピールポイントを抜粋した文章です。

Aureliaは、シンプルな方法によってデザインされています。
そのため、たくさんの設定やテンプレートを書く必要がありません。
また、カスタマイズしたり、プラグインを使ったりできるようにデザインされています。
フレームワークを意識せず、プレーンなTypeScriptのみを書くことができます。

# おまけ 「Clear All」ボタン
ここまでの内容を参考に、押すとTodoリストがすべてクリアされるボタンを作ってみましょう。
`app.ts`に下記メソッドを追加して、 `app.html` 内に、ボタンを追加してみましょう。

```diff:src/app.ts
removeTodo(todo: Todo) {
  let index = this.todos.indexOf(todo);
  if (index !== -1) {
    this.todos.splice(index, 1);
  }
}

+  // Todoリストがすべてクリアされるような機能を提供するメソッドの中身を書いてみましょう
+  clearAllTodos() {
+  }
}

```