web-dev-qa-db-ja.com

フォームグループの値が変更されたときに変更検出がトリガーされない

私が直面している奇妙な問題を示す簡単な例を作成しました。

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の値が変化していても呼び出されません。

enter image description here

変じゃないですか。ここではプレーンテキストではなく、入力に対して変更検出がどのように機能しますか?

誰かが私にこれを説明してもらえますか?そして、オンプッシュ変更検出を削除せずに回避策を教えてください。

6
Ansuman

これが理想的な解決策かどうかはわかりませんが、この問題の回避策を見つけました。

フォームグループの値の変更をリッスンし、入力コンポーネントで変更検出をトリガーできます。

this.form.valueChanges.subscribe( () => {
  this.cdr.detectChanges()
});

このようにして、入力とともにラベル値も更新します。

enter image description here

解決策は次のとおりです。

https://stackblitz.com/edit/angular-change-detection-form-group-value-change-issue-resolved

Angularからのバグかどうかはわかりませんが、回避策を見つけられたことに満足しています:)

5
Ansuman

自動変更検出(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(); 
}
_

これが理解できることを望みます;-)

0
Batajus

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)
  }
}
0
user1779362