I'm attempting to implement custom password validation for a password field. The password must be a minimum of 8 characters and satisfy at least two of the following criteria but not necessarily all four:
- Contains numbers
- Contains lowercase letters
- Contains uppercase letters
- Contains special characters
I have successfully implemented part of the validation, but I am encountering an issue where if the password is 8 or more characters long but does not meet at least two of the specified criteria, the error message does not appear. The error message related to character requirements only displays when the password is less than 8 characters long.
I have searched extensively on SO without finding a solution to similar issues. I suspect that the problem lies in my custom validation function not being associated with the password in ngModel.
The Question: How can I ensure that the error message is shown in the form field when the password is at least 8 characters long but does not meet the required character criteria mentioned above?
Here is the relevant code snippet.
From user-form.html:
<mat-form-field *ngIf="newPassword" fxFlex="100%">
<input matInput #password="ngModel" placeholder="Password" type="password" autocomplete="password"
[(ngModel)]="model.password" name="password" minlength="8" (keyup)="validatePassword(model.password)" required>
<mat-error *ngIf="invalidPassword">
Password must contain at least two of the following: numbers, lowercase letters, uppercase letters, or special characters.
</mat-error>
<mat-error *ngIf="password.invalid && (password.dirty || password.touched)">
<div *ngIf="password.errors.required">
Password is required
</div>
<div *ngIf="password.errors.minlength">
Password must be at least 8 characters
</div>
</mat-error>
</mat-form-field>
From user-form.component.ts:
export class UserFormComponent implements OnInit {
@Input()
user: User;
public model: any;
public invalidPassword: boolean;
constructor() {}
ngOnInit() {
this.model = this.user;
}
passwordFails(checks: boolean[]): boolean {
let counter = 0;
for (let i = 0; i < checks.length; i++) {
if (checks[i]) {
counter += 1;
}
}
return counter < 2;
}
validatePassword(password: string) {
let hasLower = false;
let hasUpper = false;
let hasNum = false;
let hasSpecial = false;
const lowercaseRegex = new RegExp("(?=.*[a-z])");// has at least one lower case letter
if (lowercaseRegex.test(password)) {
hasLower = true;
}
const uppercaseRegex = new RegExp("(?=.*[A-Z])"); //has at least one upper case letter
if (uppercaseRegex.test(password)) {
hasUpper = true;
}
const numRegex = new RegExp("(?=.*\\d)"); // has at least one number
if (numRegex.test(password)) {
hasNum = true;
}
const specialcharRegex = new RegExp("[!@#$%^&*(),.?\":{}|<>]");
if (specialcharRegex.test(password)) {
hasSpecial = true;
}
this.invalidPassword = this.passwordFails([hasLower, hasUpper, hasNum, hasSpecial]);
}
}