もっとヒーローが必要
私たちの物語にはもっと多くのヒーローが必要です。 Heroes Tourアプリケーションを展開して、ユーザーがヒーローを選択し、彼に関する詳細情報を表示できるヒーローのリストを表示します。
ヒーローのリストを表示するために必要なものについて考えてみましょう。 まず、表示するヒーローのリストを含む配列が必要です。 次に、配列からテンプレートにデータを渡す方法が必要です。
私たちが泊まった場所
2番目のヒーローツアーに進む前に、 最初のパートを完了する必要があります。 まだ行っていない場合は、戻ってください。
コード変換とアプリケーション実行をサポート
TypeScriptコンパイラを実行して、ファイルの変更を監視し、すぐにコンパイルし、Webサーバーも起動するようにします。 入力することでこれを行います
npm start
これにより、ヒーローツアーの作成を継続している間、アプリケーションが実行され続けます。
ヒーローの展示
ヒーロー作成
app.component.ts
の下部に10人のヒーローの配列を作成してみましょう。
app.component.ts(ヒーローの配列)
let HEROES: Hero[] = [ { "id": 11, "name": "Mr. Nice" }, { "id": 12, "name": "Narco" }, { "id": 13, "name": "Bombasto" }, { "id": 14, "name": "Celeritas" }, { "id": 15, "name": "Magneta" }, { "id": 16, "name": "RubberMan" }, { "id": 17, "name": "Dynama" }, { "id": 18, "name": "Dr IQ" }, { "id": 19, "name": "Magma" }, { "id": 20, "name": "Tornado" } ];
HEROES
は、最初のパートで作成したクラスであるHero
タイプの要素の配列です。 もちろん、このヒーローのリストをWebサービスから取得したいのですが、小さなステップを踏んで、最初にモックオブジェクト(以前に指定されたデータを返すスタブ)からヒーローを表示します。
ヒーローの表現
バインドするヒーローのソースとなるプロパティをAppComponent
作成しましょう。
app.component.ts(プロパティ-ヒーローの配列)
public heroes = HEROES;
heroes
のタイプを明示的に定義する必要はありません。 TypeScriptはHEROES
変数から取得できます。
ここで、このコンポーネントクラスでヒーローのリストを定義できます。 しかし、最終的にはWebサービスからヒーローを取得することを知っています。 したがって、このデータをクラス実装からすぐに引き出すことは理にかなっています。
テンプレートにヒーローを表示する
コンポーネントにはheroes
が含まれています。 テンプレートに順不同リストを作成して表示してみましょう。 ヒーローに関する詳細情報の上の見出しの下に、次のHTMLを挿入します。
app.component.ts(ヒーローテンプレート)
<h2>My Heroes</h2> <ul class="heroes"> <li> <!-- each hero goes here --> </li> </ul>
これで、ヒーローを入力できるテンプレートができました。
ngForでヒーローをリストする
コンポーネント内のヒーローの配列をテンプレートに関連付け、それを反復処理し、各ヒーローを個別に表示したいと思います。 これを行うには、Angularの助けが必要です。 ステップバイステップで始めましょう:
最初に、組み込み*ngFor
ディレクティブを追加して<li>
変更します。
app.component.ts(ngFor)
<li *ngFor="#hero of heroes">
* ngFor`の)
の先頭のアスタリスク( `)
は、構文の重要な部分です。**
ngFor
の*
プレフィックスは、 <li>
要素とその子がマスターテンプレートを構成することを示します。
ngFor
ディレクティブはheroes
配列を通過して、 AppComponent.heroes
プロパティを返し、テンプレートに表示します。
ngFor
によって割り当てられた引用テキストは、「 heroes
配列の各ヒーローを取得し、ローカル変数hero
に保存し、対応するテンプレートインスタンスで使用できるようにする」ことを意味します。
hero
前のプレフィックス#
は、ヒーローをローカルテンプレート変数として識別します。 次に、テンプレートでこの変数を参照して、ヒーローのプロパティにアクセスできます。
ngFor
およびローカルテンプレート変数の詳細については、 データマッピングおよびテンプレート構文の章をngFor
ください。
ここで、 <li>
タグの間にコードを挿入します。このコードは、 hero
テンプレート変数を使用してhero
のプロパティを表示します。
app.component.ts(ngForテンプレート)
<li *ngFor="#hero of heroes"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li>
ブラウザがページを更新すると、ヒーローのリストが表示されます!
ヒーローにスタイルを追加する
ヒーローのリストはかなり退屈に見えます。 どのヒーローが選択され、カーソルが現在どのヒーローの上にあるかをユーザーが理解できるように、ユーザーに視覚的にわかりやすくしたいと思います。
コンポーネントにスタイルを追加しましょう。 次のCSSクラスを@Component
デコレーターのstyles
プロパティに割り当てます。
app.component.ts(スタイルの追加)
styles:[` .selected { background-color: #CFD8DC !important; color: white; } .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } .heroes li.selected:hover { background-color: #BBD8DC !important; color: white; } .heroes li:hover { color: #607D8B; background-color: #DDD; left: .1em; } .heroes .text { position: relative; top: -3px; } .heroes .badge { display: inline-block; font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; background-color: #607D8B; line-height: 1em; position: relative; left: -1px; top: -4px; height: 1.8em; margin-right: .8em; border-radius: 4px 0 0 4px; } `]
長い文字列の複数行表現には、再び `-notationを使用することに注意してください。
コンポーネントにスタイルを割り当てると、スタイルはその特定のコンポーネントのスコープ内にのみ存在します。 したがって、スタイルはAppComponent
のみ適用され、外部HTMLに「漏れる」ことはありません。
ヒーローを表示するためのテンプレートは次のようになります。
app.component.ts(ヒーローのスタイル)
<h2>My Heroes</h2> <ul class="heroes"> <li *ngFor="#hero of heroes"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul>
たくさんのスタイル! ここに示すように、それらをコンポーネントの説明に含めるか、別のファイルに移動して、コンポーネントコードを簡素化します。 これは次の章で行います。 とりあえず、そのままにしておきましょう。
ヒーローセレクション
このアプリケーションには、ヒーローのリストと1人のヒーローに関する情報があります。 ヒーローのリストと1人のヒーローは、まったく関係ありません。 ユーザーにリスト内のヒーローを選択してもらい、選択したヒーローに関する情報が詳細ビューに表示されるようにします。 このUIパターンは一般に「マスター/ディテール」として知られています(「マスター/スレーブ」と翻訳されますが、「マスター/ディテール」は将来使用される予定です)。 この場合、マスターはヒーローのリストであり、詳細は選択したヒーローの詳細な表現です。
リスト内のヒーローのクリックのイベントに関連付けられた「selectedHero」コンポーネントのプロパティを介して、マスターを詳細に接続しましょう。
クリックイベント(マウスクリック)
Angularイベントバインディングを使用してクリックイベント処理を挿入することにより、 <li>
変更します。
app.component.ts(クリックイベントキャプチャ)
<li *ngFor="#hero of heroes" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li>
イベントバインディングに焦点を当てる:
(click)="onSelect(hero)"
括弧は、クリックイベントのターゲットとして<li>
要素を指定します。 等号の右側の式は、 AppComponent
コンポーネントにあるonSelect()
メソッドを呼び出し、 hero
テンプレートのローカル変数を引数として渡します。 これは、以前にngFor
定義したのと同じhero
変数です。
イベントバインディングの詳細については、「 ユーザー入力」および「 テンプレート構文 」の章を参照してください。
クリックハンドラーを追加する
このイベントは、まだ存在しないonSelect
メソッドに関連付けられています。 このメソッドをコンポーネントに追加します。 このメソッドは何をすべきですか? コンポーネントの変数「selected hero」に、ユーザーがクリックしたヒーローを書き込む必要があります。
これまでのところ、コンポーネントにはそのような変数はないので、追加することから始めましょう。
選択したヒーローの任命
AppComponent
hero
プロパティは必要なくなりました。 selectedHero
プロパティに置き換えます。
app.component.ts(selectedHero)
selectedHero: Hero;
ユーザーが自分でヒーローを選択するまでヒーローは重要ではないと判断したため、ヒーローで行ったようにselectedHero
初期化しません。
次に、ユーザーがクリックしhero
selectedHero
プロパティに値hero
書き込むonSelect
メソッドを追加します。
app.component.ts(onSelect)
onSelect(hero: Hero) { this.selectedHero = hero; }
選択したヒーローの詳細情報をテンプレートに表示する必要があります。 現時点では、テンプレートはまだ古いhero
プロパティにアクセスしています。 テンプレートを修正して、新しいselectedHero
プロパティにバインドします。
app.component.ts(selectedHeroにスナップ)
<h2>{{selectedHero.name}} details!</h2> <div><label>id: </label>{{selectedHero.id}}</div> <div> <label>name: </label> <input [(ngModel)]="selectedHero.name" placeholder="name"/> </div>
ngIfで空のデータを非表示にする
アプリケーションをダウンロードすると、ヒーローのリストが表示されますが、ヒーローは選択されていません。 selectedHero
定義さselectedHero
いません。つまり、 undefined
です。 これが、ブラウザコンソールに次のエラーメッセージが表示される理由です。
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
覚えているように、テンプレートにはselectedHero.name
が表示selectedHero.name
れます。 プロパティを含むselectedHero
変数が定義されていないため、 name
プロパティは存在しません。
ヒーローが選択されるまで、ヒーローに関する詳細情報をDOMから削除することにより、この問題を解決します。
ヒーローに関する詳細情報を含むHTMLを<div>
ラップしました。 組み込みのngIf
ディレクティブを追加し、コンポーネントのselectedHero
プロパティをそれに設定します。
app.component.ts(ngIf)
<div *ngIf="selectedHero"> <h2>{{selectedHero.name}} details!</h2> <div><label>id: </label>{{selectedHero.id}}</div> <div> <label>name: </label> <input [(ngModel)]="selectedHero.name" placeholder="name"/> </div> </div>
*「ngIf」の前の先頭のアスタリスク( )は構文の重要な部分であることを忘れないでください **
selectedHero
変数が定義されるまで、 ngIf
ディレクティブは詳細なヒーロー情報を含むHTMLをDOMから削除します。 したがって、ヒーローに関する詳細な情報を持つ要素も、心配する価値のあるバインディングもありません。
ユーザーがリストからヒーローを選択すると、 selectedHero
変数が値を取得して定義され、 ngIf
がヒーローに関する詳細情報を含むデータをDOMに配置し、ネストされたバインディングを実装します。
ngIf
と `ngFor 'は、「構造ディレクティブ」と呼ばれます。これは、DOMの一部の構造を変更できるためです。 つまり、AngularがDOMでコンテンツをレンダリングする方法の構造を定義します。
ngIf、ngFor、およびその他の構造化ディレクティブの詳細については、 構造ディレクティブとテンプレート構文の章を参照してください。
ブラウザが更新され、ヒーローのリストが表示されますが、選択したヒーローに関する詳細情報は表示されません。 NgIf
は、 selectedHero
変数が定義されるまで、DOMの外部に保存します。 リスト内のヒーローをクリックすると、選択したヒーローに関する詳細情報が表示されます。 すべてが期待どおりに機能します。
選択したアイテムのスタイル設定
リストの下に選択したヒーローに関する情報が表示されますが、上のリストではこのヒーローをすばやく見つけることができません。 これを修正するには、CSSでselected
クラスをメインリストの対応する<li>
要素に適用します。 たとえば、ヒーローのリストからマゼンタを選択すると、次のように背景色を変更して視覚的に強調表示することができます。
テンプレートでselected
クラスを設定するために、 class
要素にバインディングプロパティを追加します。 これは、現在のselectedHero
とhero
を比較する式によって行います。
キーはCSSクラスの名前です( selected
)。 両方のヒーローが一致する場合、値はtrue( true
)、そうでない場合はfalse( false
)です。 「文字が一致する場合はselected
クラスを適用し、一致しない場合は削除します」と言います 。
app.component.ts(CSSクラスの設定)
[class.selected]="hero === selectedHero"
テンプレートでは、 class.selected
角括弧( []
)で囲まれていることに注意してください。 これは、データソース(式hero === selectedHero
)からclass
プロパティにデータフローが一方向に進むプロパティバインディングの構文です。
app.component.ts(各ヒーローの様式化)
<li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li>
プロパティのバインドの詳細については、テンプレートの構文の章を参照してください。
ブラウザがアプリケーションを再起動します。 マゼンタのヒーローを選択します。選択は背景色によって明確に識別されます。
別のヒーローを選択すると、背景色がこのヒーローに切り替わります。
現時点でのapp.component.ts
の完全なコンテンツはapp.component.ts
です。
import {Component} from 'angular2/core'; export class Hero { id: number; name: string; } @Component({ selector: 'my-app', template:` <h1>{{title}}</h1> <h2>My Heroes</h2> <ul class="heroes"> <li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul> <div *ngIf="selectedHero"> <h2>{{selectedHero.name}} details!</h2> <div><label>id: </label>{{selectedHero.id}}</div> <div> <label>name: </label> <input [(ngModel)]="selectedHero.name" placeholder="name"/> </div> </div> `, styles:[` .selected { background-color: #CFD8DC !important; color: white; } .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } .heroes li.selected:hover { background-color: #BBD8DC !important; color: white; } .heroes li:hover { color: #607D8B; background-color: #DDD; left: .1em; } .heroes .text { position: relative; top: -3px; } .heroes .badge { display: inline-block; font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; background-color: #607D8B; line-height: 1em; position: relative; left: -1px; top: -4px; height: 1.8em; margin-right: .8em; border-radius: 4px 0 0 4px; } `] }) export class AppComponent { title = 'Tour of Heroes'; heroes = HEROES; selectedHero: Hero; onSelect(hero: Hero) { this.selectedHero = hero; } } var HEROES: Hero[] = [ { "id": 11, "name": "Mr. Nice" }, { "id": 12, "name": "Narco" }, { "id": 13, "name": "Bombasto" }, { "id": 14, "name": "Celeritas" }, { "id": 15, "name": "Magneta" }, { "id": 16, "name": "RubberMan" }, { "id": 17, "name": "Dynama" }, { "id": 18, "name": "Dr IQ" }, { "id": 19, "name": "Magma" }, { "id": 20, "name": "Tornado" } ];
歩いた道
この章で達成したことは次のとおりです。
- ヒーローツアーにヒーローのリストが表示されるようになりました。
- ヒーローを選択し、選択したヒーローに関する詳細情報を表示する機能を追加しました。
- コンポーネントテンプレートで
ngIf
およびngFor
組み込みディレクティブを使用する方法を学びました。
今後のパス
私たちのヒーローツアーは成長しましたが、まだ完全ではありません。 アプリケーション全体を1つのコンポーネントに入れることはできません。 それをサブコンポーネントに分解し、それらを連携させる方法を教える必要があります。 次の章でこれを行う方法を学びます。