サイトトップ

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

HTML5テクニカルノート

Angular 2入門 03: コンポーネントを分ける


Angular 2入門 02: リストを加える」の作例をさらに手直しします。といっても、見た目や動きは変わりません。コンポーネントを分け、クラスもひとつモジュールに切り出します。開発が進んで規模が大きくなったときに管理しやすくし、コンポーネントの使い回しもできるようになります。

01 オブジェクトのクラスをコンポーネントから分ける

前出「Angular 2入門 02: リストを加える」で書いたコード002のコンポーネントのモジュール(app.component)は、コンポーネントのクラス(AppComponent)だけでなく、その中で扱うオブジェクトのクラス(Heroine)も含んでいます。まず、このクラスを以下のように新たなモジュール(heroine)に分け、「app」フォルダに納めましょう。

app.component.ts


/*
export class Heroine {
	id: number;
	name: string;
}
*/

export class AppComponent {

}

heroine.ts

export class Heroine {
	id: number;
	name: string;
}

そうすると、コンポーネントのモジュール(app.component.ts)は、つぎのように別モジュール(heroine)に分けたクラス(Heroine)をimportしなければなりません。ビルドして試すと、前出「Angular 2入門 02: リストを加える」で書いたコード002と変わりなく動くはずです(図001)。

app.component.ts


import {Heroine} from './heroine';

export class AppComponent {

}

図001■項目をクリックするとその情報がリストの下に表れる

図001

02 コンポーネントをふたつのモジュールに分ける

つぎに、コンポーネント(app.component)のモジュールから新たにモジュールを切り分けましょう。リストの項目をクリックしたとき下に示される、つぎの詳細情報の部分です。

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 {

}

新たなモジュール(heroine-detail.component)は、つぎのようにクラス(HeroineDetailComponent)を定めます。デコレータの関数(@Component)には、セレクタ(selectorプロパティ)を与え、テンプレート(templateプロパティ)は前掲の親の記述を少し書き替えてあります。リストから選ばれた項目(selectedHeroine)は、親のコンポーネント(app.component)のプロパティなので、そのまま参照できないからです。前項でモジュールを分けたクラス(Heroine)は、このコンポーネントでも用いています。複数のモジュールで使うクラスは、このように分けておくと便利です。なお、importしたデコレータ関数のInputについては、あとでご説明します。

heroine-detail.component.ts

import {Component, Input} from '@angular/core';
import {Heroine} from './heroine';
@Component({
	selector: 'my-heroine-detail',
	template: `
		<div *ngIf="heroine">
			<h2>{{heroine.name}}の情報</h2>
			<div><label>番号: </label>{{heroine.id}}</div>
			<div>
				<label>名前: </label>
				<input [(ngModel)]="heroine.name" placeholder="名前">
			</div>
		</div>
	`
})
export class HeroineDetailComponent {
	@Input()
	heroine: Heroine;
}

もとのコンポーネント(app.component)のテンプレート(templateプロパティ)は、詳細情報の部分をつぎのように子コンポーネント(heroine-detail.component)のセレクタ(my-heroine-detail)に差し替えます。選ばれた項目(selectedHeroine)は、角かっこ[]で参照する子コンポーネントのプロパティ(heroine)に与えています。

app.component.ts


@Component({

	template: `

		<ul class="heroines">

		</ul>
		<my-heroine-detail [heroine]="selectedHeroine"></my-heroine-detail>
		`,

})
export class AppComponent {

}

このとき、子コンポーネント(heroine-detail.component)は、渡された値をプロパティ(heroine)が受け取る(バインディング)と示しておかなければなりません。それがデコレータ関数Inputの役割です(「Attribute Directives」参照)。

heroine-detail.component.ts

import {Component, Input} from '@angular/core';

export class HeroineDetailComponent {
	@Input()
	heroine: Heroine;
}

03 アプリケーションのモジュールに詳細情報のコンポーネントを加える

そして、新しく切り分けた詳細情報のモジュール(heroine-detail.component)は、アプリケーションのモジュール(app.module)につぎのように定めます。詳細情報のクラス(HeroineDetailComponent)をimportし、デコレータ関数(@NgModule)に渡すオブジェクトのdeclarationsプロパティに配列要素として加えます。

app.module.ts


import {HeroineDetailComponent} from './heroine-detail.component';
@NgModule({

	declarations: [
		AppComponent,
		HeroineDetailComponent
	],

})
export class AppModule {}

ビルドして確かめると、前回「Angular 2入門 02: リストを加える」で書いたコードと同じ動きになります。けれど、クラスやコンポーネントをモジュールに分けましたので、テストや拡張、使い回しなどがしやすくなりました。。書き直した4つのモジュールは、つぎのコード001にまとめてあります。併せて、Plunkerに作例のコードを掲げました。

コード001■クラスやコンポーネントを分けた4つのモジュールの定め

heroine-detail.component.ts

import {Component, Input} from '@angular/core';
import {Heroine} from './heroine';
@Component({
	selector: 'my-heroine-detail',
	template: `
		<div *ngIf="heroine">
			<h2>{{heroine.name}}の情報</h2>
			<div><label>番号: </label>{{heroine.id}}</div>
			<div>
				<label>名前: </label>
				<input [(ngModel)]="heroine.name" placeholder="名前">
			</div>
		</div>
	`
})
export class HeroineDetailComponent {
	@Input()
	heroine: Heroine;
}

app.component.ts

import {Component} from '@angular/core';
import {Heroine} from './heroine';
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>
		<my-heroine-detail [heroine]="selectedHeroine"></my-heroine-detail>
		`,
	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;
	}
}

heroine.ts

export class Heroine {
	id: number;
	name: string;
}

app.module.ts

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';
import {AppComponent} from './app.component';
import {HeroineDetailComponent} from './heroine-detail.component';
@NgModule({
	imports: [
		BrowserModule,
		FormsModule
	],
	declarations: [
		AppComponent,
		HeroineDetailComponent
	],
	bootstrap: [AppComponent]
})
export class AppModule {}


作成者: 野中文雄
作成日: 2016年11月28日


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