首页 文章

Reactive Angular表单等待提交时异步验证器完成

提问于
浏览
4

我正在 Build 一个反应式角形,我可以,因为我可以获得内联的状态 . 否则,如果验证器是异步的并且尚未触发,则 ngSubmit 方法上的表单将处于挂起状态 . 我已经尝试注册表单 statusChange 属性的订阅,但是当我使用 markAsTouched 函数调用验证时它不会被触发 .

这是一些片段:

//initialization of form and watching for statusChanges
   ngOnInit() {
        this.ctrlForm = new FormGroup({
            'nome': new FormControl('', Validators.required),
            'razao_social': new FormControl('', [], CustomValidators.uniqueName),
            'cnpj': new FormControl('', CustomValidators.cnpj),
        });

        this.ctrlForm.statusChanges.subscribe(
            x => console.log('Observer got a next value: ' + x),
            err => console.error('Observer got an error: ' + err),
            () => console.log('Observer got a complete notification')
        )
    }
    //called on ngSubmit
    register(ctrlForm: NgForm) {
            Forms.validateAllFormFields(this.ctrlForm);
            console.log(ctrlForm.pending); 
            //above will be true if the async validator
            //CustomValidators.uniqueName was not called during form fill.
    }
    //iterates on controls and call markAsTouched for validation,
    //which doesn't fire statusChanges
    validateAllFormFields(formGroup: FormGroup) {         
          Object.keys(formGroup.controls).forEach(field => {  
              const control = formGroup.get(field);             
              if (control instanceof FormControl) {             
                control.markAsTouched({ onlySelf: true });
              } else if (control instanceof FormGroup) {        
                this.validateAllFormFields(control);            
              }
          });
      }

关于如何确保执行异步验证器的任何想法,以便我可以继续使用触发并完成所有验证器的寄存器逻辑?

2 回答

  • 4

    据我所知,Angular不会在触发 ngSubmit 之前等待异步验证器完成 . 由于验证器尚未解决,因此表单可能无效 .

    使用 Subject 作为表单提交的事件流,您可以 switchMapform.statusChangefilter 结果 .

    startWith 开始确保没有悬挂发射,如果表格在提交时有效 .

    'PENDING' 过滤等待此状态更改, take(1) 确保在挂起后的第一次发射中完成流: VALIDINVALID .

    //
    // <form (ngSubmit)="formSubmitSubject$.next()">
    
    this.formSubmitSubject$ = new Subject();
    
    this.formSubmitSubject$
      .pipe(
        tap(() => this.form.markAsDirty()),
        switchMap(() =>
          this.form.statusChanges.pipe(
            startWith(this.form.status),
            filter(status => status !== 'PENDING'),
            take(1)
          )
        ),
        filter(status => status === 'VALID')
      )
      .subscribe(validationSuccessful => this.submitForm());
    

    您还可以添加 tap ,触发将窗体设置为脏的副作用 .

  • 0

    markAsTouched 将不会激活验证,而是使用 markAsDirty ,然后您的自定义验证器将触发 . 所以改变......

    control.markAsTouched({ onlySelf: true });
    

    control.markAsDirty({ onlySelf: true });
    

    此外,如果您使用的是v 5,则可以使用可选的 updateOn: 'submit' ,在提交表单之前不会更新值(因此不会更新) . 为此,请进行以下更改:

    this.ctrlForm = new FormGroup({
      'nome': new FormControl('', Validators.required),
      'razao_social': new FormControl('', [], CustomValidators.uniqueName),
      'cnpj': new FormControl('', CustomValidators.cnpj),
    }, { updateOn: 'submit' }); // add this!
    

    有了它,这意味着您不再需要调用 this.validateAllFormFields(control) ,我假设切换一些布尔标志并检查验证或类似的东西 .

    以下是表单的示例,在提交表单后始终返回错误:

    https://stackblitz.com/edit/angular-rjnfbv?file=app/app.component.ts

相关问题