Angularで親コンポーネントから子コンポーネントに値を引き渡すには?(@Input):Angular TIPS
@Inputデコレーターを使って、親コンポーネントから子コンポーネントに情報を引き渡すことで、マスター/詳細画面を作成する方法を解説。
【対応バージョン】
Angular 5以降。v5時点で執筆し、v6で動作を確認しました。
Angularでは、コンポーネントを組み合わせることで、ページを組み立てていくのが基本です。本連載のこれまでの例では、ページにコンポーネントを1つだけ配置する例を見てきましたが、一般的には、1つの画面に複数のコンポーネントを配置することもできますし、コンポーネント同士を入れ子にすることも可能です。複雑なページでは、(1つのコンポーネントに何でも機能を詰め込むのではなく)機能単位にコンポーネントを分離することで、部品としてのシンプルさを維持できます。
本稿では、そのようなコンポーネント連携の方法について解説します。扱うサンプルは、以下の図の通りです。ページ上部に表示された記事タイトルをクリックすると、詳細情報を下部に表示します。
サンプルを構成するファイルは、以下の通りです。
ファイル名 | 概要 |
---|---|
app.module.ts | メインモジュール |
app.component.ts app.component.html app.component.css |
記事一覧を表示するメインコンポーネント |
details-article.component.ts details-article.component.html details-article.component.css |
記事詳細を表示するためのDetailsArticleComponent |
article.ts | コンポーネント間で受け渡しする記事情報 |
本サンプルを構成する主なファイル |
ページ全体はAppComponentが担当し、その配下でDetailsArticleComponentコンポーネントが記事情報の詳細表示を担うという役割分担です。記事タイトルをクリックすることで、AppComponent⇒DetailsArticleComponentに記事情報(Articleオブジェクト)を引き渡します。
以上の関係を念頭に、ここからは具体的な実装を見ていきます。
記事情報を表すArticleクラス
コンポーネント間で受け渡しする記事情報は、Articleクラスとして準備しておきます。記事に関わる属性をプロパティとして定義しただけのクラスなので、特段に説明すべき点はありません。コンポーネント側から参照できるよう、exportキーワードを付与するのを忘れないようにしてください。
export class Article {
url: string; // URL
title: string; // 記事タイトル
author: string; // 著者名
view: number; // ページビュー
updated: string; // 更新日
}
記事一覧を作成するメインコンポーネント
メインコンポーネント(AppComponent)では、記事一覧を生成すると共に、DetailsArticleComponentコンポーネントを呼び出します。
import { Component } from '@angular/core';
import { Article } from './article';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedItem: Article; // リストで選択された記事
// 記事一覧を準備
articles: Article[] = [
{
url: 'http://www.atmarkit.co.jp/ait/series/9383/',
title: 'Angular TIPS',
author: '山田祥寛',
view: 110092,
updated: '2018/06/13'
},
// …… 中略 ……
];
// [2]リンククリック時に記事情報をselectedItemプロパティに退避
onclick(article: Article) {
this.selectedItem = article;
}
}
<ul>
<!--[1]記事情報を一覧表示-->
<li *ngFor="let article of articles">
<a href="#" (click)="onclick(article)">{{article.title}}</a>
</li>
</ul>
<!--[3]子コンポーネントの呼び出し-->
<my-details-article [source]="selectedItem"></my-details-article>
記事情報を一覧に整形するのはngForディレクティブの役割です([1])。配下のアンカータグでは、「(click)="onclick(article)」で、イベントハンドラーを設定している点に注目です。これで、記事がクリックされた「その記事情報(変数article)をonclickメソッドに渡しなさい」という意味になります。onclickメソッド([2])では、後でDetailsArticleComponentコンポーネントに引き渡すために、渡された記事情報をselectedItemプロパティに設定しておきます。
そして、[3]がDetailsArticleComponentコンポーネントの呼び出しです。「[source]="selectedItem"」はProperty Bindingの構文で、DetailsArticleComponentコンポーネントのsourceプロパティに対して、メインコンポーネントのselectedItemプロパティをバインドします。
以上をまとめると、記事リンクをクリックすると、選択された記事情報をselectedItemプロパティ経由でDetailsArticleComponentに引き渡す、という流れが出来上がるわけです。
記事詳細を表示する子コンポーネント
最後に、メインコンポーネントから呼び出され、記事の詳細情報を表示するためのDetailsComponentコンポーネントです(なお、冒頭の表にも示していますが、実際に動作させるためには、内容は空でよいのでdetails-article.component.cssファイルも必要になります)。
import { Component, Input, OnInit } from '@angular/core';
import { Article } from './article';
@Component({
selector: 'my-details-article',
templateUrl: './details-article.component.html',
styleUrls: ['./details-article.component.css']
})
export class DetailsArticleComponent {
@Input() source: Article;
}
<div id="details">
<ul *ngIf="source">
<li>タイトル:<a [href]="source.url">{{source.title}}</a></li>
<li>著者:{{source.author}}</li>
<li>ページビュー:{{source.view}}</li>
<li>更新日:{{source.updated.toLocaleDateString()}}</li>
</ul>
</div>
ここでポイントとなるのは、太字の部分だけです。コンポーネントの属性として値を受け取るのは@Inputデコレーターの役割です。
[構文]属性の定義(@Inputデコレーター)
@Input() property: type
- property:プロパティ名
- type:データ型
プロパティ定義を@Inputで修飾することで、(この例であれば)sourceプロパティが同名のsource属性として公開されます。
後は、テンプレート側でもsourceプロパティ経由で記事情報(Articleオブジェクトのプロパティ)にアクセスできます。
コンポーネントをモジュールに登録する
コンポーネントを追加した場合には、アプリ側できちんと認識できるように、モジュール側にも追加しておきましょう*1。
*1 Angular CLIのng generateコマンドを利用した場合には、自動的に登録されます。
import { DetailsArticleComponent } from './details-article.component';
@NgModule({
declarations: [
AppComponent,
DetailsArticleComponent,
],
// …… 中略 ……
})
export class AppModule { }
以上を理解できたら、サンプルを実行してみましょう。冒頭の図のように、記事リンクをクリックすると、ページ下部に対応する記事の詳細が表示されます。
Copyright© Digital Advantage Corp. All Rights Reserved.