こんにちは。いまいです。
Angular2がそろそろstableになるということで、触れてみる次第です。
私が初めてAngular JSと触れた時に一番印象的だったのは双方向バインディングだったような気がします。その双方向バインディングがAngular 2でどうなったのかを見てみたいと思います。
#前提
- AngularJSに触れたことがある方を対象とします。
- セットアップの手順を省くため、@armorik83 氏のnpm iしてAngular 2のHello World!を書くところまでを読まれた前提で話を進めていきます。
- 対象の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!を書くところまで」と同じですが、NgModel
を利用するためにFormsModule
を読み込む必要があります。FormsModule
は@angular/forms
にあるので、これをnpmで引っ張ってきます。
npm i --save @angular/forms
加えて、app.module.ts
にFormsModule
を加えます。
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へ値を反映させてみます。
// 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へ値を反映させてみます。
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が発火した時に、title
に<input>
の値を反映させています。(event名)="expression"
もしくは、on-event名="expression"
という記述でEventをバインディングすることができます。(...)
という表記にてアウトプットプロパティを意味しています。
#双方向バインディング(ModelからViewへ、ViewからModelsへ)
今度は上の二つを、ModelからView、ViewからModelを実現してみます。
##ややこしい書き方
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
というDirectiveを利用していたかと思いますが、Angular 2ではNgModel
というDirectiveが存在します。これを使って次はもっと簡潔に書きます。
##楽な書き方
はい。本題です。NgModelを使って双方向バインディングを実現します。
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"
と書くこともできます。 インプットとアウトプットの両方のメソッドをあるというのは、データのバインディング方向を意識しているのがうかがえますね。
#最後に