0

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

8
  • MatFormField only displays mat-error elements when the FormControl has an error. It does not display just because you tell it to via ngIfElse .. If you give the mat-error outside of the mat-form-field then it works.. Commented Jun 23, 2019 at 12:23
  • Is there way that i can set formcontrol error after submitting the form. I have tried calling this.configurationForm.get('website').setErrors({apiErr: true}); and made the required changes in html too <mat-error *ngIf="configurationForm.get('website').hasError('apiErr') >some api error happened</mat-error> Commented Jun 23, 2019 at 12:29
  • I think you need to handle this with custom validator only.. Commented Jun 23, 2019 at 12:41
  • Can you provide stackblitz with your code?? Commented Jun 23, 2019 at 12:42
  • stackblitz.com/edit/… Commented Jun 23, 2019 at 13:22

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

2 Comments

Hi @eliseo, Thanks a lot for your reply. But asyncValidator will be automatically triggered on blur event. My requirement is to trigger the validation after submitting the form and it may be a scenario where there are errors on more than one field.
::glups:: it's true.... but... I just update the stackblitz and the answer. My solution is, in submit, make a setAsyncValidators and make an updateValueAndValidity(), see my answer updated

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.