繁体   English   中英

表单组值更改时不会触发更改检测

[英]Change detection does not trigger when the formgroup values change

我创建了一个简单示例来演示我面临的一个奇怪问题。

Stackblitz -https://stackblitz.com/edit/angular-change-detection-form-group

我有三个组件,这里是它们:

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 组件和 input 组件中,我都将 changedetection 策略设置为 onpush。 正如您在上面看到的,我在应用程序组件中创建了一个表单组实例并将其传递给子组件。 现在,当我单击应用程序组件上的按钮来更改表单值时,它会更改输入字段中的值,但不会更改纯文本。 仅当我从两个子组件中删除推送更改检测时它才有效。 即使 formgroup 值发生变化,也不会调用 ngOnChanges。

在此处输入图像描述

是不是很奇怪。 更改检测如何针对输入而不是此处的纯文本工作?

有人可以向我解释一下吗? 在不删除 onpush 更改检测的情况下,它的解决方法是什么。

我找到了解决此问题的方法,但我不确定这是否是理想的解决方案。

我们可以监听表单组值的变化,然后在输入组件中触发变化检测

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

通过这种方式,它会连同输入一起更新标签值。

在此处输入图像描述

这是解决方案:

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

我不确定这是否是 Angular 的错误,但很高兴我找到了一些解决方法:)

你可以做得更好,:让我知道这是否有效:

当你使用响应式表单时,你可以使用一个非常酷的方法,叫做updateValueAndValidity()

private changeControlValue(control, value: number) {
    control.setValue(value);
    control.updateValueAndValidity();
  }

您也可以在更新添加到表单的验证器时使用此方法,例如:

this.control.setValidators([Validators.required, Validators.minLength(5)]);
control.updateValueAndValidity();

这应该可以解决问题。 我认为这是针对 ng-model 使用反应式表单或表单控件的最佳冒险之一。

我不建议在您的表单中使用 valueChanges,因为您很容易忘记取消订阅并造成内存泄漏,即使您记得创建此流程可能很乏味。

请记住,当使用 onPush 变化检测时,只有三件事会被 Angular 检测为变化:

1- 输入和输出。 2- 用户可以执行的 Html 事件,例如(单击)。 3- 订阅等异步事件。

我希望我帮助了你..

只需使用async管道在模板中订阅控件的valueChanges属性,避免手动触发更改检测和订阅组件中的valueChanges的需要。

<input [formControl]="control"/>
<p>{{control.valueChanges | async}}</p>

如果变量的内存地址发生变化,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)
  }
}

在自动更改检测 (cd) 运行期间,Angular 会注意到您的FormGroup的值已更新并分别更新 UI。

通过将更改检测策略设置为OnPush ,您可以停用 Angular 运行的自动更改检测。 在这种情况下,只会更新内部值,但 Angular 不会检查 UI 的值。

也许这是对 Angular 的 ChangeDetection 的过于肤浅的解释,所以我推荐这篇博客来更深入地研究这个主题。

ngOnChanges不会触发,因为你的FormGroup的对象引用(内存地址)没有改变。 ngOnChanges仅在您将原传递给组件的@Input时才会触发。 这也会触发 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(); 
}

我希望这是可以理解的;-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM