FormControl 上的 ValueChanges 在 Form.enable 时触发,即使使用 emitEvent: false
Posted
技术标签:
【中文标题】FormControl 上的 ValueChanges 在 Form.enable 时触发,即使使用 emitEvent: false【英文标题】:ValueChanges on FormControl triggers when Form.enable, even with emitEvent: false 【发布时间】:2017-09-18 09:48:03 【问题描述】:已修复:根据 GH 的说法,此问题是现在在 6.0.0 和 5.2.5 以上版本中修复的错误 问题:github.com/angular/angular/issues/12366
对于 Angular (4.x),我使用 ReactiveForms,并且我已经订阅了我的 FormControl(“输入”)上的 valueChanges,如下所示:
export class App
version:string;
formControl = new FormControl('default', []);
form = this.fb.group(
input: this.formControl,
input2: ('',[])
);
constructor(private fb: FormBuilder)
this.version = `Angular v$VERSION.full`
ngOnInit()
this.formControl.valueChanges.subscribe(value => doSomething(value));
所以现在我可以对我的 FormControl 值的变化做出反应,但我当然会从某个地方开始填充表单的值,所以我使用 form.patchValue(data)
这样做。
由于这不是用户更改,我不想对此做出反应,因此添加标志emitEvent: false
,例如:this.form.patchValue(data, emitEvent: false)
。
按预期工作。
现在我有一些逻辑,当加载表单时,我将整个表单设置为禁用,this.form.disable( emitEvent: false )
,然后在完成加载时将整个表单再次设置为启用:this.form.disable( emitEvent: false )
但我也有逻辑,根据不同的标志将 FormControl 设置为启用/禁用:this.formControl.enable( emitEvent: false);
我现在看到的问题是,当 Form
更改状态时,它会触发 FormControl.valueChanges
,即使我提供了 emitEvent: false
标志。
这是预期的行为,还是错误? 我希望在提供标志时根本不会触发任何事件?
我做了一个可以在这里测试的地方: https://plnkr.co/edit/RgyDItYtEfzlLVB6P5f3?p=preview
【问题讨论】:
看来这确实是个bug:github.com/angular/angular/issues/12366 同源,已推送修复,github issue已关闭。 【参考方案1】:disable()
和 enable()
函数 (code source):
/**
* Disables the control. This means the control will be exempt from validation checks and
* excluded from the aggregate value of any parent. Its status is `DISABLED`.
*
* If the control has children, all children will be disabled to maintain the model.
* @param ?= opts
* @return ?
*/
AbstractControl.prototype.disable = function (opts)
if (opts === void 0) opts = ;
this._status = DISABLED;
this._errors = null;
this._forEachChild(function (control) control.disable( onlySelf: true ); );
this._updateValue();
if (opts.emitEvent !== false)
this._valueChanges.emit(this._value);
this._statusChanges.emit(this._status);
this._updateAncestors(!!opts.onlySelf);
this._onDisabledChange.forEach(function (changeFn) return changeFn(true); );
;
/**
* Enables the control. This means the control will be included in validation checks and
* the aggregate value of its parent. Its status is re-calculated based on its value and
* its validators.
*
* If the control has children, all children will be enabled.
* @param ?= opts
* @return ?
*/
AbstractControl.prototype.enable = function (opts)
if (opts === void 0) opts = ;
this._status = VALID;
this._forEachChild(function (control) control.enable( onlySelf: true ); );
this.updateValueAndValidity( onlySelf: true, emitEvent: opts.emitEvent );
this._updateAncestors(!!opts.onlySelf);
this._onDisabledChange.forEach(function (changeFn) return changeFn(false); );
;
请致电:
this._updateAncestors(!!opts.onlySelf);
它调用其父级的updateValueAndValidity()
函数而没有emitEvent
标志,然后调用
this._valueChanges.emit(this._value);
这会触发表单的valueChanges
发射器,您会在控制台中看到:
Form.valueChanges: Object input2: null
这是由表单触发的,而不是由default
输入字段控制器触发的。
为了停止祖先更新,我们只需要提供额外的标志 - onlySelf: true
,它告诉只更新它自己而不是祖先。
因此,在每次调用 disable()
或 enable()
函数时,如果您不想更新祖先,请添加此标志:
disable()
this.form.disable(
onlySelf: true,
emitEvent: false
);
disableWEvent()
this.form.disable(
onlySelf: true
);
enable()
this.form.enable(
onlySelf: true,
emitEvent: false
);
enableWEvent()
this.form.enable(
onlySelf: true
);
disableCtrl()
this.formControl.disable(
onlySelf: true,
emitEvent: false
);
disableCtrlWEvent()
this.formControl.disable(
onlySelf: true
);
enableCtrl()
this.formControl.enable(
onlySelf: true,
emitEvent: false
);
enableCtrlWEvent()
this.formControl.enable(
onlySelf: true
);
这将解决叶子 formControls(没有子控件)的问题,但是这一行
this._forEachChild(function (control) control.disable( onlySelf: true ); );
将调用disable
(或enable
)函数而不传递emitEvent: false
。它看起来像 angular bug,因此,作为解决方法,我们可以覆盖这两个函数。
先导入AbstractControl
:
import ReactiveFormsModule, FormBuilder, FormControl, Validators, AbstractControl from '@angular/forms'
而不是覆盖这两个函数:
// OVERRIDE disable and enable methods
// https://github.com/angular/angular/issues/12366
// https://github.com/angular/angular/blob/c59c390cdcd825cca67a422bc8738f7cd9ad42c5/packages/forms/src/model.ts#L318
AbstractControl.prototype.disable = function (opts)
if (opts === void 0) opts = ;
this._status = 'DISABLED';
this._errors = null;
this._forEachChild(function (control)
control.disable(Object.assign(opts, onlySelf: true));
);
this._updateValue();
if (opts.emitEvent !== false)
this._valueChanges.emit(this._value);
this._statusChanges.emit(this._status);
this._updateAncestors(!!opts.onlySelf);
this._onDisabledChange.forEach(function (changeFn) return changeFn(true); );
;
AbstractControl.prototype.enable = function (opts)
if (opts === void 0) opts = ;
this._status = 'VALID';
this._forEachChild(function (control)
control.enable(Object.assign(opts, onlySelf: true));
);
this.updateValueAndValidity( onlySelf: true, emitEvent: opts.emitEvent );
this._updateAncestors(!!opts.onlySelf);
this._onDisabledChange.forEach(function (changeFn) return changeFn(false); );
;
更新plunker:https://plnkr.co/edit/IIaByz4GlBREj2X9EKvx?p=preview
【讨论】:
一个广泛而好的答案,很好的挖掘,谢谢!只要错误仍然存在,就可以将其标记为已回答。 在 6.0.0 和 5.2.5 以上版本中已修复,根据 GH 问题:github.com/angular/angular/issues/12366 是的,我看到了,谢谢。 github.com/angular/angular/blob/master/packages/forms/src/…, github.com/angular/angular/blob/master/packages/forms/src/…...以上是关于FormControl 上的 ValueChanges 在 Form.enable 时触发,即使使用 emitEvent: false的主要内容,如果未能解决你的问题,请参考以下文章
如何禁用 FormGroup 中的所有 FormControl