This seems like it should be pretty straight forward... within a stepper, you're collecting info, and you want to make sure an email is an email. But it seems like the shared 'form' tag causes some issues where the error checker gets messed up and doesn't work?
Further clarification... the issue seems to actually be in the following tag element...
formControlName="emailCtrl"
When I remove this line, and remove it's sibling line from the .ts (emailCtrl: ['', Validators.required],) the error check starts working. However, that means that the stepper can't verify that this step is required.
How can I make sure the stepper validates an entry and at the same time make sure that the ErrorStateMatcher works?
Here is my combined HTML...
<mat-step [stepControl]="infoFormGroup">
<form [formGroup]="infoFormGroup">
<ng-template matStepLabel>Profile Information</ng-template>
<div>
<!-- <form class="emailForm"> -->
<mat-form-field class="full-width">
<input matInput placeholder="Username" [formControl]="emailFormControl"
formControlName="emailCtrl"
[errorStateMatcher]="infoMatcher">
<mat-hint>Must be a valid email address</mat-hint>
<mat-error *ngIf="emailFormControl.hasError('email') && !emailFormControl.hasError('required')">
Please enter a valid email address for a username
</mat-error>
<mat-error *ngIf="emailFormControl.hasError('required')">
A username is <strong>required</strong>
</mat-error>
</mat-form-field>
<!-- </form> -->
</div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</form>
</mat-step>
As you can see, I have commented out the nested 'form' for the email slot. In testing, I have tried it commented and not commented out. Either way, the error checking doesn't work right.
Here are some of the pertinent .ts snippets...
import { FormControl, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { FormBuilder, FormGroup } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
export class Pg2ErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
}
}
...
export class Pg2Dialog {
...
emailFormControl = new FormControl('', [
Validators.required,
Validators.email,
]);
infoMatcher = new Pg2ErrorStateMatcher();
...
this.infoFormGroup = this._formBuilder.group({
emailCtrl: ['', Validators.required],
});
I believe I figured this out. the ErrorStateMatcher requires a named form control. In this case, it's emailFormControl. This is declared as the following...
emailFormControl = new FormControl('', [
Validators.required,
Validators.email,
]);
Also, the stepper requires a named form group, that in itself declares a new form control. In this case, it was emailCtrl. It was declared as the following...
this.infoFormGroup = this._formBuilder.group({
emailCtrl: ['', Validators.required],
});
To have the stepper form control utilize the ErrorStateMatcher form control, simply drop the square brackets inside the .group assignment and assign emailFormControl to the emailCtrl. Like this...
this.infoFormGroup = this._formBuilder.group({
emailCtrl: this.emailFormControl
});
I tested this in a different code section with a similar problem and it worked in both places!