私が直面している奇妙な問題を示す簡単な例を作成しました。
Stackblitz- https://stackblitz.com/edit/angular-change-detection-form-group
私は3つのコンポーネントを持っています、そしてそれらはここにあります:
1-アプリコンポーネント
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'my-app',
template: `<hello [form]="form"></hello>
<hr />
<button (click)="changeFormValue()">Change Form Value</button>`,
styleUrls: ['./app.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
name = 'Angular';
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
name: new FormControl('ABC'),
age: new FormControl('24')
});
}
changeFormValue() {
this.form.setValue({
name: 'XYZ',
age: 35
})
}
}
2-こんにちはコンポーネント
import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'hello',
template: `<form [formGroup]="form">
<app-input [form]="form"></app-input>
</form>`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloComponent implements OnChanges {
@Input() form: FormGroup;
ngOnChanges(changes) {
console.log(changes)
}
}
3-入力コンポーネント
import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-input',
template: `Name : <input type="text" [formControl]="nameFormcontrol" /> {{nameFormcontrol.value}} <br /><br />
Age : <input type="text" [formControl]="ageFormcontrol" /> {{ageFormcontrol.value}}`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
@Input() form: FormGroup;
nameFormcontrol;
ageFormcontrol;
ngOnInit() {
this.nameFormcontrol = this.form.get('name');
this.ageFormcontrol = this.form.get('age');
}
ngOnChanges(changes) {
console.log(changes)
}
}
Helloコンポーネントと入力コンポーネントの両方で、変更検出戦略をonpushに設定しました。上記のように、アプリコンポーネントでフォームグループインスタンスを作成し、それを子コンポーネントに渡します。ここで、フォームの値を変更するためにアプリコンポーネントのボタンをクリックすると、入力フィールドの値は変更されますが、プレーンテキストは変更されません。両方の子コンポーネントからon Push変更検出を削除した場合にのみ機能します。 ngOnChangesでさえ、formgroupの値が変化していても呼び出されません。
変じゃないですか。ここではプレーンテキストではなく、入力に対して変更検出がどのように機能しますか?
誰かが私にこれを説明してもらえますか?そして、オンプッシュ変更検出を削除せずに回避策を教えてください。
これが理想的な解決策かどうかはわかりませんが、この問題の回避策を見つけました。
フォームグループの値の変更をリッスンし、入力コンポーネントで変更検出をトリガーできます。
this.form.valueChanges.subscribe( () => {
this.cdr.detectChanges()
});
このようにして、入力とともにラベル値も更新します。
解決策は次のとおりです。
https://stackblitz.com/edit/angular-change-detection-form-group-value-change-issue-resolved
Angularからのバグかどうかはわかりませんが、回避策を見つけられたことに満足しています:)
自動変更検出(cd)の実行中にAngular=を実行すると、FormGroup
の値がそれぞれ更新され、UIが更新されていることがわかります。
変更検出戦略をOnPush
に設定すると、Angularによって実行される自動変更検出が無効になります。この場合、内部値のみが更新されますが、AngularはUIの値をチェックしません。
これはAngularのChangeDetectionの表面的な説明かもしれないので、このトピックをより深く調べるために blog をお勧めします。
ngOnChanges
のオブジェクト参照(メモリアドレス)は変更されていないため、FormGroup
はトリガーされません。 ngOnChanges
は、コンポーネントの_@Input
_に primitives を渡す場合にのみトリガーされます。また、これにより、Angularによって実行される新しい変更検出がトリガーされます。
UIを更新するには、親コンポーネントに ChangeDetectorRef
を挿入し、detectChanges()
を呼び出して、変更検出を手動でトリガーできます。
これは次のようになります。
_constructor(private cd: ChangeDetectorRef) {}
...
changeFormValue() {
this.form.setValue({
name: 'XYZ',
age: 35
});
// This will trigger the change detection and your input field are updated
this.cd.detectChanges();
}
_
これが理解できることを望みます;-)
Angularは、変数のメモリアドレスが変更された場合にのみ変更を検出します。値を設定してもメモリアドレスは変更されないため、ngOnChangesにはヒットしません。
配列についても同じことが言えます。シンプルなプッシュはngOnChangesにヒットせず、メモリアドレスを=で新しい配列に変更する必要があります。
これを試してください:
import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-input',
template: `
<div formGroupName="form">
Name : <input type="text" formControlName="name" /> {{form.value.name}} <br /><br />
Age : <input type="text" formControlName="age" /> {{form.value.age}}
</div>`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
@Input() form: FormGroup;
ngOnInit() {
}
ngOnChanges(changes) {
console.log(changes)
}
}