在自定义步进器中具有待处理状态的 FormGroup

FormGroup with pending status within custom stepper

我试图在最后一步结束时在注册之前强制更新自定义步进器的所有表单组的验证器,如下所示:

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(...),稍后回答。这导致表单组的状态为“待处理”。

我找到了一个 similar subject connected to this tutorial 但我真的不明白如何将它应用到我当前的问题,因为我对 Angular.

还很陌生

我在代码的另一部分发现,使用 CdkStepper that setting the value of 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 次尚未得到答复.我想知道如何等待pending结束

编辑:修改了一些问题布局并更改了标题,因为主步进器是自定义的,而不是 CdkStepper。

编辑2:所以我找到了this github thread但是在关闭之前似乎没有得到明确的答案...

编辑 3:this Whosebug post:

中找到另一个线索

我刚刚在我正在开发的应用程序中发现了这段代码,它隐藏在一个组件中:

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),
    );
}

它在订阅中与附加到它的订阅对象一起使用。 目前正在测试中。

终于找到了更新我所有表格有效性的正确方法。
以下函数允许等待挂起状态结束。

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),
    );
}

然后使用 awaitAsyncValidator().subscribe(...); 而不是 await awaitAsyncValidator();:

public async register(): Promise<void> {
    // do stuff

    // update validity of all forms from all steps
    const stepForms: FormGroup[] = this.stepFactories.map(step => step.form);
    const globalForm = new FormGroup({});
    for (let [index,group] of stepForms.entries()) {
        if (!isNullOrUndefined(group)) {
            globalForm.addControl(index.toString(), group);
        }
    }
    updateValueAndValidity(globalForm);
    awaitAsyncValidator(globalForm).subscribe(
        async () => {
            // check if any form is KO
            if (globalForm.valid || globalForm.disabled) {
                try {
                    result = await this.registrationService.register(model);
                } catch (error) {
                    // do stuff
                }

                // do stuff
            }
        });
}

感谢@Ibsn 的帮助。