Angularで1:Nのコンポーネント間でデータ連携を行う
はじめに
AngularのComponent間のデータ連携はいくつか手法があります。
1:1のComponent間であれば @Input
@Output
で事足りていましたが、1つのComponentで入力されたデータを加工し、複数のComponentで拾い、表示しようと考えた時に少し冗長なコードになってしまいます。
困った時の公式ドキュメント:コンポーネントの相互作用に Service
を利用した例が書かれていたのでまとめてみます。
設計概要
今回はappComponent
のFormから入力したデータをvalueChangeで監視しdataService
に渡します。
dataService
では共通のデータ処理としてkey3にtrueフラグを立てnext()
で各Componentへデータを渡します。
個別の表示を目的とした処理として片方のComponentでは足し算、もう片方では引き算を行い、key3がtrueであれば表示します。
Angularで実装
まずはデータ型を規定します。
export class Data { key1: number; key2: number; key3: boolean; result: number; }
次にデータの入力箇所を実装します。
formGroupとしてkey1/key2のformを規定しそれとなくバリデーションしておきます。
dataFormGroup = this.formBuilder.group({ key1: [0, Validators.required], key2: [0, Validators.required], });
Templateは以下の感じです。
<form [formGroup]="dataFormGroup"> key1: <input placeholder="key1" type="number" formControlName="key1" min="0"><br> key2: <input placeholder="key2" type="number" formControlName="key2" min="0"> </form>
Templateから入力された値をvalueChanges
で監視しsubscribe()
します。
その結果をServiceへ渡します。
ngOnInit() { this.dataFormGroup.valueChanges.subscribe( (value: Data) => { this.dataService.recieve(value); } ); }
さて、いよいよ本題ですが
任意のタイミングでデータのやりとりが可能なSubject
というクラスを利用します。
今回はobserverをdataSubject、ObservableをdataStateとして宣言しています。
また、共通の処理として各Componentにnext
する前にdata.key3 = true
とフラグを立てておきます。
export class DataService { public dataSubject = new Subject<Data>(); public dataState = this.dataSubject.asObservable(); constructor() { } recieve(data: Data) { data.key3 = true this.dataSubject.next(data); } }
あとは各Componentでsubscribe
し個別の設定として足し算処理を行います。
export class Receive01Component implements OnInit { data = {} as Data; constructor( private dataService: DataService, ) { } ngOnInit() { this.dataService.dataState.subscribe( (data: Data) => { this.addition(data); } ) } addition(data: Data) { this.data.key3 = data.key3; this.data.result = data.key1 + data.key2; } }
結果をTemplateに表示します。
*ngIf="data.key3
としてフラグをみて表示可否を切り替えることができます。
<div>receive01:</div> <div *ngIf="data.key3; else elseBlock">{{data.result}}</div> <ng-template #elseBlock>0</ng-template>
stackblitzを貼っておきます。
まとめ
Angularで1:Nのコンポーネント間でデータ連携を行う方法をまとめました。
このくらいのデータ量と処理であれば@Input
@Output
でやった方が簡単ですが、共通処理が大きくなればなるほどコード量が少なくなるはずです。
参考にさせていただいたページ
わからなかったこと
1度の入力でvalueChanges
が2回発火してしまう。
関係してそうなissueは見つけたが解決できず。