サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

Angular 4入門 02: リストを加える


Angular 4入門 01: 編集ページをつくる」の作例にさらに手を加えます。配列のデータからリストの要素を組み立てて、クリックされた項目の情報を表示します。また、テンプレートからつくられた要素に、スタイルを割り当てます。

Angular Version 5については「Angular 5入門 03: データのリストを表示する」をお読みください。

01 配列のデータからリストをつくって表示する

本稿では、前出「Angular 4入門 01: 編集ページをつくる」で書いたコード002のコンポーネントのモジュール(app.component.ts)に手を加えます。まず、つぎのようにクラス(Heroine)の複数のオブジェクトを配列にして、新たな定数(HEROINES)に納めます(「TypeScript入門 06: メソッド引数のデフォルト値と省略および定数を定める」03「定数を定める」参照)。つぎに、コンポーネントのクラス(AppComponent)のプロパティ(heroines)に、その定数の参照を収めました。プロパティを型づけしなくても、TypeScriptでは代入により型が推論されます(「TypeScript入門 08: 型の互換性」03「型推論」参照)。

app.component.ts


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: 'フィオ'}
];

export class AppComponent {

	/* heroin: Heroin = {
		id: 1,
		name: 'シータ'
	}; */
	heroines = HEROINES;
}

そして、デコレータ(@Component)宣言に渡すオブジェクトのテンプレート(template)は、つぎのように書き替えます。もとのデータひとつのテンプレートも少し手直ししてあとで使いますので、宣言の外に出してコメントアウトしておきましょう。<li>要素にはngForディレクティブを加えました。複数のデータ(heroines)から項目をひとつずつlet宣言した変数(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■プロパティの配列エレメントすべての値がリストとして表示された

図001

02 デコレータ関数でスタイルを割り当てる

コンポーネントのクラス(AppComponent)に宣言したデコレータ(@Component)は、引数のオブジェクトでHTMLのテンプレートを定めるだけでなく、スタイルを割り当てることもできます。その場合、つぎのように引数のオブジェクトにstylesプロパティを加え、CSSは配列に文字列で加えます。プロパティ値が配列なのは、複数のスタイルが定められるようにするためです。コンポーネントに与えたスタイルは、そのコンボーネントの中だけで使われます。ほかのHTMLに影響は与えません。

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■リストの要素にスタイルが割り当てられた

図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: 0.8em;
			border-radius: 4px 0 0 4px;
		}
	`]
})
export class AppComponent {
	title = 'ヒロイン一覧';
	heroines = HEROINES;
}

03 クリックした項目の情報を表示する

リストの項目をクリックしたら、その情報を取り出して示しましょう。要素のクリックを受け取るイベントはclickで、つぎのようにタグの中に丸括弧()にくくって、実行するコードを書き加えます(「Event binding ( (event) )」参照)。これで、要素(<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ディレクティブに条件を加えて決められます(「Structural Directives」参照)。その値(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■項目をクリックするとその情報がリストの下に表れる

図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の参照にクラス名(selected)を添えます。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■選択した項目のスタイルが変わる

図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: 0.8em;
			border-radius: 4px 0 0 4px;
		}
	`]
})
export class AppComponent {
	title = 'ヒロイン一覧';
	heroines = HEROINES;
	selectedHeroine: Heroine;
	onSelect(heroine: Heroine): void {
		this.selectedHeroine = heroine;
	}
}


作成者: 野中文雄
作成日: 2017年6月3日


Copyright © 2001-2017 Fumio Nonaka.  All rights reserved.