TODOリストを作って理解するAngular2


# はじめに

Angular2 Dart版で作成した単純なTodo Listアプリの動作を確認し、そのコードの解説を通じてAngular2の基本を見ていきます。

この記事は、Angular2がbeta版になった契機に私が年末年始にAngular2の学習を開始した上で、[この記事](http://blog.scottlogic.com/2015/12/07/angular-2.html)を下敷きにして、せっかくなのでコードをDart版に変更し、一部内容を加筆したものです。もし誤りを発見されましたらコメント欄にでもお知らせください。

TypeScript版の解説を読みたい方、または簡潔な説明を読みたい方は元の記事を読んでください。

## Angular2 Dart
Angular2のDart版は、基本的にはTypeScriptで開発されているAngularをDartにトランスパイル([ts2dart](https://github.com/angular/ts2dart))したものであるようで、APIがほぼ同じです。(現時点では開発の進捗状況によって、部分的にDart版のAPIの変更が遅れたり、逆に一部の機能がDart版のみ実験的にサポートされたりすることもあるようです。)したがって、TypeScript版を学べばDart版でも違和感なく開発でき、その逆も成り立つことが期待できます。

また、Dartはセマンティクスがシンプルで学習コストが低いので、TypeScriptやJavaなどの言語に慣れていればDartを学ぶことは容易です。さらに、[SDK](https://api.dartlang.org/)が充実、安定しており、特に[Stream](https://api.dartlang.org/1.13.1/dart-async/Stream-class.html)、[async/await](https://www.dartlang.org/articles/await-async/)といった非同期APIや[Zone](https://www.dartlang.org/articles/zones/)の導入等、[非同期プログラミングのサポートが手厚い](https://www.youtube.com/watch?v=MUDOIAssBDs)言語です。

既存のプロジェクトをAngular2にアップグレードする場合は現時点ではES6、ES5やTypeScriptを採用するのが妥当ですが、新規プロジェクトの場合はDartの採用を検討してみるのも良いかもしれません。

なお、Dartで書いたサンプルコードはGithubに置いてあります。
https://github.com/ntaoo/angular2_dart_todo/releases/tag/20160104-qiita



# Todo Listの動作を確認する

## Dart SDKのインストール

まだDart SDKがインストールされていない場合は、以下に従いインストールします。https://www.dartlang.org/downloads/
Macの場合はHomebrewでインストールできます。

```shell
brew tap dart-lang/dart
brew install dart --with-content-shell --with-dartium
```

## 動作確認

[angular2_dart_todo](https://github.com/ntaoo/angular2_dart_todo)をcloneします。

```shell
git clone https://github.com/ntaoo/angular2_dart_todo.git
```

### pub get
[`pub get`]((https://www.dartlang.org/tools/pub/cmd/pub-get.html))してください。`pub get`は`npm install`のようなものです。

```shell
pub get
```

### Development server
次に、[`pub serve`](https://www.dartlang.org/tools/pub/cmd/pub-serve.html)してください。Dartプロジェクト用dev serverが立ち上がります(デフォルトでは`http://localhost:8080`)。

```shell
pub serve
```

### Dartium

Dartプロジェクトでは、開発用ブラウザとしてDartiumを使用します。Macの場合は以下のコマンドでDartiumが[checked mode](https://www.dartlang.org/tools/dart-vm/#enabling-checked-mode)で起動します。

```shell
DART_FLAGS='--checked' open /usr/local/opt/dart/Chromium.app
```

実際の開発ではWebStormなどのIDEからDartiumを起動させるなどすると良いでしょう。

[http://localhost:8080](http://localhost:8080)にアクセスすると、以下の画面になるはずです。

![Screen Shot 2016-01-02 at 12.15.20 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/3bb72837-394d-326a-b624-cafb0e3ae2a8.png "Screen Shot 2016-01-02 at 12.15.20 AM.png")


ちなみに、Chrome等のモダンブラウザでURLを開くと、dev serverは必要に応じて自動的にJSへのコンパイルを行います。そのため、表示まで多少(数十秒ほど)時間がかかります。

![Screen Shot 2016-01-01 at 11.22.52 PM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/3d3f1767-524f-e55f-1515-a78489135ce2.png "Screen Shot 2016-01-01 at 11.22.52 PM.png")

DartVMが載っているDartiumを利用するとDartコードをJSにコンパイルしなくて良いので、一瞬でbuildが終わります。コンパイル時間を意識することはありません。そして単にDartiumのタブをreloadするだけで再buildが行われ、非常に素早くコード変更のサイクルを回すことができます。Debuggerの利用もできるため、開発にはDartiumを利用しましょう。

### せっかくなのでBuildしてみる。

デプロイにあたってはDartをJavaScriptにコンパイルする等、buildを行う必要があります。せっかくなので確認しておきましょう。`pub build`でDartからJSへのコンパイルを含むbuildが完了します。

```shell
pub build
```

てっとり早くローカルサーバーを立ち上げて確認してみます。

```shell
cd build/web
python -m SimpleHTTPServer
```

モダンブラウザで正常に動作するはずです。

`build/web/main.dart.js`ファイルに、コンパイルされた、アプリケーション全体のJavaScriptコードが一つのファイルにまとめられています。この`main.dart.js`は460KBで、圧縮して127KBです。これはDartとAngular2のようなリッチなフレームワークにしてはかなりリーズナブルなサイズであると言えるのではないでしょうか。
[AngularチームはDart版のJSへのコンパイル後のコードサイズを小さく保つことに気を払っており、Production Readyの際には現時点より更に小さくなることが期待できます。](https://github.com/angular/angular/issues/5312)

---------------------------------------------

## Tips: 一からDartプロジェクトを作成するには

一からDartプロジェクトを始めるにあたっては、[Stagehand](https://github.com/google/stagehand)を使うのが便利です。これはYeomanのようなproject generatorです。

また、一から手で作成していくのも難しくありません。例えば、`mkdir angular2_todo && cd angular2_todo`して、pubspec.yamlファイルを作成して以下の内容を書きます。

```yaml:pubspec.yaml
name: todo_app
version: 0.1.0
dependencies:
  angular2: 2.0.0-beta.0
  browser: ^0.10.0
  dart_to_js_script_rewriter: ^0.1.0
transformers:
- angular2:
    entry_points: web/main.dart
- dart_to_js_script_rewriter
```

そして`pub get`しましょう。このコマンドは、[pubspec.yamlで宣言した依存packagesをプロジェクトで使用可能にするようセットアップします。](https://www.dartlang.org/tools/pub/cmd/pub-get.html)
あとは、プロジェクトディレクトリ直下に`web`と`lib`ディレクトリを作成すれば、最低限の[Dartアプリケーションプロジェクトの構成](https://www.dartlang.org/tools/pub/package-layout.html)が整います。そしてweb/index.htmlを用意し、[`pub serve`](https://www.dartlang.org/tools/pub/cmd/pub-serve.html)すればdev serverが立ち上がります。シンプルですね。

---------------------------------------------


# Bootstrap(ここからが本題)

ここからが本題です。コードを見ていきましょう。

## 注意

Angular2は2016年1月時点でベータ版のため、APIに破壊的変更が入る可能性があります。

----

lib/todo_list.dartです。今回のアプリケーション全体のroot componentです。

```dart:lib/todo_list.dart
library todo_app.todo_list;

import "package:angular2/angular2.dart";

@Component(
    selector: 'todo-list',
    templateUrl: './todo_list.html',
    styleUrls: const ['./todo_list.css']
）    
class TodoList {
}
```

`import "package:angular2/angular2.dart";`でAngular2の機能が一通りimportされます。
`TodoList`classに`@Component`Annotationが付与されています。AnnotationはDartの機能で、Angular2ではここで`TodoList`classはAngularのComponentであると宣言し、メタデータを付加します。

(TypeScript版では[Decorator](https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#decorators)が使用されています。少なくともAngular2においては、{}やconst宣言の有無の違いはあるにせよ、文法は概ね同じであるようです。)

ここでは`selector: `で、css selectorの文法で、Componentに対応するElementが`<todo-list>`であることを宣言しています。同様に、`templateUrl: `と`styleUrls: `でtemplateとstyleのurlを宣言しています。

(CSSの代わりにSASSやLESSなどを使用することは可能ですが、そのためにはbuild processを編集する必要があります。おそらく[transformer](https://www.dartlang.org/tools/pub/assets-and-transformers.html)に指定を加える等、ノウハウがこれから出てくるでしょう。また、Polymerのように[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables)を採用するのも良いかもしれません。)

`<todo-list>`はこのアプリ全体を含むComponentを表すElementです。どのようなboilerplateコードを書いてこのComponentをloadしているのでしょうか。
`web/index.html`と`web/main.dart`を見てみます。

```html:index.html
<head>
  ......
  <script defer src="main.dart" type="application/dart"></script>
  <script defer src="packages/browser/dart.js"></script>
</head>
<body>
  <todo-list>Loading...</todo-list>
</body>
```
(`packages/browser/dart.js`は、開発時にDartium以外のBrowserで動作確認する際に、DartファイルのpathをコンパイルしたJSファイルのpathに動的に置き換える小さなscriptです。開発の際に気にする必要はありません)

```dart:main.dart
library todo_app.web;

import 'package:angular2/bootstrap.dart';
import "package:todo_app/todo_list.dart";

main() => bootstrap(TodoList);
```

`angular2/bootstrap.dart`と`todo_list.dart`をimportし、TodoList componentをこのAngular2アプリケーションのroot componentとして指定してbootstrapしているだけのシンプルなものです。(後の説明で、ここにDependency Injectionを追加します。)

なお、`package:angular2/bootstrap.dart';`は@deprecated宣言されているので、近々APIが変わる見込みです

このAPIの詳細は`package:angular2/platform/browser.dart`のコメントや[angular.io](https://angular.io/docs/ts/latest/)のドキュメントを確認するなどすると良いでしょう。Angular1との違いやAngular2アプリケーションの設計思想、複数のAngular2アプリケーションの混在についても説明されています。

なお、TypeScript版とは異なり、Dartでは[SystemJS](https://github.com/systemjs/systemjs)のようなソリューションは必要なく、単に[DartのPackage管理システムを利用](https://www.dartlang.org/tools/pub/get-started.html#importing-libraries-from-packages)するだけです。

# Web Components

さて、Angular2のComponentは、もしかしたらWeb Componentなのでしょうか？
Dartium、またはChromeのInspectorを開いて確認してみます。

![Screen Shot 2016-01-02 at 6.57.51 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/914f2793-8af5-57de-bcdb-9ec2bcfa4589.png "Screen Shot 2016-01-02 at 6.57.51 AM.png")

ShadowDOMは見当たりません。代わりに、`<todo-list>`elementには-ghost-yfy-1、child elementsには_ngcontent-yfy-1という奇妙なattributeが付与されています。

Angular2はWeb Componentsのカプセル化をShadow DOMを使わずに擬似的に実現しています。対象elementとそのchildrenそれぞれに"代理ID(surrogate id)"情報を含むAttributeを付与し、style ruleをpre-processingしてからDOMにinsertしています。
![Screen Shot 2016-01-02 at 7.26.22 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/9a60f9c6-8183-8273-8f7f-b52876ff6ba1.png "Screen Shot 2016-01-02 at 7.26.22 AM.png")
`.todoapp`というセレクターは、実行時に`.todoapp[_ngcontent-yfy-1]`に変更されていることが確認できます。

## 3つのモード

Angular2では擬似的にWeb Componentのメリットを実現していることがわかりました。ではAngular2 Componentを本物のWeb Componentにできるのでしょうか？
すこしAngular2 Dartの実装をのぞいてみます。

```dart:angular2-2.0.0-beta.0/lib/src/core/metadata/view.dart
/**
 * Defines template and style encapsulation options available for Component's [View].
 *
 * See [ViewMetadata#encapsulation].
 */
enum ViewEncapsulation {
  /**
   * Emulate `Native` scoping of styles by adding an attribute containing surrogate id to the Host
   * Element and pre-processing the style rules provided via
   * [ViewMetadata#styles] or [ViewMetadata#stylesUrls], and adding the new Host Element
   * attribute to all selectors.
   *
   * This is the default option.
   */
  Emulated,
  /**
   * Use the native encapsulation mechanism of the renderer.
   *
   * For the DOM this means using [Shadow DOM](https://w3c.github.io/webcomponents/spec/shadow/) and
   * creating a ShadowRoot for Component's Host Element.
   */
  Native,
  /**
   * Don't provide any template or style encapsulation.
   */
  None
}
```

---
Angular2はTypeScriptで開発されてDartにトランスパイルされてpub packageとして提供されています。トランスパイルされたコードもこのようにソースコードの可読性が保たれ、コメントも適切にメンテナンスされていることが期待できます。

---

enumでモードが定義されています。
つまり、Angular2 Componentのカプセル化には3つのモードが存在します。

* Emulated: 上記説明の通り、擬似的なカプセル化を行う。
* Native: レンダラーの提供するカプセル化を利用する。（DOMの場合はShadow DOM）
* None: templateとstyleのカプセル化を行わない。
 
Defaultは`Emulated`です。ですので、なにも指定していない先程の例では、`Emulated`モードが適用されていました。

では試しに`encapsulation: ViewEncapsulation.Native`を指定してみます。

```dart:lib/todo_list.dart
@Component(
    selector: 'todo-list',
    templateUrl: './todo_list.html',
    styleUrls: const ['./todo_list.css'],
    encapsulation: ViewEncapsulation.Native,
```

Chromeで開くと、あの奇妙なattributeが存在しなくなり、代わりにShadowRootが現れました。大変きれいでいいですね！

![Screen Shot 2016-01-02 at 6.37.50 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/3b7a4d5f-1960-23b2-fb13-23711bab301b.png "Screen Shot 2016-01-02 at 6.37.50 AM.png")

しかし、Web Components技術、要するにShadow DOMを実装していないブラウザだとどうなるのでしょうか？

試しにFirefoxで開いてみると、案の定、
`EXCEPTION: NullError: receiver.webkitCreateShadowRoot is undefined`
とエラーが出てアプリケーションが正常に起動しません。実装していないので当たり前ですね。**Angular2内部でWeb ComponentsのPolyfillを使っているわけではない**ということも確認できました。

## どのモードを指定するべきか

Web Componentsの進捗はざっくり言うと2015年春から秋にかけて各ブラウザーベンダー間の合意が大きく進み、特に[SafariにShadowDOMの実装が進められている](https://plus.google.com/+YoichiroTanaka/posts/Bw1S4XcoKY2)という明るいニュースがあり全体としては大いに希望が持てるものの、2016年1月の時点では未だに[Chrome系でしかサポートされていません](http://caniuse.com/#search=component)。

したがって、Chrome系しかサポートしなくて良い特殊な事情か、ViewがDOM以外である(これはまだよく調べていないが、例えば[NativeScript](https://www.nativescript.org/)を想定しているのだろうか？)といった事情がない限り、`ViewEncapsulation.Native`は現時点では指定するべきではありません。ShadowDOM Polyfillも使うべきではないでしょう。

全てのモダンブラウザでCustom Elements、ShadowDOMの実装が完了し、ブラウザの実装バグも取れて状況が落ち着くまでは、Defaultの`ViewEncapsulation.Emulated`がやはり妥当です。

また、自分は動作を確認していませんが、`ViewEncapsulation.None`という選択は、組織とプロジェクトの状況によっては、CSSのBEMなどの命名規則を守る方針の下でAngular2の導入コストを下げるという目的ならばありなのかもしれません。


# シンプルなData Binding

Angular2ではData Bindingがシンプルになって、動作の理解が簡単になりました。

## Property Binding

まずは単にDartの`class`に`property`を追加します。Dartの`property`の文法そのものですね。Angular1とは異なり、`scope`のコンセプトはありません。その代わりにComponentが対応するViewのcontextになります。


```dart:lib/todo_list.dart
class TodoList {
  String newItem = 'test';
```

そしてその`property`をDOM elementの`property`に以下のように`[value]="newItem"`とすることでbindします。

```html:lib/todo_list.html
<input class="new-todo" [value]="newItem">
```  

(実際のコードは`[(ngModel)]="newItem"`で双方向に(Bidirectional)Bindingをしていますが、説明のため一時的なコードを提示しています。)

これだけでinput elementのvalue propertyにdata bindできました。

![Screen Shot 2016-01-02 at 12.15.20 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/3bb72837-394d-326a-b624-cafb0e3ae2a8.png "Screen Shot 2016-01-02 at 12.15.20 AM.png")

Propery bindingでは、dataはComponentから対応するViewに常に一方向に(Unidirectional)流れます。


# Event Binding

Event Bindingもシンプルです。TodoList classのmethodをDartの通常の文法で書きます(`addItem()`)。

```dart:todo_list.dart
class TodoList {
  String newItem = 'test';
  
  addItem() {
    this.store.addItem(this.newItem);
    this.newItem = '';
  }
```

そして以下のように`(click)="addItem()"`と書けば`addItem()`がclickイベントにbindされます。

```html:todo_list.html
<button class="add" (click)="addItem()">Add</button>
```
Event bindingでは、dataはViewから対応するComponentに常に一方向に(Unidirectional)流れます。


# 双方向Binding

Property bindingは[]記法で書き常にComponentからViewにdataが流れ、Event bindingは()記法で書き常にViewからComponentにdataが流れます。

さて、ユーザーの`input`の更新を`newItem` propertyに反映させたい場合は、`(input)="newItemChanged($event)"`とevent bindingするのが良さそうです。($eventはDOMのeventです。event bindingのexpressionには$eventを引数に取る事ができます。)

```html
<input class="new-todo"
      [value]="newItem" (input)="newItemChanged($event)">
```

```dart
newItemChanged(KeyboardEvent event) {
  InputElement target = event.target;
  this.newItem = target.value;
}
```

これで結果的に双方向(bidirectional)Bindingを実現できました。

# ローカルテンプレート変数

先ほどの例で、expressionがDOMのEventを引数に取ることができることを説明しました。

```html
<input class="new-todo"
      [value]="newItem" (input)="newItemChanged($event)">
```

EventはDOM APIなので汎用性があるのですが、取り回しと型チェックがやや面倒なのも事実です。もっと簡単にできないでしょうか。ローカルテンプレート変数を使うと簡略化できます。

```html
<input class="new-todo"
      #todoInput [value]="newItem" (input)="newItemChanged(todoInput.value)">
```

`#todoInput`と書くことで、todoInputという変数を宣言することができます。ここでは`Input`の`value` propertyを`todoInput.value`で参照することができました。

以下はExpressionに対応するTodoList classのmethodです。

```dart
newItemChanged(String value) {
  this.newItem = value;
}
```

Keyboard eventからevent targetのvalueを取り出すといったEventを取り回すコードをなくすことで、シンプルで見通しの良いコードになりました。

# NgModel

さらに、これを[(ngModel)]で簡略化することができます。

```html
<input class="new-todo" [(ngModel)]="newItem">
```

これだけです。Dart側のmethodも不要になりました。[(ngModel)]はPropery BindingとEvent Bindingの組み合わせのSyntactic Sugarで、特に`input`を処理する際に便利です。[(ngModel)]の内部構造を知りたい場合は[ANGULAR 2 TEMPLATE SYNTAX](http://victorsavkin.com/post/119943127151/angular-2-template-syntax)を読むかソースコードを読むなどしてください。

# 擬似イベント

Todo Itemを追加する操作として、Addボタンを押す他に、Enterキーを押す操作もサポートするべきでしょう。Dartコード側でkeyupイベントのキーコードをfilterするのもDartのEvent Streamのおかげで書くのは簡単なのですが、Angular2ではさらに簡便な方法を用意してくれています。

```html:todo_list.html
<input class="new-todo" [(ngModel)]="newItem" (keyup.enter)="addItem()">
<button class="add" (click)="addItem()">Add</button>
```

ここでは、`(keyup.enter)`と書くだけで、キーコードをfilterしてくれます。便利ですね。


# なぜData Bindingの文法をAngular1から変えたのか

この決断の背景は非常に面白いので、[ng-conf 2015のkeynote](https://www.youtube.com/watch?v=-dMBcqwvYA0)を見てください。(1年近く前の情報です。自分は最近までチェックしていなかったことを後悔しました。)

要点の一部を抜き出すと、

* Html AttributeはDOM Propertyの値がserializeされたもので、その後にPropertyの値が更新されてもAttributeの値は更新されないケースもある。
* Attributeと異なりPropertyはserializeしなくても良い。よってmodelの値を型を保持したまま子componentに渡すことができる。{{}}expressionのserializeが不要になる。
* ng-*の多数のdirectiveが必要なくなり、DOM propertyにbindするだけで良くなる。

![Screen Shot 2016-01-02 at 9.16.49 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/7ca8f082-9e3f-3ad6-170f-e21137a79b1a.png "Screen Shot 2016-01-02 at 9.16.49 AM.png")

余談ですが、設計思想の違いがあるので単純に比較してもあまり意味はありませんが、ComponentはAngular2のほうがPolymer1よりもよりもかなり書きやすい印象です。


# Storeを追加する

ではここからModel層を追加します。ここではFluxパターンのコンセプトを一部借用して、Storeを追加します。Storeはビジネスロジックを処理し、データを保持します。(ここでは[オリジナルの記事](http://blog.scottlogic.com/2015/12/07/angular-2.html)の考えとコードを尊重しそれに合わせるため、Storeと名づけています。)

```dart
class TodoItem {
  String text;
  TodoItem(this.text);
}

class TodoStore {
  List<TodoItem> items;

  TodoStore() : this.items = [];
}

```

さしあたっては`TodoList` Componentに直接インスタンス化しておきます。後にDIする方法に改良します。

```dart
import './todo_store.dart' as model;

final store = new model.TodoStore();

@Component({ ... })
class TodoList {
  String newItem = 'test';
  model.TodoStore store;
  // 略
}
```

なお、この単純なアプリケーションにおいてActionを定義してDispatherを追加し、それを経由してStoreを駆動させるのは大げさすぎてサンプルとして適当でないので、ここは簡略化して、StoreはTodoList Componentから直接messageを受ける実装に留めています。

このTodoStoreが保持するTodoItemのListを`ngFor`でレンダリングします。

```html
<section class="main">
  <ul class="todo-list">
    <li *ngFor="#item of items">
      <div class="view">
        <label></label>
      </div>
    </li>
  </ul>
</section>
```
(Dart版では、ofではなく#item in itemsと書けるようになって欲しいですね)

# DI (Dependency Injection)

TodoStoreはSingletonであることが求められます。Angular1ではServiceを作りControllerにDIすることで実現していました。Angular2でもDIしましょう。

`TodoList` classに`model.TodoStore store` propertyを追加し、constructorに指定を追加しています。

```dart
import './todo_store.dart' as model;
@Component(
  // 略
)
class TodoList {
  String newItem = 'test';
  model.TodoStore store;

  TodoList(this.store);
  // 略
}  	
```

なお、`TodoList(this.store);`は、

```dart
TodoList(model.TodoStore store) {
  this.store = store;
}
```
と読み替えることができます。

そして`bootstrap()`に`TodoStore`を加える事で完了です。

```dart:main.dart
library todo_app.web;

import 'package:angular2/bootstrap.dart';
import "package:todo_app/todo_list.dart";
import 'package:todo_app/todo_store.dart';

main() => bootstrap(TodoList, [TodoStore]);
```

詳細は[DEPENDENCY INJECTION](https://angular.io/docs/ts/latest/guide/dependency-injection.html)を参照してください。


# TodoItem追加機能

Storeを追加したので、Todo Itemを追加する機能を加えます。

```dart:todo_list.dart
@Component(
  // 略
)
class TodoList {
  String newItem = 'test';
  model.TodoStore store;

  TodoList(this.store);

  addItem() {
    this.store.addItem(this.newItem);
    this.newItem = '';
  }
  // 略
}
```

```dart:todo:store.dart
class TodoStore {
  // 略
  addItem(String newItem) {
    this.items.add(new TodoItem(newItem));
  }
　// 略
}　
```
これだけで、Storeへのitemの追加によるstore propertyの更新、そしてnewItem propertyの更新がViewに反映されました。

![Screen Shot 2016-01-03 at 9.40.28 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/f6d7f4d6-d1c5-7329-cb8a-8296f9a05186.png "Screen Shot 2016-01-03 at 9.40.28 AM.png")


## Angular2は、いつ、どのようにデータの更新をViewに反映するのか？

Angular2はいつ、どのようにデータの更新をViewに反映するのでしょうか？

Angular1では、ScopeとDigest Cycleの概念を用いてデータの更新管理をしていました。これはシンプルなアプリケーションには便利ですが、アプリケーションのサイズが大きくなり管理すべきデータが増えるにつれ無駄な更新チェックが増えて性能が劣化したり、複雑になりがちなDigest Cycleを制御するためにプログラマーがヒューリスティックにDigest Cycleを回すコードを書くケースも有るなど問題を孕んでおり、特に性能の低いモバイル端末では問題になりやすいものでした。

Angular2ではこの問題を解決するために、新たにChange Detectionと名づけた仕組みを導入しています。これにより、"Digest Cycle"的なものの隠蔽と劇的な性能向上を実現しています。

このChange Detectionは、"Zone"([JS](https://www.youtube.com/watch?v=3IqtmUscE_U),[Dart](https://www.dartlang.org/articles/zones/))を用いてBrowserの非同期APIにパッチを当て、DOM eventやHttp request等の各event周辺で自動的に適切に実行される設計になっているようです。そして、Angular1とは異なり、常にRoot Componentから深さ優先探索でConponent treeを捜査してPropertyにbindされている値をチェックし、Viewを更新します。(注:詳細は要確認)。

また、Developmer modeというものがあり、プログラマーがChange Detectionによる更新管理を破壊するコードを書くと、実行時にエラーを吐いてプログラマーに修正を促してくれるようです。この辺りの動作は要確認です。

Change Detectionのさらなる解説は、[Angular2のChange Detectionについて](http://qiita.com/laco0416/items/78edd53f5da8ead02e75)
および[CHANGE DETECTION IN ANGULAR 2](http://victorsavkin.com/post/110170125256/change-detection-in-angular-2)等を参照してください。


# 子componentの追加

現実のAngular2アプリケーションは多数のComponentが木構造を構成するはずです。このTodoListのサンプルでも、子componentの例を説明するため、Root ComponentであるTodoListしかない現在の構成を変更してみます。

todo_list.htmlの一部の、

```html:lib/todo_item.html
<div class="view">
  <label></label>
</div>
```
をView templateにし、TodoItem componentを作成します。

```dart:lib/todo_item.dart
import "package:angular2/angular2.dart";
import "./todo_store.dart" as model show TodoItem;

@Component(
    selector: 'todo-item',
    templateUrl: './todo_item.html',
    styleUrls: const ['./todo_item.css']
)    
class TodoItem {
  @Input()
  model.TodoItem item;
}

```

`@Input()` annotationに注目してください。このannotationが付けられたpropertyは、このcomponentのpublic APIとなり、bindingが可能になります。実際に`<todo-item>`に置き換えた例が以下です。

```html:todo_list.html
<li *ngFor="#item of store.items">
  <todo-item [item]="item"></todo-item>
</li>
```
`[item]="item"`において、@Input() annotationによってTodoItem componentのpublic propertyとして宣言されたitemが、StoreのTodoItem instanceにbindingされています。


# Deleteボタンの追加

子componentのpropertyにbindingする例は確認できました。次はDeleteボタンの追加を通じて、componentをDOM elementとみなし、componentからeventを発火させる例を見てみます。


```html:lib/todo_item.html
<div class="view">
  <label>{{item.text}}</label>
  <button (click)="doneClicked()" class="destroy"></button>
</div>
```
TodoItem componentのtemplateにDeleteボタンを追加します。


```dart:lib/todo_item.dart
library todo_app.todo_item;

import "package:angular2/angular2.dart";
import "./todo_store.dart" as model show TodoItem;

@Component(
    selector: 'todo-item',
    templateUrl: './todo_item.html',
    styleUrls: const ['./todo_item.css']
)    
class TodoItem {
  @Input()
  model.TodoItem item;

  @Output()
  EventEmitter<model.TodoItem> done = new EventEmitter();

  doneClicked() {
    this.done.add(this.item);
  }
}
```

ここで、`<todo-item>`からeventを発火させるために、`@Output()`が付加された`EventEmitter`を追加しました。そして、DeleteボタンをクリックするとこのEventEmitterがeventを発火させるmethodをbindingしています。

これで、`done` eventがTodoItem componentから発火するようになったので、親componentのTodoList componentでこの`done` eventをbindingしましょう。

```html:todo_list.html
<todo-item [item]="item" (done)="removeItem($event)"></todo-item>
```

```dart:todo_list.dart
removeItem(model.TodoItem item) {
  this.store.removeItem(item);
}
```
ここで、TodoStoreにmodel.TodoItemを削除させます。

これでDeleteボタンの実装は完了です。


# Change Detectionのさらなる効率化

実際に、Change Detectionがいつ実行され、Propery Bindingを通じてStoreのmodel objectであるTodoItemのデータにアクセスするのか確認してみましょう。

```html:lib/todo_item.html
<div class="view">
  <label>{{item.text}}</label>
  <button (click)="doneClicked()" class="destroy"></button>
</div>
```

ここで、Dartの[SetterとGetter](https://www.dartlang.org/dart-tips/dart-tips-ep-10.html)の機能を使い、textにアクセスするたびにconsoleにlogを出すようにします。

```dart:todo_store.dart
class TodoItem {
  String _text;

  get text {
    print('getting value for text ${this._text}');
    return this._text;
  }

  set text(String value) {
    this._text = value;
  }

  TodoItem(this._text);
}
```

(`print('getting value for text ${this._text}');`に注目してください。)

Browserで動作を確認してみます。

![Screen Shot 2016-01-04 at 10.21.37 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/60152e96-aa31-037f-0b6b-ad6c6bfe2378.png "Screen Shot 2016-01-04 at 10.21.37 AM.png")

Input fieldにKey入力するたびに大量のlogが吐かれてしまいました。つまり、ここでChange DetectionはKey入力するたびに全てのTodoInputのtextの値をチェックし、変更がないか確認しています。これは実際のTodo Listアプリケーションの動作から考えると明らかに不要なチェックであり、非効率に思えます。

Angular2はBrowserのeventの際に毎回Change Detectionを実行し、深さ優先探索で**デフォルトでは全てのComponentを捜査**します。これは非効率に思えますが、実際は[そのパフォーマンスは非常に高い](https://docs.google.com/document/d/10W46qDNO8Dl0Uye3QX0oUDPYAwaPl0qNy73TVLjd1WI/edit)ため殆どのケースでは問題にならないようです。とはいえ、明らかに不要なチェックは省略できないでしょうか。


# イミュータビリティ

Angular2のComponentはそれぞれに`ChangeDetectionStrategy`を指定する事ができます。この指定によってより効率的なチェックにすることが可能です。

今回のTodo Listアプリケーションの仕様では、TodoItem componentのデータはcomponent生成後に更新されることはないため、実質的にイミュータブルであるとみなすことができます。そのためComponentの生成後はChange Detectionのチェックをスキップさせて問題ないと判断できます。`ChangeDetectionStrategy`を`OnPush`に変更することでそのように指示しましょう。

```dart:lib/todo_item.dart
@Component(
    selector: 'todo-item',
    // 略
    changeDetection: ChangeDetectionStrategy.OnPush)
class TodoItem {
  // 略
}
```

![Screen Shot 2016-01-04 at 11.13.19 AM.png](https://qiita-image-store.s3.amazonaws.com/0/8065/9b6f98cc-2197-855e-ba3f-01660d281403.png "Screen Shot 2016-01-04 at 11.13.19 AM.png")

これでKey入力のたびに無駄に各TodoItem componentをチェックされることがなくなり、生成時に一度だけチェックされるようになりました。


さらなる学習には以下の資料が参考になります。
[Angular2のChange Detectionについて(再掲)](http://qiita.com/laco0416/items/78edd53f5da8ead02e75)
[CHANGE DETECTION IN ANGULAR 2(再掲)](http://victorsavkin.com/post/110170125256/change-detection-in-angular-2)
[ANGULAR, IMMUTABILITY AND ENCAPSULATION](http://victorsavkin.com/post/133936129316/angular-immutability-and-encapsulation)

Change Detectionの実装を探検してみるのも面白いかもしれません。

# まとめ

ベータ版が出てから一気に学んだ印象では、Angular2は文法と動作を理解しやすくかつ素晴らしく効率的なWebフレームワークです。また、これも個人的な印象ですが、Angular2はAngular1時代に感じたフラストレーションを解消して自分のメンタルモデルに自然にフィットする感覚で、書いていて気持ちが良いです。

Angular2とImmutableライブラリやFluxパターンとの組み合わせなど、これからプラクティスが沢山出てくるでしょう。自分もAngular2を追っていく中で紹介していけたらと思います。

また、Dartは言語そのものは生産性が高くて素晴らしいのですが、Reflection(Mirror)APIを使うと注意深く設定しなければJSへのCompile時にTree Shakingが失敗してコードサイズが膨れ上がるという問題を抱え、私見では恐らくそれが大きな要因でなかなか信頼の置けるWebフレームワークが確立されないという状況でした。Reflection問題は[Reflectable](https://github.com/dart-lang/reflectable/tree/master/reflectable)で解決が期待されますが、Angular2では内部でこの問題に独自のReflectable的な仕組みを導入するなど[JSへのコンパイル後のコードサイズを小さく保つことに気を払って](https://github.com/angular/angular/issues/5312)おり、DartにとってはAngular2がキラーフレームワークとなる期待が持てます。

2016年は、Angular2と願わくばDartでReactiveなWebApp開発が盛り上がる年になったらいいですね。


# 個人的なTODO

* Client side routing (For SPA)
* Dart版ののhttp moduleについて。Dart SDKのものをそのまま使うのか各自工夫するのか、それともAPIをTypeScript版に合わせた薄いwrapperが提供されるのか。
* Angular Material for Angular2の進捗(pub packageとしてすでにbeta versionが提供されているが、まだ初期段階に見える)
* Angular2は必要に応じてPolymer Element等のWeb ComponentをProperty BindingやEvent Bindingで取り扱うことができるが、例えばPolymerDartとの組み合わせは実際に実用的なのかどうか。
* Angular2独自のreflectableの仕組み
* Angular2のtransformerの仕組み