HTML5テクニカルノート
Angular 2入門 02: リストを加える
- ID: FN1611006
- Technique: HTML5 / JavaScript
- Package: Angular 2.1
「Angular 2入門 01: 編集ページをつくる」の作例にさらに手を加えます。配列のデータからリストの要素を組み立てて、クリックされた項目の情報を表示します。また、テンプレートからつくられた要素に、スタイルを割り当てます。
01 配列のデータからリストをつくって表示する
本稿では、前出「Angular 2入門 01: 編集ページをつくる」で書いたコード002のコンポーネントのモジュール(app.component.ts)に手を加えます。まず、つぎのように複数のデータのオブジェクトを配列にして、新たな定数(HEROINES)に納めます(「TypeScript入門 06: メソッド引数のデフォルト値と省略および定数を定める」03「定数を定める」参照)。つぎに、コンポーネントのクラス(AppComponent)のプロパティ(heroines)に、その定数の参照を収めました。
app.component.tsconst HEROINES: Heroine[] = [ {id: 11, name: 'シータ'}, {id: 12, name: 'ナウシカ'}, {id: 13, name: 'キキ'}, {id: 14, name: '千尋'}, {id: 15, name: 'さつき'}, {id: 16, name: 'ソフィー'}, {id: 17, name: 'マーニー'}, {id: 18, name: '菜穂子'}, {id: 19, name: 'サン'}, {id: 20, name: 'フィオ'} ]; export class AppComponent { /* heroin: Heroin = { id: 1, name: 'シータ' }; */ heroines = HEROINES; }
そして、デコレータ(@Component)宣言に渡すオブジェクトのテンプレート(template
)は、つぎのように書き替えます。もとのデータひとつのテンプレートも少し直してあとで使いますので、宣言の外に出してコメントアウトしておきましょう。<li>
要素にはngFor
ディレクティブを加えました。複数のデータ(heroines)から項目(heroine)をひとつずつ取り出し、テンプレートにしたがってすべての要素を差し込みます。頭のアスタリスク*
は、ディレクティブの与えられた要素(<li>
)のノード以降がテンプレートとなることを示します。
app.component.ts@Component({ template: ` <h1>{{title}}</h1> <h2>ヒロインたち</h2> <ul class="heroines"> <li *ngFor="let heroine of heroines"> <span class="badge">{{heroine.id}}</span> {{heroine.name}} </li> </ul> ` }) /* もとのテンプレート <h2>{{heroine.name}}の情報</h2> <div><label>番号: </label>{{heroine.id}}</div> <div> <label>名前: </label> <input [(ngModel)]="heroine.name" placeholder="名前"> </div> */ export class AppComponent { }
ビルドして試すと、コンポーネントのクラス(AppComponent)に与えられたプロパティ(heroines)の配列エレメントがすべて取り出され、つぎの図001のようにテンプレートの定めにしたがって値はリスト表示されます。
図001■プロパティの配列エレメントすべての値がリストとして表示された
02 デコレータ関数でスタイルを割り当てる
コンポーネントのクラス(AppComponent)に宣言したデコレータ(@Component)は、引数のオブジェクトでHTMLのテンプレートを定めるだけでなく、スタイルを割り当てることもできます。その場合、つぎのように引数のオブジェクトにstyles
プロパティを加え、CSSは文字列で与えます。
app.component.ts@Component({ template: ` `, styles: [` .selected { background-color: #CFD8DC !important; color: white; } .heroines { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroines li { cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: 0.5em; padding: 0.3em 0; height: 1.6em; border-radius: 4px; } .heroines li.selected:hover { background-color: #BBD8DC !important; color: white; } .heroines li:hover { color: #607D8B; background-color: #DDD; left: 0.1em; } .heroines .text { position: relative; top: -3px; } .heroines .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: 0.8em; border-radius: 4px 0 0 4px; } `] }) export class AppComponent { }
ビルドして確かめると、つぎの図002のようにリストにスタイルが割り当てられました。:hover
擬似クラスも加えましたので、マウスポインタを重ねると項目のスタイルが変わります。なお、CSSファイルを別につくるのは、もう少しあとの課題になります。ここまで手を加えたコンポーネントのモジュール(app.component.ts)は、一旦以下のコード001にまとめておきます。
図002■リストの要素にスタイルが割り当てられた
コード001■複数のデータからつくったリストにスタイルを割り当てる
app.component.ts
import {Component} from '@angular/core';
export class Heroine {
id: number;
name: string;
}
const HEROINES: Heroine[] = [
{id: 11, name: 'シータ'},
{id: 12, name: 'ナウシカ'},
{id: 13, name: 'キキ'},
{id: 14, name: '千尋'},
{id: 15, name: 'さつき'},
{id: 16, name: 'ソフィー'},
{id: 17, name: 'マーニー'},
{id: 18, name: '菜穂子'},
{id: 19, name: 'サン'},
{id: 20, name: 'フィオ'}
];
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<h2>ヒロインたち</h2>
<ul class="heroines">
<li *ngFor="let heroine of heroines">
<span class="badge">{{heroine.id}}</span> {{heroine.name}}
</li>
</ul>
`,
styles: [`
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroines {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroines li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: 0.5em;
padding: 0.3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroines li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroines li:hover {
color: #607D8B;
background-color: #DDD;
left: 0.1em;
}
.heroines .text {
position: relative;
top: -3px;
}
.heroines .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 = 'ヒロイン一覧';
heroines = HEROINES;
}
03 クリックした項目の情報を表示する
リストの項目をクリックしたら、その情報を取り出して示しましょう。クリックを受け取るイベントはclick
で、つぎのようにタグの中に丸括弧()
にくくって、呼び出す関数を与えます。要素(<li>
)をクリックすると、コンポーネント(AppComponent)に定めたコールバック関数(onSelect())が呼び出されるのです。受け取る引数(heroine)は、新たに加えたクラスのプロパティ(selectedHeroine)に納めました。確認のconsole.log()
メソッドで、その値がわかります。
app.component.ts@Component({ selector: 'my-app', template: ` <h1>{{title}}</h1> <h2>ヒロインたち</h2> <ul class="heroines"> <li *ngFor="let heroine of heroines" (click)="onSelect(heroine)"> <span class="badge">{{heroine.id}}</span> {{heroine.name}} </li> </ul> `, }) export class AppComponent { selectedHeroine: Heroine; onSelect(heroine: Heroine): void { this.selectedHeroine = heroine; console.log(this.selectedHeroine); // 確認用 } }
項目をクリックすると、たとえばつぎのようにその情報をプロパティとして含むオブジェクトがコンソールに示されます。このオブジェクトがクラス(AppComponent)のプロパティ(selectedHeroine)に納められたということです。
Object {id: 12, name: "ナウシカ"}
では、はじめにコメントアウトしていたテンプレートを手直しして戻しましょう。参照する項目のオブジェクトを、つぎのように新たなプロパティ(selectedHeroine)で置き替えれば、選ばれた項目の情報が示されるはずです。
<h2>{{selectedHeroine.name}}の情報</h2> <div><label>番号: </label>{{selectedHeroine.id}}</div> <div> <label>名前: </label> <input [(ngModel)]="selectedHeroine.name" placeholder="名前"> </div> /* もとのテンプレート <h2>{{heroine.name}}の情報</h2> <div><label>番号: </label>{{heroine.id}}</div> <div> <label>名前: </label> <input [(ngModel)]="heroine.name" placeholder="名前"> </div> */
ところが、ビルドして確かめると、エラーで正しく動きません。その中には、つぎのような指摘が見つかります。理由は、項目をクリックするまでプロパティ(selectedHeroine)にオブジェクトがないからです。だったら、初期値のオブジェクトを入れておく、というのもひとつの解決でしょう。けれどここでは、値がないときは要素をつくらない、つまり表示しないことにします。
TypeError: Cannot read property 'name' of undefined
要素をつくるかどうかは、つぎのようにngIf
ディレクティブに条件を加えて決められます。その値(selectedHeroine)がfalse
と評価されれば、その要素の処理はされず、除かれます。頭のアスタリスク*
は、ディレクティブの与えられた要素(<div>
)のノード以降がテンプレートとなることを示します。つまり、プロパティ値がない(undefined
)とき、そのノードは表示されないということです。
app.component.ts@Component({ template: ` <div *ngIf="selectedHeroine"> <h2>{{selectedHeroine.name}}の情報</h2> <div><label>番号: </label>{{selectedHeroine.id}}</div> <div> <label>名前: </label> <input [(ngModel)]="selectedHeroine.name" placeholder="名前"> </div> </div> `, }) export class AppComponent { selectedHeroine: Heroine; onSelect(heroine: Heroine): void { this.selectedHeroine = heroine; } }
これでビルドして試せば、エラーなくリストは表示されます。そして、項目をクリックするとリストの下に情報が出てくるという流れです(図003)。もっとも、この作例ではすでにリストに示されている情報(idとname)しかありません。さらにデータを加えたら、それを示すこともできるということでご理解ください。
図003■項目をクリックするとその情報がリストの下に表れる
04 選んだリスト項目のスタイルを変える
今は、クリックしたリストの項目からマウスポインタを外してしまうと、どれを選んだのかわかりません。その項目のスタイルを変えて、どの情報が示されているのか明らかにしましょう。選択された項目のスタイルは、すでにつぎのようにデコレータ(@Component)宣言に渡すオブジェクトのstyles
プロパティに定めてありました。けれど、template
プロパティのHTML要素の中にはこのclass
属性(selected)が見当たりません。選ばれた要素に動的にクラスを割り当てるつもりだからです。
app.component.ts@Component({ styles: [` .selected { background-color: #CFD8DC !important; color: white; } .heroines li.selected:hover { background-color: #BBD8DC !important; color: white; } `] }) export class AppComponent { }
要素のクラスを動的に与えたり除いたりすることは、クラスバインディングと呼ばれます(「Class binding」参照)。テンプレート(template
)のタグの中で、つぎのように[]
にくくったclass
の参照にクラス名()を添えます。true
と評価される式を与えるとそのクラスが加わり、false
であれば除かれます。ここでは、その要素のオブジェクト(heroine)が、選択された項目(selectedHeroine)かどうかで判定しています。
app.component.ts@Component({ template: ` <ul class="heroines"> <li *ngFor="let heroine of heroines" [class.selected]="heroine === selectedHeroine" (click)="onSelect(heroine)"> </li> </ul> `, }) export class AppComponent { }
ビルドして確かめると、選択したリストの項目のスタイルがつぎの図004のように変わります。その項目にマウスポインタを重ねたとき(:hover
)のスタイルも別に定めてありました。ここまで書き上げたコンポーネントのモジュールは、以下のコード002にまとめます。併せて、Plunkerに作例のコードを掲げました。
図004■選択した項目のスタイルが変わる
コード002■書き上げたコンポーネントのモジュール
app.component.ts
import {Component} from '@angular/core';
export class Heroine {
id: number;
name: string;
}
const HEROINES: Heroine[] = [
{id: 11, name: 'シータ'},
{id: 12, name: 'ナウシカ'},
{id: 13, name: 'キキ'},
{id: 14, name: '千尋'},
{id: 15, name: 'さつき'},
{id: 16, name: 'ソフィー'},
{id: 17, name: 'マーニー'},
{id: 18, name: '菜穂子'},
{id: 19, name: 'サン'},
{id: 20, name: 'フィオ'}
];
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<h2>ヒロインたち</h2>
<ul class="heroines">
<li *ngFor="let heroine of heroines"
[class.selected]="heroine === selectedHeroine"
(click)="onSelect(heroine)">
<span class="badge">{{heroine.id}}</span> {{heroine.name}}
</li>
</ul>
<div *ngIf="selectedHeroine">
<h2>{{selectedHeroine.name}}の情報</h2>
<div><label>番号: </label>{{selectedHeroine.id}}</div>
<div>
<label>名前: </label>
<input [(ngModel)]="selectedHeroine.name" placeholder="名前">
</div>
</div>
`,
styles: [`
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroines {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroines li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: 0.5em;
padding: 0.3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroines li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroines li:hover {
color: #607D8B;
background-color: #DDD;
left: 0.1em;
}
.heroines .text {
position: relative;
top: -3px;
}
.heroines .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 = 'ヒロイン一覧';
heroines = HEROINES;
selectedHeroine: Heroine;
onSelect(heroine: Heroine): void {
this.selectedHeroine = heroine;
}
}
- Angular 2: とにかくAngular 2でコードを書いて動かす
- Angular 2入門 01: 編集ページをつくる
- Angular 2入門 03: コンポーネントを分ける
- Angular 2入門 04: サービスをつくる
- Angular 2入門 05: Routerを使う
- Angular 2入門 06: HTTPサービスでデータを取得・保存する
- Angular 2入門 07: HTTPサービスでデータを追加・削除する
- Angular 2入門 08: HTTPサービスが返すObservableを使ったデータの検索
作成者: 野中文雄
作成日: 2016年11月16日
Copyright © 2001-2017 Fumio Nonaka. All rights reserved.