Angular 2入門：NgModelに触れてみる[rc.5対応]

こんにちは。いまいです。
Angular2がそろそろstableになるということで、触れてみる次第です。

私が初めてAngular JSと触れた時に一番印象的だったのは双方向バインディングだったような気がします。その双方向バインディングがAngular 2でどうなったのかを見てみたいと思います。

---


#前提
- AngularJSに触れたことがある方を対象とします。
- セットアップの手順を省くため、@armorik83 氏の[npm iしてAngular 2のHello World!を書くところまで](http://qiita.com/armorik83/items/ae737ab584012a0f5876)を読まれた前提で話を進めていきます。
- 対象のAngular2のバージョンは2.0.0-rc.5です。
- この記事では、Angular 1.xをAngular JS。Angular 2.xをAngular 2と呼称します。
- 公式サイト: https://angular.io/

#下準備
基本的な環境、構成は「[npm iしてAngular 2のHello World!を書くところまで](http://qiita.com/armorik83/items/ae737ab584012a0f5876)」と同じですが、`NgModel`を利用するために`FormsModule`を読み込む必要があります。`FormsModule`は`@angular/forms`にあるので、これをnpmで引っ張ってきます。

```
npm i --save @angular/forms
```

加えて、`app.module.ts`に`FormsModule`を加えます。


```app.module.ts
import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {AppComponent} from "./app.component";
import {FormsModule} from "@angular/forms"; // 追加

@NgModule({
  imports: [
    BrowserModule,
    FormsModule // 追加
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

#ModelからViewへ
MVCモデルでいうModelからViewへ値を反映させてみます。

```app.component.ts
// Componentを作成するため、`@angular/core`から`Component`をimportしてます。
import {Component} from "@angular/core";

// `@Component`という見慣れない書き方をしていますが、これはTypescriptが採用しているDecorator構文です。
// 今ではES7の仕様としても検討されています。(20160818時点でstage2)
// このDecoartor構文により、`AppComponent`クラスに`selector`、`template`のメタデータを付与することができます。
@Component({
  selector: "my-app",
  template: `
    <h1>{{title}}</h1>
    <input type='text' [value]="title">
  `
})
export class AppComponent {
  title: string = "Hello World";
}
```

`[value]="title"`と記述することで、`AppComponent`クラスで宣言している`title="HelloWorld"`を、input要素の`value`というプロパティに反映させることできます。`[target]="expression"`か`bind-target="expression"`もしくは`target={{expression}}`という記述で任意のProperty、Attribute、Style、Classに値をバインディングすることができます。`[...]`という表記にてインプットプロパティを意味しています。


#ViewからModelへ
MVCモデルでいうViewからModelへ値を反映させてみます。

```app.component.ts
import {Component} from "@angular/core";

@Component({
  selector: "my-app",
  template: `
    <h1>{{title}}</h1>
    <input type='text' (input)="title=$event.target.value">
  `
})
export class AppComponent {
  title: string = "";
}
```

`(input)="title=$event.target.value"`と記述していますが、ユーザーの入力によって[input event](https://developer.mozilla.org/en-US/docs/Web/Events/input)が発火した時に、`title`に`<input>`の値を反映させています。`(event名)="expression"`もしくは、`on-event名="expression"`という記述でEventをバインディングすることができます。`(...)`という表記にてアウトプットプロパティを意味しています。

#双方向バインディング(ModelからViewへ、ViewからModelsへ)
今度は上の二つを、ModelからView、ViewからModelを実現してみます。

##ややこしい書き方
```app.component.ts
import {Component} from "@angular/core";

@Component({
  selector: "my-app",
  template: `
    <h1>{{title}}</h1>
    <input type='text' [value]="title" (input)="title=$event.target.value">
  `
})
export class AppComponent {
  title: string = "Hello World";
}
```
双方向バインディングを実現するにはそのまま`[value]` `(input)`を書けばいけます。しかし、まあめんどくさいですね。 Angular JSでは、[`ng-model`](https://docs.angularjs.org/api/ng/directive/ngModel)というDirectiveを利用していたかと思いますが、Angular 2では[`NgModel`](https://angular.io/docs/ts/latest/api/forms/index/NgModel-directive.html)というDirectiveが存在します。これを使って次はもっと簡潔に書きます。

##楽な書き方
はい。本題です。[NgModel](https://angular.io/docs/ts/latest/api/forms/index/NgModel-directive.html)を使って双方向バインディングを実現します。

```
import {Component} from "@angular/core";

@Component({
  selector: "my-app",
  template: `
    <h1>{{title}}</h1>
    <input type='text' [(ngModel)]="title">
  `
})
export class AppComponent {
  title: string = "Hello World";
}
```
これでもっと簡単に双方向バインディングを行えました。`[(...)]`という記法が特徴的ですね。公式サイトではこの記法を`banana in a box`と呼称していました。（確かにバナナがボックスに入っているような、、、）
これまでの話を踏まえるとインプットとアウトプットの両方を実現するという意味を読み取ることができるかと思います。
ちなみに、`[(ngModel)]="title"` <=> `[ngModel]="title" (ngModelChange)="title=$event"` <=> `bindon-value="title"`と書くこともできます。 ***インプットとアウトプットの両方のメソッドをあるというのは、データのバインディング方向を意識しているのがうかがえますね。***


#最後に
- [ここらへん](https://github.com/angular/angular/blob/9adf80385b753ffa9b802bbb872dd2d902c7bc31/modules/%40angular/compiler/src/template_parser/template_parser.ts#L36)で`()``[]``bind-``on-`などをパースしていますね。ソースを見ると理解が深まります。
- `[ngModel]`を`[ng-model]`や`[NgModel]`と書いてもエラーが出て止まっちゃうので気をつけてください。
- 今回のソースは[GitHub](https://github.com/shinsukeimai/advent-angular2)にあげておきます。