【发布时间】:2021-09-30 11:57:40
【问题描述】:
我正在尝试在最后一步结束时的注册之前强制更新自定义步进器的所有表单组的验证器,如下所示:
public async register(): Promise<void> {
// do stuff
// update validity of all forms from all steps
const stepForms = this.stepFactories.map(step => step.form);
stepForms.forEach(group => {
if (group !== null && group !== undefined) {
this.updateValueAndValidity(group);
}
});
// check if any form is KO
if (stepForms.every(form => form.valid || form.disabled)) {
try {
result = await this.registrationService.register(model);
} catch (error) {
// do stuff
}
// do stuff
}
}
其中一个表单组包含另一个带有异步验证器的表单组,其中包含对服务器的 2 次调用,其工作原理如下:
export function fooAsyncValidator(fooService: FooService, fooParams: FooParams): AsyncValidatorFn {
return (group: AbstractControl): Observable<ValidationErrors | null> => {
const fooControlVal1 = group.get('FooVal1').value;
const fooControlVal2 = group.get('FooVal2').value;
// do stuff
let availableFoo: number;
// first call to server, which is answered according to chrome debugger
return fooService.getAvailableFoo(fooParams).pipe(
flatMap((avFoo: number) => {
availableFoo = avFoo;
// second call to server which is answered after I get to check the validity
return fooService.getConsumedFoo(fooParam, availableFoo);
}),
map((consumedFoo: number) => {
// do stuff with available Foo and consumedFoo
if (/* stuff */) {
return { availableFooError: true };
}
return null;
}),
);
}
}
根据 chrome 调试器,当我在此表单组上应用 updateValueAndValidity 时,第一个呼叫 fooService.getAvailableFoo(...) 会按时应答,但第二个呼叫 fooService.getConsumedFoo(...) 稍后会应答。这导致表单组的状态为“PENDING”。
我找到了一个连接到this tutorial 的similar subject,但我真的不明白如何将它应用于我当前的问题,因为我对 Angular 还很陌生。
我在使用CdkStepper 的代码的另一部分中发现,将selectedIndex 的值设置为具有相同表单组的步骤数将正确更新表单的有效性。
作为参考,这里是 updateValueAndValidity 自定义方法的代码,它递归地对每个 fromGroup 和 formControl 应用 updateValueAndValidity:
private updateValueAndValidity(group: FormGroup): void {
group.updateValueAndValidity({ emitEvent: false });
for (const field in group.controls) { // 'field' is a string
const control = group.get(field);
if (control) {
if (control instanceof FormGroup) {
updateValueAndValidity(control);
} else {
if (control.disabled) {
control.enable({ emitEvent: false });
control.updateValueAndValidity({ emitEvent: false });
if (!control['invalidActived'] || !control.invalid) {
control.disable({ emitEvent: false });
}
} else {
control.updateValueAndValidity({ emitEvent: false });
}
}
}
}
总结:当我通过 2 次对服务器的调用来更新表单组的值和有效性时,状态切换为待处理,因为 2 尚未得到答复。我想知道如何等待等待结束。
编辑:重新设计了一些问题布局并更改了标题,因为主步进器是自定义步进器,而不是 CdkStepper。
编辑2:所以我找到了this github thread,但在关闭之前似乎没有得到明确的答案......
编辑 3: 在 this stackoverflow post 中找到另一个潜在客户:
- this answer 似乎很有趣,但我需要在订阅中包含所有四个表单组 => 还不知道该怎么做
- this github request 的最后一个答案提示作者描述了他的自定义解决方案
我刚刚在我正在开发的应用程序中发现了这段代码,隐藏在一个组件中:
export function awaitAsyncValidator(form: FormGroup): Observable<number> {
return timer(0, 500).pipe(
tap((timerCount: number) => {
if (timerCount > 200) {
throw new Error('The form is blocked in "pending" status.');
}
}),
filter(() => !form.pending),
take(1),
);
}
它与订阅中附加的订阅对象一起使用。 目前正在测试中。
【问题讨论】:
-
只是猜测:尝试在您的
fooAsyncValidator中使用concatMap而不是flatMap -
我知道使用
concatMap会更好,因为flatMap已被弃用。但是,在测试此解决方案后它并没有改变任何东西 =/ 遗憾的是我的工作目前仅限于错误修复而不是重构代码。还是谢谢你。 -
至于您的编辑 2,该标题的“确定”答案似乎在此 comment 中:确保您的可观察对象始终完成。
-
@Ibsn,我试图理解这个评论以及如何将它应用到我的案例中,但似乎还不是这样=(但是我在 Eddit 中找到了一些其他线索3 现在。
标签: angular forms validation asynchronous