components: [input] Add example of using mat-error with parent formgroup validation

Bug, feature request, or proposal: <mat-error> doesn’t show when I use an email maching validator for emails inputs.

What is the expected behavior? To display the error.

What is the current behavior? No error is displayed

What are the steps to reproduce?

Set a custom validator for check if two emails inputs are equals, like this :

private matchEmail(AC: AbstractControl) {
    return AC.get('mail').value === AC.get('mailconfirm').value ? null : { mailmismatch: true };
}

this.administratifForm = this.fb.group({
        (...),
        mail: this.fb.control('', [Validators.required, Validators.email]),
        mailconfirm: this.fb.control('', [Validators.required]),
        (...),
    }, {
    validator: this.matchEmail,
    },
);

The template :

<mat-form-field>
    <input matInput placeholder="Vérification d'email" formControlName="mailconfirm">
    <mat-error *ngIf="administratifForm.get('mailconfirm').hasError('required')">
        Ce champ est requis
    </mat-error>
    <mat-error *ngIf="administratifForm.hasError('mailmismatch')">
        Les adresses mail ne correspondent pas
    </mat-error>
</mat-form-field>

Which versions of Angular, Material, OS, TypeScript, browsers are affected? Angular 5.0.2, Material 5.0.0-rc0, MacOS Sierra, Firefox

Additional Information If i replace the <mat-error> tag by a <p> tag (or anything else), it’s work.

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 9
  • Comments: 20 (5 by maintainers)

Most upvoted comments

Ok, I’ve followed the stack post linked by @Jbz797, and here’s the TLDR version of that poorly written post.

  1. Add this class. You can add it to your current .ts, or import it from somewhere else. Doesn’t matter.
export class ParentErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
      const isSubmitted = !!(form && form.submitted);
      const controlTouched = !!(control && (control.dirty || control.touched));
      const controlInvalid = !!(control && control.invalid);
      const parentInvalid = !!(control && control.parent && control.parent.invalid && (control.parent.dirty || control.parent.touched));

      return isSubmitted || (controlTouched && (controlInvalid || parentInvalid));
  }
}
  1. Then, add [errorStateMatcher]="parentErrorStateMatcher" to the template input that needs the particular error. Add it to whichever mat-form-field needs to render that parent error. Eg, for passwords group with a password control and password_verify control, add it to password_verify’s input. Here’s mine.
<mat-form-field>
  <input matInput formControlName="password_verify" placeholder="Verify Password" type="password" required [errorStateMatcher]="parentErrorStateMatcher">
  <mat-error *ngIf="register_password_verify.invalid">{{ getRegisterPasswordVerifyError() }}</mat-error>
  <mat-error *ngIf="register_password_group.invalid && !register_password_verify.pristine">{{ getRegisterPasswordGroupError() }}</mat-error>
</mat-form-field>
  1. Now, the mat-error that belongs to the GROUP will be rendered. In the example above, it’s the last mat-error.

Thanks for this thread!

ErrorStateMatcher doesn’t work with custom MatFormFieldControl

How about [input] Add example of using mat-error with parent formgroup validation

See this answer for pretty much the same thing. I think it’s a little more convoluted than necessary, but looks to get the job done. I’ll try and add an official example soon.

@vcartera81 you don’t necessarily need to extend and instantiate an ErrorStateMatcher class. You could very much do something like this:

// this is untested

@Component({...})
export MyComponent {

  @Input() showError: boolean;

  myErrorStateMatcher = {
	isErrorState = () => this.showError;
  }
}

I agree that it isn’t the most convenient approach, but the ErrorStateMatcher is flexible enough to support a huge variety of use cases while maintaining the default error behavior defined in the spec.

Thank you very much, I found the solution with this answer.

mat-error only shows when the FormControl is invalid, but you’ve added the validation to a parent FormGroup. You’ll need to use a Custom Error Matcher to accomplish this.

I’ve been meaning to add an example for this because it’s a common need to validate matching passwords/emails/etc. Would you mind repurposing this issue for tracking such an example?

@ewaschen I’m answering your question from https://github.com/angular/material2/issues/4027#issuecomment-346077541 over here to keep the discussion related to the issue topics

If it works, the way you’ve outlined (by checking dirty and the two different possible validation errors) is totally fine. If you add more validation to the control though, you’ll have to remember to add it to your errorStateMatcher too (like min length or special character required).

Another approach (forgive me for not testing this) would be to inject ErrorStateMatcher and || your custom logic with it.

@Component({...})
export MyComponent {

  constructor(private defaultMatcher: ErrorStateMatcher) { }

  customErrorStateMatcher = {
    isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
      const invalidParent = control && control.touched && control.parent.invalid;
      return invalidParent || this.defaultMatcher.isErrorState(control, form);
    }
  }
}