I have this code to check if both passwords match, however, it's not working as intended. I've tried what I've seen in some older questions but with no success.
I have this in my component's template:
<form [formGroup]="passwordForm">
<mat-form-field>
<input #thePassword matInput [type]="hidePasswordButton ? 'password' : 'text'" [formControl]="password"
(input)="onPasswordInput(thePassword.value)" />
<mat-label>Enter Password</mat-label>
<mat-error *ngIf="password.hasError('required')">
Password is <strong>required</strong>
</mat-error>
<mat-error *ngIf="password.hasError('minlength')">
Must have <strong>at least 8 characters.</strong>
</mat-error>
<button mat-icon-button matSuffix (click)="hidePasswordButton = !hidePasswordButton"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="hidePasswordButton">
<mat-icon>{{ hidePasswordButton ? "visibility_off" : "visibility" }}</mat-icon>
</button>
</mat-form-field>
<mat-form-field *ngIf="!isInputDisabled()">
<input #ConfirmedPassword matInput [type]="hideConfirmedPasswordButton ? 'password' : 'text'"
[formControl]="confirmedPassword" (input)="onConfirmedPasswordInput(ConfirmedPassword.value)" />
<mat-label>Confirm Password</mat-label>
<mat-error *ngIf="passwordForm.errors?.['mismatch']">
Passwords <strong>don't match</strong>
</mat-error>
<mat-error *ngIf="confirmedPassword.hasError('required')">
<strong>You must confirm your password</strong>
</mat-error>
<button mat-icon-button matSuffix (click)="hideConfirmedPasswordButton = !hideConfirmedPasswordButton"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="hideConfirmedPasswordButton">
<mat-icon>{{ hideConfirmedPasswordButton ? "visibility_off" : "visibility" }}</mat-icon>
</button>
</mat-form-field>
</form>
Then the ts file is something like this:
import { matchingPasswords } from 'src/types/password-match-validator';
password = new FormControl('', [Validators.required, Validators.minLength(8), Validators.pattern(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)/)]);
confirmedPassword = new FormControl('', [Validators.required, Validators.minLength(8)]);
passwordForm: FormGroup = new FormGroup({
password: this.password,
confirmedPassword: this.confirmedPassword
},
{
validators: [matchingPasswords]
});
The matchingPasswords
validator comes from this class:
import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms";
export const matchingPasswords : ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const password = control.get('password');
const confirmedPassword = control.get('confirmedPassword');
return password
&& confirmedPassword
&& password.value !== confirmedPassword.value
? { 'mismatch': true }
: null;
}
This approach kinda works in the sense that if I type something in the password input, say "Abcd1234" and then some random stuff in the confirmation then the mat-error triggers, however, if I type something like "123456aA" in the password input and then something like "123456aB" in the confirmation one the error never shows up even tho both passwords are not exactly the same. At least it doesn't let me submit the form, but I need to show that error message.
I've already tried with the this.fb.group
approach, but that didn't work either.
The reason why the error for passwordForm.errors?.['mismatch']
is due to the default behavior of ErrorStateMatcher
which shows the error when these conditions are fulfilled:
The confirmedPassword
FormControl
is required to show an error other than the error from itself, which is the "mismatch" error from the FormGroup.
You need to override the default ErrorStateMatcher
behavior in order to resolve the issue.
ErrorStateMatcher
.export class ConfirmedPasswordErrorStateMatcher implements ErrorStateMatcher {
isErrorState(
control: FormControl | null,
form: FormGroupDirective | NgForm | null
): boolean {
return !!(
control &&
(control.invalid || form?.errors?.['mismatch']) &&
(control.dirty || control.touched || (form && form.submitted))
);
}
}
ConfirmedPasswordErrorStateMatcher
instance in the component.confirmedPasswordErrorStateMatcher = new ConfirmedPasswordErrorStateMatcher();
[errorStateMatcher]="confirmedPasswordErrorStateMatcher"
to the confirmedPassword
control.<input
#ConfirmedPassword
matInput
[type]="hideConfirmedPasswordButton ? 'password' : 'text'"
[formControl]="confirmedPassword"
(input)="onConfirmedPasswordInput(ConfirmedPassword.value)"
[errorStateMatcher]="confirmedPasswordErrorStateMatcher"
/>