Angularで親子コンポーネントのデータを楽々共有!双方向バインディングのしくみとサンプルコード
Angular 2 における親子コンポーネント間の双方向バインディング
双方向バインディングのしくみ
双方向バインディングは、以下の 2 つの主要な要素によって実現されます。
- @Input() デコレータ: 親コンポーネントから子コンポーネントへ値を渡すために使用されます。
- @Output() デコレータとイベントバインディング: 子コンポーネントから親コンポーネントへ値変更を通知するために使用されます。
以下の例は、親コンポーネントと子コンポーネント間で name
プロパティを双方向バインディングする方法を示しています。
親コンポーネント (app.component.html)
<app-child-component [(name)]="name"></app-child-component>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<input type="text" [(ngModel)]="name">
<app-child-component [(name)]="name"></app-child-component>
`
})
export class AppComponent {
name = 'Angular';
}
<p>名前: {{ name }}</p>
<input type="text" [(ngModel)]="name">
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child-component',
template: `
<p>名前: {{ name }}</p>
<input type="text" [(ngModel)]="name">
`
})
export class ChildComponent {
@Input() name: string;
@Output() nameChange = new EventEmitter<string>();
onNameChange(newName: string) {
this.nameChange.emit(newName);
}
}
この例では、親コンポーネントの name
プロパティは子コンポーネントの name
プロパティに @Input()
デコレータによってバインドされています。子コンポーネント内で name
プロパティが変更されると、onNameChange
メソッドが呼び出され、nameChange
イベントが親コンポーネントに発行されます。親コンポーネントは [(name)]
バインディングを使用してこのイベントをリッスンし、name
プロパティを更新します。
双方向バインディングを使用する利点は次のとおりです。
- 簡潔なコード: 親コンポーネントと子コンポーネント間でデータをやり取りするためのコードが簡潔になります。
- リアルタイムの更新: ユーザーが値を変更すると、インターフェースがすぐに更新されます。
- 双方向のデータフロー: 親コンポーネントと子コンポーネント間でデータを双方向にやり取りできます。
双方向バインディングは、さまざまな場面で使用できます。以下にいくつかの例を示します。
- フォームデータの入力: ユーザーが入力したデータを処理するために使用できます。
- 動的なコンテンツ: ユーザーの操作に応じてコンテンツを更新するために使用できます。
- ゲーム: ゲームの状態を更新するために使用できます。
<div class="container">
<h1>双方向バインディング</h1>
<input type="text" [(ngModel)]="name">
<p>名前:{{ name }}</p>
<app-child-component [name]="name" (nameChange)="onNameChange($event)"></app-child-component>
</div>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'Angular';
onNameChange(newName: string) {
this.name = newName;
}
}
<div class="child">
<h2>子コンポーネント</h2>
<p>名前:{{ name }}</p>
<input type="text" [(ngModel)]="name">
</div>
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child-component',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent {
@Input() name: string;
@Output() nameChange = new EventEmitter<string>();
onNameChange(newName: string) {
this.nameChange.emit(newName);
}
}
.container {
width: 500px;
margin: 0 auto;
}
.child {
border: 1px solid #ccc;
padding: 10px;
}
- app.component.html: 親コンポーネントのテンプレートです。
input
要素を使用して、ユーザーが入力した名前をバインドします。p
要素を使用して、現在の名前を表示します。app-child-component
コンポーネントをname
プロパティとnameChange
イベントバインディングを使用して呼び出します。
- app.component.ts: 親コンポーネントの TypeScript コードです。
name
プロパティを定義して、ユーザーが入力した名前を格納します。onNameChange
メソッドを定義して、子コンポーネントから名前変更イベントを受信したときに呼び出されます。このメソッドは、親コンポーネントのname
プロパティを更新します。
- child.component.html: 子コンポーネントのテンプレートです。
p
要素を使用して、親コンポーネントから受け取った名前を表示します。input
要素を使用して、名前を変更します。
- child.component.ts: 子コンポーネントの TypeScript コードです。
name
プロパティを@Input()
デコレータでデコレートして、親コンポーネントから受け取る名前を定義します。nameChange
イベントバインディングを使用して、onNameChange
メソッドを呼び出します。このメソッドは、親コンポーネントに名前変更イベントを発行します。
- app.component.css: 親コンポーネントと子コンポーネントの CSS スタイルです。
- Angular CLI を使用して新しい Angular プロジェクトを作成します。
- 上記のコードを
app.component.html
、app.component.ts
、child.component.html
、child.component.ts
、app.component.css
ファイルに貼り付けます。 ng serve
コマンドを実行して、アプリケーションを起動します。
- 子コンポーネントにボタンを追加して、親コンポーネントの
name
プロパティをプログラムで変更できるようにします。 - 子コンポーネントに別のコンポーネントを埋め込んで、より複雑な UI を作成します。
- 双方向バインディングを使用して、フォームデータを処理します。
サービスは、コンポーネント間でデータを共有するための共有クラスです。双方向バインディングよりも柔軟性と制御性に優れていますが、コードが煩雑になる可能性があります。
利点:
- コンポーネント間の疎結合を促進
- テストが容易
- コードの再利用性が高い
欠点:
- コードが煩雑になる可能性がある
- デバッグが難しい場合がある
例:
// data.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DataService {
private data: string = 'Hello from DataService!';
getData() {
return this.data;
}
setData(newData: string) {
this.data = newData;
}
}
// parent.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {
name = 'Angular';
constructor(private dataService: DataService) { }
ngOnInit() {
this.name = this.dataService.getData();
}
onNameChange(newName: string) {
this.name = newName;
this.dataService.setData(newName);
}
}
// child.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { DataService } from '../data.service';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent {
@Input() name: string;
@Output() nameChange = new EventEmitter<string>();
constructor(private dataService: DataService) { }
onNameChange(newName: string) {
this.nameChange.emit(newName);
this.dataService.setData(newName);
}
}
RxJS サブジェクト
RxJS サブジェクトは、イベントストリームを管理するための Observables の一種です。双方向バインディングよりも複雑ですが、非同期データ処理に適しています。
- 非同期データ処理に適している
- 複雑なデータフローを処理できる
- 習得するのが難しい
// parent.component.ts
import { Component, OnInit } from '@angular/core';
import { Subject, from } from 'rxjs';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {
name = 'Angular';
nameSubject = new Subject<string>();
constructor() { }
ngOnInit() {
this.nameSubject.subscribe(name => this.name = name);
}
onNameChange(newName: string) {
this.nameSubject.next(newName);
}
}
// child.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Subject, from } from 'rxjs';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent {
@Input() name: string;
@Output() nameChange = new EventEmitter<string>();
nameSubject = new Subject<string>();
constructor() { }
onNameChange(newName: string) {
this.nameSubject.next(newName);
this.nameChange.emit(newName);
}
}
angular