How to validate password strength with Angular 5 Validator Pattern
I took a stab at this with Angular's built-in pattern validator and was able to come up with the following that checks for:
- At least 8 characters in length
- Lowercase letters
- Uppercase letters
- Numbers
Special characters
password: [ '', [ Validators.required, Validators.pattern('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&].{8,}') ]]
I'll add that I'm by no means a regex expert. This is simply what worked for me for a closely related case to the OP. Maybe it will help someone else. Mozilla's documentation helped a lot in figuring this out, specifically the Writing a regular expression pattern section.
I´ve not been able to use correctly the Validator Pattern, so I made a Custom Validator, and validate the password field string with three simple regex.
Anyway, I look forward to use correctly the Angular Validator Pattern.
Custom Validator
// password.validator.tsimport { FormControl } from '@angular/forms';export interface ValidationResult { [key: string]: boolean;}export class PasswordValidator { public static strong(control: FormControl): ValidationResult { let hasNumber = /\d/.test(control.value); let hasUpper = /[A-Z]/.test(control.value); let hasLower = /[a-z]/.test(control.value); // console.log('Num, Upp, Low', hasNumber, hasUpper, hasLower); const valid = hasNumber && hasUpper && hasLower; if (!valid) { // return what´s not valid return { strong: true }; } return null; }}
Replaced the Validator Pattern with my Custom Validator
// signup.component.tsimport { Component } from '@angular/core';import { FormBuilder, FormGroup, Validators } from '@angular/forms';import { PasswordValidator } from 'validators/password.validator';@Component({ selector: 'signup', templateUrl: './signup.component.html', styleUrls: ['./signup.component.scss']})export class SignupComponent { form: FormGroup; constructor() { this.init(); } init() { this.form = this.fb.group({ name: ['', [Validators.required]], email: ['', [Validators.required, Validators.email], password: ['', [ Validators.required, PasswordValidator.strong ]] }); }}
If you have a look at Validator.js, you will notice that you may pass both a string or a regex literal to the Validators.pattern
.
Regex passed as a string literal
- The whole string must match the pattern (i.e. the pattern is anchored on both sides)
- Backslashes can form string escape sequences and you must use double backslashes to define a literal backslash that is used to define a regex escape. So, to define a digit matching pattern, use
'\\d'
, to define a whitespace pattern use'\\s'
, to define a backslash, use'\\\\'
.
Regex passed as a regex literal
- The regex does not automatically require a full string match.
- Use single backslashes to define regex escapes (e.g.
/\s+/
)
So, what you may use is either of the two:
this.form = this.fb.group({ name: ['', [Validators.required]], email: ['', [Validators.required, Validators.email], password: ['', [ Validators.required, Validators.pattern('(?=\\D*\\d)(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z]).{8,30}') ]]});
Or
this.form = this.fb.group({ name: ['', [Validators.required]], email: ['', [Validators.required, Validators.email], password: ['', [ Validators.required, Validators.pattern(/^(?=\D*\d)(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z]).{8,30}$/) ]]});
Regex details
Note that the pattern changes I suggest are merely those related to the principle of contrast:
^
- start of string (implicit in string regex pattern)(?=\D*\d)
- there must be 1 digit(?=[^a-z]*[a-z])
- there must be 1 lowercase ASCII letter(?=[^A-Z]*[A-Z])
- there must be 1 uppercase ASCII letter.{8,30}
- any 8 to 30 chars other than line break chars$
- end of string (implicit in string regex pattern).