【问题标题】:FormGroup with pending status within custom stepper自定义步进器中具有待处理状态的 FormGroup
【发布时间】: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 tutorialsimilar 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


【解决方案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 的帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    • 2017-06-16
    • 2020-05-25
    相关资源
    最近更新 更多