Arter
Arter

Reputation: 2324

handling with multiple mat-errors

I have my error handling for mat-input like this

      <mat-form-field>
        <input formControlName="title" matInput placeholder="Title*" />
        <mat-error *ngIf="addNewForm.get('title').errors?.required">
          Can not be empty
        </mat-error>
        <mat-error *ngIf="addNewForm.get('title').errors?.minlength">
          Min length is 1 character
        </mat-error>
        <mat-error *ngIf="addNewForm.get('title').errors?.maxlength">
          Max length is 255 characters
        </mat-error>
      </mat-form-field>

      <mat-form-field>
        <input formControlName="name" matInput placeholder="Name*" />
        <mat-error *ngIf="addNewForm.get('name').errors?.required">
          Can not be empty
        </mat-error>
        <mat-error *ngIf="addNewForm.get('name').errors?.minlength">
          Min length is 1 character
        </mat-error>
        <mat-error *ngIf="addNewForm.get('name').errors?.maxlength">
          Max length is 255 characters
        </mat-error>
      </mat-form-field>

How to handle this directly in component, soo instead of writing all of this errors manually, check all in component and dynamically set errors, something like this

      <mat-form-field>
        <input formControlName="title" matInput placeholder="Title*" />
        <mat-error>{{ someError }}</mat-error>
      </mat-form-field>

      <mat-form-field>
        <input formControlName="name" matInput placeholder="Name*" />
        <mat-error>{{ someError }}</mat-error>
      </mat-form-field>

Here is component

this.addNewUserForm = this.formBuilder.group({
      title: [
        "",
        [
          Validators.required,
          Validators.minLength(1),
          Validators.maxLength(255),
          noWhitespaceValidator
        ]
      ],
      name: [
        "",
        [
          Validators.required,
          Validators.minLength(4),
          Validators.maxLength(255),
          noWhitespaceValidator
        ]
      ]
    });

Upvotes: 0

Views: 11886

Answers (2)

Anarno
Anarno

Reputation: 1640

You can write a getter function which returns simply the error message. Like this:

get errorMessage(): string {
   const form: FormControl = (this.addNewUserForm.get('title') as FormControl);
   return form.hasError('required') ?
     'Required error message' :
     form.hasError('maxlength') ?
     'Max length error' : 
     form.hasError('minlength') ?
     'Min length error' :
     form.hasError('nowhitespaceerror') ?
     'No white space error' : '';
}

After this you simply use this method like a variable in template:

<mat-error>{{ errorMessage }}</mat-error>

Or a simple solution based on this example: here.

export class InputErrorExample {
  constructor(private formBuilder: FormBuilder) {}

  errorMessages = {
    maxlength: 'max',
    minlength: 'min',
    email: 'email'
  };

  addNewUserForm = this.formBuilder.group({
      title: [
        "",
        [
          Validators.required,
          Validators.minLength(1),
          Validators.maxLength(255),
          Validators.email
          // noWhitespaceValidator
        ]
      ]
    });

  errors(ctrl: FormControl): string[] {
    return ctrl.errors ? Object.keys(ctrl.errors) : [];
  }
}
<form class="example-form">
  <mat-form-field class="example-full-width">
    <input matInput placeholder="Email" [formControl]="addNewUserForm.get('title')"
           [errorStateMatcher]="matcher">
    <mat-hint>Errors appear instantly!</mat-hint>
    <mat-error>
      <ng-container *ngFor="let error of errors(addNewUserForm.get('title'))">
        {{ errorMessages[error] }} <br>
      </ng-container>
    </mat-error>
  </mat-form-field>
</form>

Upvotes: 6

Andrei Gătej
Andrei Gătej

Reputation: 11934

Here is one approach:

Listen to the statusChanges observable of your FormControl instance and when it emits, get its first error.

const titleCtrl = this.addNewUserForm.get('title');

const titleErr$ = titleCtrl.statusChanges
  .pipe(
   filter(status => status === 'INVALID'),
   map(() => {
    if (!titleCtrl.errors) {
      return null; 
    }   

    // Returning the first error
    for (const errName in titleCtrl.errors) {
       return titleCtrl.errors[errName];
     }
   }),
   filter(err => !!err)
  )

Now, it depends on your use-case.

If you need to use your error only once in the template, you can use the async pipe.

<mat-form-field>
    <input formControlName="title" matInput placeholder="Title*" />
    <mat-error>{{ titleErr$ | async }}</mat-error>
</mat-form-field>

If you need to use the error in multiple places or if you need to perform the same logic for multiple FormControls, you can either subscribe in the component(imperative way) or you can find a way to combine all the errors and keep the subscription in the view(reactive way).

Bear in mind that if you subscribe in the component class, you'll have to unsubscribe.

Upvotes: 1

Related Questions