重现步骤:
现在的问题是:为什么名称控件会改变其状态?hello.component 中无关控件的 updateValueAndValidity() 如何影响名称控件状态发生的情况?
从updateValueAndValidity 的文档来看,它应该只更新控件及其祖先,而不是兄弟姐妹......请帮助我理解这一点。
小智 6
您注意到的行为的原因非常微妙且没有记录。
反应式表单是使用FormControl、FormArray和FormGroup对象构建的。这些类公开了定义和执行验证器、禁用控件和更改值的能力。[formControl]如果您从未使用指令( 、[formControlName]、等)将这些控件中的任何一个绑定到表单,[formGroup]您将看到验证器的所需/预期行为。
但是,如果您有异步验证器并在这些验证器运行时将控件绑定到表单,它们将“卡在” PENDING. 底层status将在异步验证器完成后更新,但statusChanges主体不会发出事件,直到某些其他操作导致它运行。
这发生在上面使用的指令的绑定中。与 Angular 中的所有指令一样,该ngOnChanges钩子将在实例化时被调用。ngOnChanges这就是for的实现form_group_directive.ts。
this._checkFormPresent();
if (changes.hasOwnProperty('form')) {
this._updateValidators();
this._updateDomValue();
this._updateRegistrations();
this._oldForm = this.form;
}
Run Code Online (Sandbox Code Playgroud)
上面重要的一行是this._updateDomValue();. 对 的调用updateDomValue()将依次调用this.form._updateTreeValidity({emitEvent: false});。然后对 的调用_updateTreeValidity()将调用this.updateValueAndValidity({onlySelf: true, emitEvent: opts.emitEvent});。然后调用updateValueAndValidity()将执行以下操作:
this._setInitialStatus();
this._updateValue();
if (this.enabled) {
this._cancelExistingSubscription();
(this as {errors: ValidationErrors | null}).errors = this._runValidator();
(this as {status: FormControlStatus}).status = this._calculateStatus();
if (this.status === VALID || this.status === PENDING) {
this._runAsyncValidator(opts.emitEvent);
}
}
if (opts.emitEvent !== false) {
(this.valueChanges as EventEmitter<any>).emit(this.value);
(this.statusChanges as EventEmitter<FormControlStatus>).emit(this.status);
}
if (this._parent && !opts.onlySelf) {
this._parent.updateValueAndValidity(opts);
}
Run Code Online (Sandbox Code Playgroud)
这里发生了很多事情,但这里是重要的部分。首先,调用this._cancelExistingSubscription(). 这将取消任何当前正在运行的异步验证器,使控件保持PENDING状态。接下来,this._runAsyncValidator(opts.emitEvent)将调用 。这将触发异步验证器再次运行并进行重要更改。opts.emitEvent现在传递给函数的值将是可以false从之前的调用中看到的_updateTreeValidity()。由于验证器的新执行不会发出事件。验证器将完成,设置status为适当的结果,但如果没有事件,您将不会收到来自 的状态更改statusChanges。
这个示例说明了导致您所看到的问题的原因。如果在任何时候都updateValueAndValidity()可以调用emitEvent = true该statusChanges主题,就会“卡住”在 中PENDING。
现在来了解您问题的具体情况。
为什么名称控件会改变其状态?
我还没有完全调试您的示例,上面的示例是您所看到的原因之一。下面可能是另一个:
this.form.setControl(FormFields.Documents, control);进来app.component.ts。setControl()来电this._onCollectionChange()。该[formGroup]指令将其设置为this._updateDomValue()。_updateDomValue()来电this.form._updateTreeValidity({emitEvent: false});。这和我上面的例子是一样的。请注意,这emitEvent是false._updateTreeValidity()呼吁_updateTreeValidity()所有的孩子。这包括您的“名称”控件。hello.component 中无关控件的 updateValueAndValidity() 如何影响名称控件状态发生的情况?
我不认为这会影响“名称”控制状态。我看到 的控制台输出this.form.valueChanges,但没有看到 的控制台输出this.form.get(FormFields.Name).statusChanges。订阅正在valueChanges输出“名称”控件的状态,但这是因为表单的值已更改。验证器永远不会运行,因为它正在使用,valueChanges这就是它停留在PENDING. 这可能是也可能不是您想要的。异步验证器已经在输入值发生变化时触发。如果您想确保它不会进行太多 HTTP 调用,我可能建议使用timer()from rxjs 或设置updateOn为blur.
定时器示例:
nameValidatorAsync(): AsyncValidatorFn {
return (control) =>
timer(300).pipe(
switchMap(() =>
this.http.get(FAKE_URL).pipe(map(() => Math.random() > 0.5))
),
map((isValid) => (isValid ? null : { invalidName: true })),
first()
);
}
Run Code Online (Sandbox Code Playgroud)
的例子updateOn
...
[FormFields.Name]: ['', {
validators: [],
asyncValidators: [this.nameValidatorAsync()],
updateOn: 'blur'
}]
...
Run Code Online (Sandbox Code Playgroud)
关于如何解决这些问题,我没有任何很好的答案。我也不认为它们会很快得到修复。此票证提供了一些创建可观察状态的建议,可能会对您有所帮助。