FormControl的ValueChanges在Form.enable时触发,即使emitEvent为false。

29
“修复:此问题是一个漏洞,在6.0.0及以上版本和5.2.5中已经得到修复,详见GH问题:github.com/angular/angular/issues/12366。”
“在Angular(4.x)中,我使用ReactiveForms,并订阅了我的FormControl('input')上的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 更改状态时,即使我提供了 emitEvent: false 标志,它也会触发 FormControl.valueChanges
这是期望的行为还是一个错误?我希望在提供标志时根本不触发任何事件?
我已经创建了一个 plunk 以进行测试: https://plnkr.co/edit/RgyDItYtEfzlLVB6P5f3?p=preview

1
看起来这确实是一个bug: https://github.com/angular/angular/issues/12366 - Bulan
1
根据同一来源,修复已经推送并且 Github 问题已关闭。 - Pac0
1个回答

25

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


一份详尽而优秀的答案,挖掘得很好,谢谢!由于该错误仍然存在,因此将其标记为已回答,并将此作为解决方法。 - Bulan
根据 GH 问题:https://github.com/angular/angular/issues/12366,此问题已在6.0.0及以上版本和5.2.5中得到解决。 - Bulan
是的,我看到了,谢谢。https://github.com/angular/angular/blob/master/packages/forms/src/model.ts#L465,https://github.com/angular/angular/blob/master/packages/forms/src/model.ts#L496... - Andriy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接