今回は、ヒーローの情報を編集する簡単なエディタを作る。
あらゆる物語にはスタート地点がある。 今回の話は クイックスタート が終わったところから始めよう。
angular2-tour-of-heroes
という名前でフォルダを作り、クイックスタートと同じ手順を繰り返し、
アプリケーションを作っていくためのフォルダ構造を作ろう。
あるいは、クイックスタート終了時の全ソースコードをダウンロードして始めることもできる。
下記のような構造になっていればよい。
angular2-tour-of-heroes
├app
│├app.component.ts
│└main.ts
├node_modules ...
├typings ...
├index.html
├package.json
├styles.css
├tsconfig.json
└typings.json
npm start
で TypeScript の自動コンパイルとアプリケーションのリロードを有効にしておこう。
このコマンドは TypeScript のコンパイラを watch mode で動かし、サーバーを動作させる。
アプリケーションをブラウザで起動し、開発中にアプリケーションを動作させたまま更新を反映させることができる。
ヒーローの情報をアプリケーションに表示したい。
AppComponent
に2つのプロパティを追加しよう。
title
プロパティはこのアプリケーションの名前、
そして hero
プロパティは「Windstorm」という名前のヒーローのことだ。
export class AppComponent {
title = 'Tour of Heroes';
hero = 'Windstorm';
}
そして、 @Component
内の(HTMLの)テンプレートを、新しいプロパティをバインディングするように書き換える。
template: '<h1>{{title}}</h1><h2>{{hero}} details!</h2>'
反映したら、ブラウザは更新され、タイトルとヒーローの情報が表示されるだろう。
{{title}}
や {{hero}}
は、コンポーネントのプロパティから値を読み出し、それを表示せよ、というアプリケーションへの指示を示している。
つまりこれは、 片方向データバインディング(one-way data binding) によるデータの 補完(interpolation) である。
この補完については、データの表示の章で、より深く学べるだろう。
今のところ、ヒーローの情報は名前を示す文字列でしかない。 もっとたくさんのプロパティがこの先必要になってくる。 そこで、ヒーローの情報を単なる文字列から クラス(Class) に変えてみよう。
id
と name
というプロパティを持つ Hero
というクラスを作ってみる。
app.component.ts
の上部、import
宣言のすぐ下に書き加えてみよう。
export class Hero {
id: number;
name: string;
}
これでヒーローを示すクラスができたので、コンポーネントの hero
プロパティを Hero
型に直してみよう。
id
は 1 で、name
は「Windstorm」だ。
hero: Hero = {
id: 1,
name: 'Windstorm'
};
ヒーローの情報を文字列からオブジェクトに変えてしまったので、今度はテンプレートも、オブジェクトの中の name
プロパティを参照するように変更する必要がある。
template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>'
反映したら、ブラウザは更新され、ヒーローの名前が正しく表示されるだろう。
名前はしっかり表示できた。しかし、ヒーローの情報をすべて表示したい。
<div>
を使って、ヒーローの ID と名前をそれぞれ別枠で表示しよう。
template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2><div><label>id: </label>{{hero.id}}</div><div><label>name: </label>{{hero.name}}</div>'
ああ…とても長い文字列になってしまった。テンプレートの編集時にミスをしないように、よりよい形にしてみよう。
文字列を連結することでより長いテンプレートを作ることができ、それを読むことは不可能ではないだろう。
しかしおそらく醜く、読みづらく、編集で構文エラーを混ぜ込みやすいものになる。
そこで、 ES2015 と TypeScript のテンプレート文字列用構文を使い、後で発狂してしまわないようにメンテナンスしてみよう。
今シングルクォートで囲っている文字列をバッククォートで囲むようにして、 <h1>
、 <h2>
、 <div>
でそれぞれ改行してみよう。
template:`
<h1>{{title}}</h1>
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div><label>name: </label>{{hero.name}}</div>
`
ヒーローの名前をテキストフィールドにして、編集できるようにしたい。
<label>
の次にあるヒーローの名前を、 <input>
を使って以下のように書き換えてみよう。
template:`
<h1>{{title}}</h1>
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input value="{{hero.name}}" placeholder="name">
</div>
`
これでヒーローの名前が <input>
のテキストフィールドで表示されるようになった。
しかし何かがおかしい。
名前を書き換えたとき、それが <h2>
の中身には反映されていないのだ。
<input>
への片方向バインディングでは、そのような動作をさせることはできない。
これから <input>
フィールドにヒーローの名前を表示し、それを編集し、
編集した内容が、ヒーローの名前を表示するあらゆる場所に反映されるようにする。
つまり、これは 双方向データバインディング(two-way data binding) をする、ということだ。
テンプレートを ngModel
という双方向データバインディングのための基本ディレクティブを使うように書き換えてみよう。
<input>
の部分を書き換えよう。
<input [(ngModel)]="hero.name" placeholder="name">
反映したら、ブラウザは更新され、表示された名前を書き換えたときに <h2>
に即座に反映されるだろう。
今回作ったものを整理してみよう。
{{...}}
という記法によるデータの補完で、アプリケーションのタイトルとヒーローのオブジェクトのプロパティを表示した。- ES2015形式のテンプレート記法でテンプレートを読みやすくした。
<input>
にngModel
ディレクティブを使うことにより、ヒーローの名前を編集可能にした。ngModel
ディレクティブは変更内容を別の部分の表示に伝播させた。
最終的なapp.component.ts
はこのようになっているはずだ。
import {Component} from 'angular2/core';
export class Hero {
id: number;
name: string;
}
@Component({
selector: 'my-app',
template:`
<h1>{{title}}</h1>
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name">
</div>
`
})
export class AppComponent {
title = 'Tour of Heroes';
hero: Hero = {
id: 1,
name: 'Windstorm'
};
}
今回作ったアプリケーションでは、一人のヒーローしか表示していなが、本当はたくさんいるヒーローを一覧したい。 また、それらのヒーローの中から一人を選択して情報を表示したい。 次回は、そのような一覧を検索したり、テンプレートに情報を補完したり、選択できるようにする方法を学んでいく。