Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
819 views
in Technique[技术] by (71.8m points)

javascript - Reactive Angular form to wait for async validator complete on submit

I am building a reactive angular form and I'm trying to find a way to trigger all validators on submit. If the validor is a sync one, it'd be ok, as I can get the status of it inline. Otherwise, if the validator is an async one and it was not triggered yet, the form on ngSubmit method would be in pending status. I've tried to register a subscribe for the form statusChange property, but it's not triggered when I call for validation manualy with markAsTouched function.

Here's some snippets:

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

Any ideas on how can I ensure that the async validator was executed so I can continue with the register logic having all validators triggered and completed?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Angular doesn't wait for async validators to complete before firing ngSubmit. So the form may be invalid if the validators have not resolved.

Using a Subject to emit form submissions, you can switchMap to form.statusChange and filter the results.

Begin with a startWith to ensure there's no hanging emission, in the case the form is valid at the time of submission.

Filtering by PENDING waits for this status to change, and take(1) makes sure the stream is completed on the first emission after pending: VALID or INVALID.

//
// <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());

You can also add a tap that triggers the side effect of settings the form as dirty.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...