FormGroup中依赖表单控件的初始化

Initialization of dependent form controls in FormGroup

有一个表单组,其中一些表单控件值依赖于其他表单控件值。在我的特定情况下,它们也相互依赖,但为简单起见,下面的示例中未显示这一点。

这是一个plunk

const fetchedFormValues = {
  timestamp: '1504116487374'
};

Observable.from(this.form.get('timestamp').valueChanges)
.filter(v => v)
.distinctUntilChanged()
.subscribe(timestamp => {
  console.log('timestamp', timestamp);
  this.form.get('date').setValue(new Date(timestamp).toISOString().substr(0, 10));
  this.form.get('time').setValue(new Date(timestamp).toISOString().substr(11, 12))
});

this.form.reset(fetchedFormValues);

初始表单值从后端获取并使用 reset 设置(这也可以稍后完成)。问题是 datetimesetValuetimestamp 更改时工作正常,但在重置时被忽略。

我尽量让它保持干燥,并避免在多个地方使用 this.form.get('date').setValue...,因为这已经在列出的可观察对象中完成了。

为什么 datetime 表单控件不受 reset 上的 timestamp 订阅的影响? reset 行为是否可以修复以接受 setValue 所做的更改?是否有任何其他方法来处理(相互)依赖的表单控件?

FormGroup 的 reset() 函数如下所示:

FormGroup.prototype.reset = function (value, options) {
    if (value === void 0) { value = {}; }
    if (options === void 0) { options = {}; }
    this._forEachChild(function (control, name) {
        control.reset(value[name], { onlySelf: true, emitEvent: options.emitEvent });
    });
    this.updateValueAndValidity(options);
    this._updatePristine(options);
    this._updateTouched(options);
};

在您的情况下 this._forEachChild 将 return 按顺序控制:

  1. 时间戳
  2. 日期
  3. 时间

当时间戳字段被设置时,它的 valueChanges 被触发并且其他两个字段被正确填充,但是随后这两个字段被 [= 再次设置为 undefined (value[name]) 17=] 次迭代。

因此,最快的解决方法是将您的字段顺序更改为:

this.form = this.fb.group({
  date: null,
  time: null,
  timestamp: null
});

这样 datetime 字段将设置为 undefined 然后在最后一次迭代中 'timestamp' 字段将被填充并更改值 datetime 个字段。

另一个(我更喜欢的)解决方案是使用 patchValue 函数,而不是 resetpatchValue 函数看起来像:

FormGroup.prototype.patchValue = function (value, options) {
    var _this = this;
    if (options === void 0) { options = {}; }
    Object.keys(value).forEach(function (name) {
        if (_this.controls[name]) {
            _this.controls[name].patchValue(value[name], { onlySelf: true, emitEvent: options.emitEvent });
        }
    });
    this.updateValueAndValidity(options);
};

它只迭代提供给函数的字段,而不是控件。因此,在您的情况下,只有 timestamp 字段将被更新并触发其 valueChanges 事件发射器。

所以而不是

this.form.reset(fetchedFormValues);

this.form.patchValue(fetchedFormValues);

使用 patchValue 您还可以控制 valueChanges 发射器的触发。如果你不想 valueChanges 被触发(这不是你的情况),你可以写:

this.form.patchValue(fetchedFormValues, {emitEvent: false});

参考代码来源:https://unpkg.com/@angular/forms@4.3.6/bundles/forms.umd.js