Angularのコンポーネント間のデータ受け渡し
以前Angularの公式チュートリアルに沿ってサービスを設定した際にも少し触れましたが、改めてAngularの親コンポーネントと子コンポーネント間のデータの受け渡しについて整理します。
@Input
親から子へデータを渡す際は、子側で@Input()を利用します。
@がついているのでこれはデコレーターです。デコレータとはDI(依存性の注入)用の関数だと考えるとわかりやすいかもしれません。デコレーターを利用することで既存のクラスやプロパティに後から機能を追加することができます。
子コンポーネントで@Inputを定義すると、親コンポーネントからデータを受け取ることができます。
Inputをimportした上で、コンポーネントクラスのプロパティの先頭に@Input()を加えます。
この@Input()は後に書かれているプロパティを、親からデータを受け取れるように初期化してくれます
child.component.ts
import { Component, Input } from '@angular/core';
export class ItemDetailComponent {
@Input() public userName:string = '';
}
親コンポーネントのhtml(テンプレート)で子コンポーネントのセレクターを記述する際に、属性に@Inputをつけたプロパティ名を、値に渡したい値を設定します。固定値での運用も可能ですが、これをバインドすることで親からデータを動的に渡すことができます。
parent.component.html
...
<app-child [userName]="userNameParent"></app-child>
...
parent.component.ts
...
public userNameParent:string='ユーザー名';
...
@Inputで設定した子コンポーネントのプロパティは子コンポーネント内で変更ができますが、変更は親には伝わりませんし、親から別の値が来るとその値で上書きされますので基本的には子側で値を操作はしません。@Inputに設定したプロパティを子コンポーネントで変更したい場合は、後述する@Outputを使って親にデータの変更を依頼します。
また、親がデータを変更した際のイベントを取得したい場合は、OnChangesライフサイクルフックを使います。
child.component.ts
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
...
ngOnChanges(changes: SimpleChanges) {
console.log(changes[変数名]['previousValue']+'→'+changes[変数名]['currentValue']);
}
...
@Output
子コンポーネントから親コンポーネントにデータを渡したい場合は@Outputを使います。これも子側に記述しますが、仕組みは@Inputとは若干違い、親にデータを直接渡すのではなく、データを含んだイベントを発生させてそれを親に拾ってもらうイメージになります。
そのため@Outputを設定するプロパティはEventEmitter型でないといけません。EventEmitterはイベントを送出することができるオブジェクトです。
child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
...
@Output() userNameChagne = new EventEmitter<string>();
...
//HTML内で呼び出す
public changeName(strNewName:string):void {
userNameChagne.emit(strNewName);
}
...
親のhtml(テンプレート)に子が発生させたイベントを受け取れるようにイベントバインディングの記述をします。
parent.component.html
...
<app-child (userNameChagne)="someMethod($event)"></app-child>
...
EventEmitterとして設定したプロパティ名を()で囲い、実行させたいメソッドを設定します。
$eventには子コンポーネントのEventEmmiterのemmitの引数に渡したオブジェクト(ジェネリクスに設定したオブジェクト、ここではstring)が入ります。
@Outputは、@Inputで子コンポーネントに設定したプロパティを子側から変更したいという時にも利用します。子のイベントを受け取った親は、このプロパティに結びついている親のプロパティを受け取った値に変更します。こうすることで親と子の値の同期が取れます。
次のように@Inputと@Outputの両方を設定すると、双方向のデータ通信が実現可能です。
parent.component.html
...
<app-child [userName]="userNameParent" (nameChagneEvent)="someMethod($event)"></app-child>
...
また、単に親子間で共通の値を保持したいだけなら、[()]を使った双方向データバインディングに書き換えることができます。
ただし、双方向データバインディングにする場合は「property」という@Inputプロパティがあったら、@Outputは「propertyChange」という名前にしなければいけません。
先の例でいうとuserNameという@Inputのプロパティがあるので、@OutputのプロパティはuserNameChangeとすれば[(userName)]="userNameParent"と省略形を利用することができます。