Angular 5 中的自定义密码验证
Custom Password Validation in Angular 5
我正在尝试向密码字段添加自定义密码验证。密码必须至少包含 8 个字符,并且至少满足以下条件中的两个,但不必满足所有四个条件:
- 有数字
- 有小写字母
- 有大写字母
- 有特殊字符
我已经完成了部分验证工作,但我遇到了一个问题,如果密码长度超过 8 个字符但不满足上述至少两个条件,则错误消息将 不 显示。关于字符的错误只会在密码少于8个字符时显示。
我已经对 SO 进行了搜索,但没有成功实现类似问题的答案。我怀疑我遇到的问题与我的自定义验证函数没有与 ngModel 中的密码相关联有关。
问题:当密码超过 8 个字符但不符合上述字符要求时,如何让错误消息显示在表单字段上?
这是相关代码。
来自用户-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>
来自用户-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]);
}
}
您正在将 boolean
值传递给 passwordFails
函数
this.invalidPassword = this.passwordFails([hasLower, hasUpper, hasNum, hasSpecial]);
将您的函数更改为以下内容,如果密码不符合其中一个条件,这将 return 为真。
passwordFails(checks: boolean[]): boolean {
return checks.filter((x)=> typeof x === 'boolean' && x === true).length > 2
}
并且在 dom 中您可以添加另一个条件来检查 invalidPassword
<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>
<div *ngIf="!invalidPassword">
Password must meet the following criteria.
</div>
</mat-error>
</mat-form-field>
Component.html
<div class="error-text" *ngIf="myForms.get('password').hasError('passwordStrength')">
{{myForms.get('password').errors['passwordStrength']}}
</div>
Component.ts
this.myForms = fb.group({
password: [null, Validators.compose([
Validators.required, Validators.minLength(8), PasswordStrengthValidator])]
});
密码-strength.validators.ts
import { AbstractControl, ValidationErrors } from "@angular/forms"
export const PasswordStrengthValidator = function (control: AbstractControl): ValidationErrors | null {
let value: string = control.value || '';
let msg="";
if (!value) {
return null
}
let upperCaseCharacters = /[A-Z]+/g;
let lowerCaseCharacters = /[a-z]+/g;
let numberCharacters = /[0-9]+/g;
let specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\|,.<>\/?]+/;
if (upperCaseCharacters.test(value) === false || lowerCaseCharacters.test(value) === false || numberCharacters.test(value) === false || specialCharacters.test(value) === false) {
return {
passwordStrength: 'Password must contain at least two of the following: numbers, lowercase letters, uppercase letters, or special characters.'
}
}
}
我进一步研究了这个,看看我是否能弄清楚如何在 Angular 中使用验证器。我终于能够通过这些更新做到这一点:
创建密码-validator.directive.ts:
import { Directive, forwardRef, Attribute} from "@angular/core";
import { Validator, AbstractControl, NG_VALIDATORS} from "@angular/forms";
@Directive({
selector: '[validatePassword]',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => PasswordValidator), multi: true}
]
})
export class PasswordValidator implements Validator {
constructor (
@Attribute('validatePassword')
public invalidPassword: boolean
) {}
validate(ctrl: AbstractControl): {[key: string]: any} {
let password = ctrl.value;
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;
}
let counter = 0;
let checks = [hasLower, hasUpper, hasNum, hasSpecial];
for (let i = 0; i < checks.length; i++) {
if (checks[i]) {
counter += 1;
}
}
if (counter < 2) {
return { invalidPassword: true }
} else {
return null;
}
}
}
更新在用户-form.component.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" validatePassword="password" required>
<mat-error *ngIf="password.invalid && (password.dirty || password.touched)">
<div *ngIf="password.errors.invalidPassword">
Password must have two of the four: lowercase letters, uppercase letters, numbers, and special characters
</div>
<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>
更新在用户-form.module.ts:
import {PasswordValidator} from "../password-validator.directive"; //imported to modules
@NgModule({
imports: [
//some modules
],
declarations: [
// some modules
PasswordValidator // added this to declarations
],
exports: [
// stuff
],
providers: [
//stuff
]
})
export class UserFormModule { }
我正在尝试向密码字段添加自定义密码验证。密码必须至少包含 8 个字符,并且至少满足以下条件中的两个,但不必满足所有四个条件:
- 有数字
- 有小写字母
- 有大写字母
- 有特殊字符
我已经完成了部分验证工作,但我遇到了一个问题,如果密码长度超过 8 个字符但不满足上述至少两个条件,则错误消息将 不 显示。关于字符的错误只会在密码少于8个字符时显示。
我已经对 SO 进行了搜索,但没有成功实现类似问题的答案。我怀疑我遇到的问题与我的自定义验证函数没有与 ngModel 中的密码相关联有关。
问题:当密码超过 8 个字符但不符合上述字符要求时,如何让错误消息显示在表单字段上?
这是相关代码。
来自用户-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>
来自用户-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]);
}
}
您正在将 boolean
值传递给 passwordFails
函数
this.invalidPassword = this.passwordFails([hasLower, hasUpper, hasNum, hasSpecial]);
将您的函数更改为以下内容,如果密码不符合其中一个条件,这将 return 为真。
passwordFails(checks: boolean[]): boolean {
return checks.filter((x)=> typeof x === 'boolean' && x === true).length > 2
}
并且在 dom 中您可以添加另一个条件来检查 invalidPassword
<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>
<div *ngIf="!invalidPassword">
Password must meet the following criteria.
</div>
</mat-error>
</mat-form-field>
Component.html
<div class="error-text" *ngIf="myForms.get('password').hasError('passwordStrength')">
{{myForms.get('password').errors['passwordStrength']}}
</div>
Component.ts
this.myForms = fb.group({
password: [null, Validators.compose([
Validators.required, Validators.minLength(8), PasswordStrengthValidator])]
});
密码-strength.validators.ts
import { AbstractControl, ValidationErrors } from "@angular/forms"
export const PasswordStrengthValidator = function (control: AbstractControl): ValidationErrors | null {
let value: string = control.value || '';
let msg="";
if (!value) {
return null
}
let upperCaseCharacters = /[A-Z]+/g;
let lowerCaseCharacters = /[a-z]+/g;
let numberCharacters = /[0-9]+/g;
let specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\|,.<>\/?]+/;
if (upperCaseCharacters.test(value) === false || lowerCaseCharacters.test(value) === false || numberCharacters.test(value) === false || specialCharacters.test(value) === false) {
return {
passwordStrength: 'Password must contain at least two of the following: numbers, lowercase letters, uppercase letters, or special characters.'
}
}
}
我进一步研究了这个,看看我是否能弄清楚如何在 Angular 中使用验证器。我终于能够通过这些更新做到这一点:
创建密码-validator.directive.ts:
import { Directive, forwardRef, Attribute} from "@angular/core";
import { Validator, AbstractControl, NG_VALIDATORS} from "@angular/forms";
@Directive({
selector: '[validatePassword]',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => PasswordValidator), multi: true}
]
})
export class PasswordValidator implements Validator {
constructor (
@Attribute('validatePassword')
public invalidPassword: boolean
) {}
validate(ctrl: AbstractControl): {[key: string]: any} {
let password = ctrl.value;
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;
}
let counter = 0;
let checks = [hasLower, hasUpper, hasNum, hasSpecial];
for (let i = 0; i < checks.length; i++) {
if (checks[i]) {
counter += 1;
}
}
if (counter < 2) {
return { invalidPassword: true }
} else {
return null;
}
}
}
更新在用户-form.component.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" validatePassword="password" required>
<mat-error *ngIf="password.invalid && (password.dirty || password.touched)">
<div *ngIf="password.errors.invalidPassword">
Password must have two of the four: lowercase letters, uppercase letters, numbers, and special characters
</div>
<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>
更新在用户-form.module.ts:
import {PasswordValidator} from "../password-validator.directive"; //imported to modules
@NgModule({
imports: [
//some modules
],
declarations: [
// some modules
PasswordValidator // added this to declarations
],
exports: [
// stuff
],
providers: [
//stuff
]
})
export class UserFormModule { }