【超解説】Angular で子コンポーネントの @Input 変更を購読:サンプルコード付き
Angular で子コンポーネントの @Input の変更を購読する方法
そこで今回は、子コンポーネントが親コンポーネントから受け取った @Input プロパティの変更を購読する方法について、分かりやすく解説します。
ngOnChanges ライフサイクルフックの利用
最も一般的な方法は、子コンポーネントの ngOnChanges
ライフサイクルフックを使用する方法です。このフックは、コンポーネントのプロパティが変更されるたびに呼び出されます。
import { Component, Input, OnChanges } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<p>親コンポーネントから受け取った値:{{ inputValue }}</p>
`,
})
export class ChildComponent implements OnChanges {
@Input() inputValue: string;
ngOnChanges(changes: SimpleChanges) {
if (changes.inputValue) {
console.log('inputValue が変更されました! 新しい値:', this.inputValue);
// 変更された値に基づいて処理を行う
}
}
}
上記の例では、inputValue
プロパティが変更された際に ngOnChanges
フックが呼び出され、コンソールログを出力しています。また、コメント部分には、変更された値に基づいて具体的な処理を行うための記述例が示されています。
@ViewChild と ElementRef の利用
別の方法として、@ViewChild
デコレータと ElementRef
を使用する方法があります。この方法では、子コンポーネントのテンプレート内の特定の要素を参照し、その要素のプロパティやイベントを監視することで、@Input プロパティの変更を検知することができます。
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<input #inputValueElement type="text" [(ngModel)]="inputValue">
`,
})
export class ChildComponent {
@Input() inputValue: string;
@ViewChild('inputValueElement') inputValueElementRef: ElementRef;
ngAfterViewInit() {
this.inputValueElementRef.nativeElement.addEventListener('input', () => {
console.log('inputValue が変更されました! 新しい値:', this.inputValue);
// 変更された値に基づいて処理を行う
});
}
}
上記の例では、#inputValueElement
ディレクティブを使用して inputValue
プロパティと双方向バインディングを行っている <input>
要素を inputValueElementRef
に参照し、ngAfterViewInit
ライフサイクルフック内でその要素の input
イベントにリスナーを追加しています。
RxJS を利用
より高度な方法として、RxJS ライブラリを利用する方法があります。RxJS を使用すると、Observable を用いて @Input プロパティの変更を非同期的に監視することができます。
import { Component, Input, Observable, fromEvent } from '@angular/core';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-child',
template: `
<p>親コンポーネントから受け取った値:{{ inputValue }}</p>
`,
})
export class ChildComponent {
@Input() inputValue$: Observable<string>;
ngOnInit() {
this.inputValue$.pipe(
map((value) => {
console.log('inputValue が変更されました! 新しい値:', value);
// 変更された値に基づいて処理を行う
return value;
})
).subscribe();
}
}
上記の例では、inputValue$
プロパティを Observable<string>
型として定義し、親コンポーネントから発行される inputValue
の変更イベントを購読しています。map
オペレーターを使用して、変更された値をログ出力し、処理を行うようにしています。
補足
今回紹介した方法は、それぞれ異なるユースケースに適しています。
- ngOnChanges: シンプルな変更検知に適しています。
- @ViewChild と ElementRef: 特定の要素のイベントを監視する必要がある場合に適しています。
- RxJS: より複雑
ngOnChanges ライフサイクルフックの利用
// 親コンポーネント (app.component.ts)
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-child [inputValue]="inputValue"></app-child>
<button (click)="changeInputValue()">入力値を変更</button>
`,
})
export class AppComponent {
inputValue = '初期値';
changeInputValue() {
this.inputValue = '新しい値';
}
}
// 子コンポーネント (app-child.component.ts)
import { Component, Input, OnChanges } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<p>親コンポーネントから受け取った値:{{ inputValue }}</p>
`,
})
export class ChildComponent implements OnChanges {
@Input() inputValue: string;
ngOnChanges(changes: SimpleChanges) {
if (changes.inputValue) {
console.log('inputValue が変更されました! 新しい値:', this.inputValue);
// 変更された値に基づいて処理を行う
}
}
}
@ViewChild と ElementRef の利用
// 親コンポーネント (app.component.ts)
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-child></app-child>
<button (click)="changeInputValue()">入力値を変更</button>
`,
})
export class AppComponent {
changeInputValue() {
this.childComponent.inputValue = '新しい値';
}
@ViewChild(ChildComponent) childComponent: ChildComponent;
}
// 子コンポーネント (app-child.component.ts)
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<input #inputValueElement type="text" [(ngModel)]="inputValue">
`,
})
export class ChildComponent {
@Input() inputValue: string;
@ViewChild('inputValueElement') inputValueElementRef: ElementRef;
ngAfterViewInit() {
this.inputValueElementRef.nativeElement.addEventListener('input', () => {
console.log('inputValue が変更されました! 新しい値:', this.inputValue);
// 変更された値に基づいて処理を行う
});
}
}
RxJS を利用
// 親コンポーネント (app.component.ts)
import { Component, Observable, Subject } from '@angular/core';
import { fromEvent } from 'rxjs';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-root',
template: `
<app-child [inputValue$]="inputValue$"></app-child>
<button (click)="changeInputValue()">入力値を変更</button>
`,
})
export class AppComponent {
inputValue$ = new Subject<string>();
changeInputValue() {
this.inputValue$.next('新しい値');
}
}
// 子コンポーネント (app-child.component.ts)
import { Component, Input, Observable } from '@angular/core';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-child',
template: `
<p>親コンポーネントから受け取った値:{{ inputValue }}</p>
`,
})
export class ChildComponent {
@Input() inputValue$: Observable<string>;
ngOnInit() {
this.inputValue$.pipe(
map((value) => {
console.log('inputValue が変更されました! 新しい値:', value);
// 変更された値に基づいて処理を行う
return value;
})
).subscribe();
}
}
上記のサンプルコードは、それぞれの方法における基本的な実装例です。実際の使用例では、必要に応じてロジックや処理内容を拡張することができます。
注意事項
- サンプルコードはあくまでも一例であり、状況に応じて適宜修正する必要があります。
- RxJS を利用する場合は、RxJS の基本的な知識が必要となります。
このサンプルコードが、Angular における子コンポーネントの @Input の変更を
Angular で子コンポーネントの @Input の変更を購読する方法:その他の方法
Input プロパティを EventEmitter に変換する
親コンポーネントで @Input
プロパティを EventEmitter
に変換し、子コンポーネントでそのイベントを購読する方法があります。
// 親コンポーネント (app.component.ts)
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-child [inputValue]="inputValue"></app-child>
<button (click)="changeInputValue()">入力値を変更</button>
`,
})
export class AppComponent {
@Input() inputValue = '初期値';
@Output() inputValueChange = new EventEmitter<string>();
changeInputValue() {
this.inputValue = '新しい値';
this.inputValueChange.emit(this.inputValue);
}
}
// 子コンポーネント (app-child.component.ts)
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<p>親コンポーネントから受け取った値:{{ inputValue }}</p>
`,
})
export class ChildComponent {
@Input() inputValue: string;
@Output() inputValueChange = new EventEmitter<string>();
ngOnInit() {
this.inputValueChange.subscribe((newValue) => {
console.log('inputValue が変更されました! 新しい値:', newValue);
// 変更された値に基づいて処理を行う
});
}
}
この方法の利点は、子コンポーネントが親コンポーネントに対して能動的にイベントを発信できることです。
双方向バインディングを使用する
ngModel
ディレクティブなどの双方向バインディング機能を利用する方法もあります。
// 親コンポーネント (app.component.ts)
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-child [(inputValue)]="inputValue"></app-child>
<button (click)="changeInputValue()">入力値を変更</button>
`,
})
export class AppComponent {
inputValue = '初期値';
changeInputValue() {
this.inputValue = '新しい値';
}
}
// 子コンポーネント (app-child.component.ts)
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<input type="text" [(ngModel)]="inputValue">
`,
})
export class ChildComponent {
@Input() inputValue: string;
}
この方法の利点は、コードが簡潔になることです。
カスタムデコレータを使用する
@Input
デコレータの機能を拡張するカスタムデコレータを作成する方法もあります。
この方法は、より高度なユースケースや複雑なロジックが必要な場合に適しています。
サービスを使用する
親コンポーネントと子コンポーネント間で共有するデータをサービスに格納し、子コンポーネントがサービスを介してデータの変更を検知する方法もあります。
この方法は、複数のコンポーネント間でデータを共有する必要がある場合に適しています。
それぞれの方法の比較
方法 | 利点 | 欠点 | 適している状況 |
---|---|---|---|
ngOnChanges | シンプル | 複雑なロジックには不向き | 基本的な変更検知 |
@ViewChild と ElementRef | 特定の要素のイベントを監視できる | コードが煩雑になる可能性がある | 特定の要素の変更を検知する必要がある場合 |
RxJS | 非同期処理に適している | 学習曲線がやや高い | 複雑なロジックや非同期処理が必要な場合 |
EventEmitter | 子コンポーネントが能動的にイベントを発信できる | コードが冗長になる可能性がある | 子コンポーネントが親コンポーネントに対してアクションを通知する必要がある場合 |
双方向バインディング | コードが簡潔 | データフローが複雑になる可能性がある | シンプルなデータバインディング |
カスタムデコレータ | 柔軟 |
angular