Vivek Kumar
Vivek Kumar

Reputation: 5050

Showing error after form submit in angular reactive form

I have created a reactive form and using angular-material form control.

On form submit I am calling API and that API is returning error because one of the formControl value is invalid

For example Website already registered.

Now, I want to show this error msg in , But the error is not showing.

  <mat-form-field class="full-width website"
    [appearance]="matAppearance">
    <mat-label>Website URL</mat-label>
    <input matInput
      placeholder="Website URL"
      name="website"
      formControlName="website">
    <mat-error *ngIf="configurationForm.get('website').hasError('required')">
      <strong>(required)</strong>
    </mat-error>
    <mat-error *ngIf="configurationForm.get('website').hasError('pattern')">
      Invalid URL
    </mat-error>
    <mat-error *ngIf="websiteErrMsg">{{websiteErrMsg}}</mat-error>
  </mat-form-field>

  public submitForm() {
      this.testService.register().subscribe(
        sucRes => {
          console.log('done);
        },
        errRes => {
          if (errRes.error === 'Website is already registered') {
              this.websiteErrMsg = 'Website Already Registered!';
          }
        }
      );
  }

Question 1: What mistake I am doing?

Edit: I have tried changing mat-error or div, then is working. Now wanted to know why it is not working with mat-error

Upvotes: 0

Views: 3236

Answers (1)

Eliseo
Eliseo

Reputation: 58039

It's not exact, but I think taht using an async validator (see the docs) you can resolve your problem. The problem with asyncValidatros are is the perfomance. If you not use updateOn 'blur' or 'submit' Angular makes a call each time you change the form.

Imagine you has a service that return an observable of true or false like

@Injectable({ providedIn: 'root' })
export class ApiService {
  getWebName(webName: string): Observable<boolean> {
    const isTaken = webName=="Vivek Kumar";
    return of(isTaken).pipe(delay(1000));
  }
}

You need create the formGroup using the constructor of FormGroup and FormControl to indicate when you make the valitation. It's not possible using FormBuilder

this.testForm = new FormGroup(
  {
    name: new FormControl("Vivek Kumar", {
      asyncValidators: this.checkIfNotRegister(),
      validators: Validators.required, 
      updateOn: 'blur'
    }),
    age: new FormControl(30, {
       validators:Validators.required,
       updateOn: 'blur'
    })
  }
);

Our function "checkIfNotRegister" is

checkIfNotRegister(): AsyncValidatorFn {
    return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
      return this.service.getWebName(control.value).pipe(
        map(res => res ? { repeat: "name yet register" } : null)
      )
    };
  }

And the .html is like

<form [formGroup]="testForm">
    <mat-form-field class="name" appearance="outline">
        <input matInput placeholder="Name" formControlName="name">
    <mat-error *ngIf="testForm.get('name').hasError('required')">
      Name is required*
    </mat-error>
    <mat-error *ngIf="testForm.get('name').hasError('repeat')">
      Unknown server Error
    </mat-error>
    <mat-hint *ngIf="testForm.get('name').pending">Validating...</mat-hint>
  </mat-form-field>

  <mat-form-field class="age" appearance="outline">
        <input matInput placeholder="Age" formControlName="age">
    <mat-error *ngIf="testForm.get('age').hasError('required')">
      Age is required*
    </mat-error>
  </mat-form-field>

  <div>
    <button type="submit" (click)="submitForm()">Submit</button>
  </div>
</form>

See how we use <mat-hint> to show when is checking the observable

Update only check the async validators in submit()

If we make in submit some like:

  submitForm() {
    if (this.testForm.valid)
    {
      //Add an async validators
      this.testForm.get('name').setAsyncValidators(this.checkIfNotRegister());
      //force Angular to updateValueAndValidity
      this.testForm.get('name').updateValueAndValidity();
      //remove the async validator
      this.testForm.get('name').setAsyncValidators(null);
    }
  }

Update 2019-06-27

But this don't wait to check if is valid, so need make another step that it's susbcribe to this.testForm.statusChanges, so our submitForm becomes like

submitForm() {
    if (this.testForm.valid) {
      this.testForm.statusChanges.pipe(take(2),last()).subscribe(res=>{
          //if res=='VALID' make something
          //if res=='INVALID'we don't need make nothing
          console.log(res)
      })
      this.testForm.get('name').setAsyncValidators(this.checkIfNotRegister());
      this.testForm.get('name').updateValueAndValidity({onlySelf:false,emitEvent:true});
      this.testForm.get('name').setAsyncValidators(null);
    }

Our form not need validator onBlur or onSubmit

this.testForm = new FormGroup(
      {
        name: new FormControl("Vivek Kumar", Validators.required),
        age: new FormControl(30, Validators.required)
      }
    );

You can see in the stackblitz the final result

Upvotes: 1

Related Questions