我创建了一个简单的示例来演示我面临的一个奇怪问题。
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组件和输入组件中,我都将changedetection策略设置为onpush。如您在上面看到的,我正在app组件中创建一个表单组实例,并将其传递给子组件。现在,当我单击应用程序组件上的按钮以更改表单值时,它将更改输入字段中的值,但不更改纯文本。仅当我从两个子组件中删除“按推”更改检测时,它才起作用。即使表单组值正在更改,即使ngOnChanges也不会被调用。
这不是很奇怪。更改检测如何用于输入而不是此处的纯文本?
有人可以向我解释一下吗?而没有删除onpush更改检测的解决方法是什么。
答案 0 :(得分:3)
您可以做点更好的事情,让我知道是否可行:
当您使用反应形式时,可以使用一种非常酷的方法,称为updateValueAndValidity();
private changeControlValue(control, value: number) {
control.setValue(value);
control.updateValueAndValidity();
}
在更新添加到表单的验证器时,也可以使用此方法,例如:
this.control.setValidators([Validators.required, Validators.minLength(5)]);
control.updateValueAndValidity();
这应该可以解决问题!我认为这是对ng模型使用反应形式或形式控件的最佳尝试之一。
我不建议在您的表单上使用valueChanges,因为您很容易忘记退订并造成内存泄漏,即使您记得创建此流程也可能很乏味。
请记住,使用onPush更改检测时,Angular只会检测到三件事:
1-输入和输出。 2-用户可以执行的HTML事件,例如(单击)。 3-订阅等异步事件。
希望我能帮助您!。
答案 1 :(得分:1)
尽管不确定这是否是理想的解决方案,但我找到了解决此问题的方法。
我们可以监听表单组的值更改,然后在输入组件中触发更改检测
this.form.valueChanges.subscribe( () => {
this.cdr.detectChanges()
});
通过这种方式,它也可以更新标签值以及输入。
这是解决方案:
https://stackblitz.com/edit/angular-change-detection-form-group-value-change-issue-resolved
我不确定这是否是Angular的错误,但很高兴我找到了一些解决方法:)
答案 2 :(得分:0)
只有变量的内存地址发生变化,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)
}
}
答案 3 :(得分:0)
在运行自动更改检测(cd)期间,Angular会注意到FormGroup
的值已更新,并且分别更新了UI。
通过将更改检测策略设置为OnPush
,可以停用Angular运行的自动更改检测。在这种情况下,仅内部值会被更新,但是Angular不会检查UI的值。
也许这是对Angular的ChangeDetection的过于肤浅的解释,所以我建议使用blog,以更深入地了解该主题。
ngOnChanges
不会触发,因为您的FormGroup
的对象引用(内存地址)尚未更改。仅当您将primitives传递到组件的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();
}
我希望这是可以理解的;-)
答案 4 :(得分:0)
只需使用 valueChanges
管道订阅模板中控件的 async
属性,无需手动触发更改检测并订阅组件中的 valueChanges
。
<input [formControl]="control"/>
<p>{{control.valueChanges | async}}</p>