Miguel Moura
Miguel Moura

Reputation: 39364

Display Client Errors and then Server Errors on Angular's Reactive Form after submission

Using Angular 12 I have a simple example form with Client and Server Validation.

  1. I want to show Client Validation errors only after first Form submission;

  2. I will show Server Validation errors after Client Validation is succeeded.

    Server Validation errors are added as 'incorrect' field in Form Field errors.

  3. I am using Reactive Forms.

The form is working as expected but I'm not sure if my implementation is the best option.

Component's HTML

<form [formGroup]="form">
  <label for="email">Email</label>
  <input id="email" type="text" formControlName="email">
  
  <span class="error" *ngIf="form.get('email')?.invalid && form.get('email')?.touched">
    {{form.get('email')?.errors?.incorrect}}
    <ng-container *ngIf="form.get('email')?.errors?.required">Email required</ng-container>
    <ng-container *ngIf="form.get('email')?.errors?.email">Invalid email</ng-container>
  </span>

  <button type="submit" (click)="send()" [disabled]="submitted">Send</button>
</form>

Component's Typescript

export class Component implements OnInit {

  form: FormGroup;
  submitted: boolean;

  constructor(private service: Service) { 

    this.form = this.formBuilder.group({ 
      email: ['', [Validators.required, Validators.email]],
    });

    this.submitted = false;

  }

  send() {

    this.submitted = true;
    this.form.markAllAsTouched();

    if (this.form.valid) {

      this.service.send({email: this.form.value.email}).subscribe(
        
        (next: Payload<Response>) => { 
          console.log("SUCCESS");
        },

        (error) => {
          if (error.status === 400)
            new FormGroupErrorBuilder(this.form).setErrors(error.errors);
          this.submitted = false;
        }

      );

    } else {
      this.submitted = false;
    }

  }    

}

FormGroupErrorBuilder

This is how I am adding server errors to Angular's FormGroup:

export class FormGroupErrorBuilder {

  formGroup: FormGroup;

  constructor(formGroup: FormGroup) {
    this.formGroup = formGroup;
  }

  setErrors(errors: Error[]) {

    for (let error of errors) {

      var control = this.formGroup.get(error.name);

      if (control)
        control.setErrors({ incorrect: error.message });
        
    }

  } 

} 

Questions

  1. Is it acceptable to use this.form.markAllAsTouched(); to fire validation for all form fields on first submit?

  2. Using the condition *ngIf="form.get('email')?.invalid && form.get('email')?.touched" a good option to show the form field errors?

Any suggestion to improve the code is welcome ...

Note:

I am using submitted variable to control if the submit button is disabled or not and to change its CSS styles.

Upvotes: 0

Views: 804

Answers (1)

Eliseo
Eliseo

Reputation: 57919

Take a look to Async custom validators in the docs:

If you create your formGroup with the option {updateOption:'submit'} I think you can achieve in other way

some like: e.g. (I put the validator in the FormGroup, you can put it in the own control Email) -then you should change the "validateEmail" function.

this.form = this.formBuilder.group({ 
  email: ['', [Validators.required, Validators.email]],
},{updateOption:'submit',asyncValidator:this.validateEmail()});


  validateEmail(): AsyncValidatorFn {
    return (group: FormGroup): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
      return 
         this.service.send({email:group.value.email}).map(res=>{
            if (res.success)
                return null
            return { repeat: "name yet register" }
         })
      )))

    };
  }

NOTE: I don't see clear in your code, when you subscribe to the service. You "catch" the error, Generally an API return some like {success:true} or {error:'the email is yet!'}, but don't return a status 400 if ther'e not an error in the call -your API can response some like above but you received the response in success, not in error.

Upvotes: 0

Related Questions