前回の投稿Angluar2のクイックスタートとチュートリアルを実施 - その2の続きです。
前回作成したソースを使用します。
本投稿の参照元(英語)
(バージョンが異なりますが、大きな影響はないと思います。)
Master/Detail - ts
本章で学ぶこと
以下を学習します。
- 選択可能なヒーローの一覧を表示する
- 一覧選択時(クリック時)ヒーローの詳細を表示する
- ディレクティブ
ngFor
とngIf
の使い方
実行結果
本章を完了すると以下リンク先のようなものが出来上がります。
ヒーローのリストを表示し、選択(クリック)すると、下に詳細が表示されます。
live-examples
事前準備
- 以下のコマンドによりサーバの起動を行います。プログラムの変更が即座にブラウザに反映されます。(サーバを落としていなければ不要です。)
cd angular2-tour-of-heroes
npm start
#ヒーローの一覧表示
ヒーローの作成
ヒーロークラスの定数配列HEROES
を追加します。
(ヒーロークラスは、id
,name
のプロパティを持ちます。)
※データはサービスから取得すべきですが、あくまでもモックなのでソースに直書きします。後の章で変更します。
const 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' }
];
ヒーロー配列の公開
AppComponent
クラスのプロパティを、先ほど定義した定数配列HEROES
を使用するため、以下の通り変更します。
export class AppComponent {
title = 'Tour of Heroes';
heroes = HEROES;
}
ngForを使用したヒーロー表示
ヒーロー名Windstorm
のみを表示する状態から、ヒーローの一覧を表示するために、app.component.ts
のhtmlテンプレート部分を修正します。
<h1>{{title}}</h1>
<ul class="heroes">
<li *ngFor="let hero of heroes">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
heroes
配列をhtmlテンプレートに表示したい場合、テンプレートで*ngFor
を使用します。
let hero of heroes
により、heroes
配列の要素数分、ローカル変数hero
に値が格納され、<li>
タグ及び内包のタグが動的に生成されます。
ngFor
のアスタリスク*
は必要です。
ngFor
については以下を参照願います。
Displaying Data(英語)
Template Syntax(英語)
これにより、ブラウザ上にヒーローの一覧がリスト表示されます。(表示されない場合更新してください。)
スタイルの追加
htmlテンプレートのスタイルは、cssのほか、@Component
デコレータ内にStyles
属性を追加することでも変更できます。htmlとcssを1対1で定義しているため、cssの内容が他のhtmlに影響を与えることはありません。
ここでは、マウスカーソルがホーバーしたときとクリックしたときに見た目を変更する定義を行っています。
cssについての一般的な知識は以下を参照ください。
いまさら聞けないCSS/スタイルシート入門(1)@IT
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;
}
`]
ヒーローを選択する
「マスター/ディテール」のUIパターンにより、マスター(ヒーローの一覧)からディテール(ヒーローの詳細)を選択できるようにします。
クリックイベント
<li>
タグに(click)
を追加し、クリックイベントをバインドします。(Javascript標準の.onClick
ではありません。)
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
これにより<li>
タグ内のクリック時に、後ほどAppComponent
クラスで定義するonSelect()
メソッドが呼ばれます。このとき選択したHero
クラスがonSelect
メソッドへの引数になります。このhero
は、ngFor
でイテレートされたheroes
配列の要素です。
クリックハンドラ(onSelect()
メソッド)の追加
選択したヒーローを取得するために、AppComponent
クラスにonSelect()
メソッドを追加します。このメソッドは、ユーザがクリックしたヒーローをAppComponent
クラスのselectedHero
プロパティとして取得します。
AppComponent
クラスを以下の通り変更します。
export class AppComponent {
title = 'Tour of Heroes';
heroes = HEROES;
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
次に、以前作成したテキストボックスに、selectedHero
プロパティの値を出力します。
onSelect()
メソッドで取得したseletedHero
プロパティをhtmlテンプレートで使用します。
<h2>{{selectedHero.name}} details!</h2>
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
</div>
未選択時のヒーロー詳細を隠す
ヒーローを選択していない場合、テキストボックスを表示しないようにします。これは、selectedHero === undefined
の場合です。
ngIf
ディレクティブによる条件判定により、それを実現します。
<h1>{{title}}</h1>
<ul class="heroes">
<li *ngFor="let hero of heroes">
<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>
ngIf
の前のアスタリスク(*)は必須です。
ngFor
やngIf
はストラクチュアルディレクティブ(構造上のディレクティブ structural directives)と呼ばれます。これはDOMの一部の構造を変更するためです。
その他ストラクチュアルディレクティブについては以下を参照してください。
Structural Directives(英語)
Template Syntax(英語)
本項により選択したヒーローの詳細情報が、ヒーローの一覧の下に表示されるようになります。
選択したヒーローのスタイル
選択したヒーローを分かりやすくするためにスタイル(見た目)を変更したい場合、先ほどstylesに追加したクラスセレクタ.selected
を使用するため、ヒーローの一覧の<li>
タグにプロパティバインディングclass.selected
を追加します。
[class.selected]="hero === selectedHero"
これはイテレータにより取得する変数hero
と現在選択中のselectedHero
が一致する場合CSSのクラスセレクタ .selected
を適用し、一致しない場合適用を解除します。
Angular2のプロパティバインディングについては以下を参照ください。
property bindings(英語)
本章により、app/app.component.tsは以下のようになります。
import { Component } from '@angular/core';
export class Hero {
id: number;
name: string;
}
const 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' }
];
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let 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): void {
this.selectedHero = hero;
}
}
本章は以上です。次章では複数のコンポーネント(部品)を使用する方法を説明します。