Showing the strength of the password entered by the user is one of the common feature in many applications. Showing relevant messages to make the password more stronger can also provide a very good user experience. In this example, we will implement the Password Strength indicator along with Error messages using Angular 13+ and TypeScript. The output of this task will be something like this (We have used bootstrap CSS for the UI)

Rules considered for Strong Password

What is a strong password is a relative thing. It depends on application to application. Applications involving financial transactions may need a very strong password. For our example purpose we are considering the following rules

  • Password must contain at least 6 characters
  • Password must contain a lowercase letter
  • Password must contain a number
  • Password must contain an uppercase letter
  • Password must contain a special character
  • Though minimum 6 characters length is OK, we consider the password better if it contains more than 8 characters.

Each of the above rule carries one point. So the total amount of 6 points makes the password very strong. 5 points strong, 4 points average and 3 points poor.

We recommend you to adjust the rules add or remove any rule based on your exact requirement.

How We are checking Each Rule ?

We are using the following methods to check if a rule is satisfied

  • We are using the length property of strings in JavaScript to determine if the password length is 6 or more than 8.
  • We are using Regular Expression “/[a-z]/” to check if the password contains a lowercase letter
  • We are using Regular Expression “/[A-Z]/” to check if the password contains a uppercase letter
  • We are using Regular Expression “/\d/” to check if the password contains a number
  • We are using Regular Expression “/[-+_!@#$%^&*.,?{}()\[\]]/”, to check if the password contains a special character

Here is the HTML part of the application:

<div class="mb-3 mt-5 ">
    <label for="password" class="form-label">Password</label>
    <input type="password" class="form-control" id="password" #pw (input)="checkPasswordStrength(pw.value)">
</div>
<div class="password-strength" id="passwordStrengthMeter">
    <span [ngClass]="poorPasswordClass" id="poor"></span>
    <span [ngClass]="averagePasswordClass" id="average"></span>
    <span [ngClass]="goodPasswordClass" id="good"></span>
    <span [ngClass]="veryGoodPasswordClass" id="very-good"></span>
</div>
<div style="width: 30vw;">
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
        <symbol id="check-circle-fill" fill="currentColor" viewBox="0 0 16 16">
            <path
                d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
        </symbol>

        <symbol id="exclamation-triangle-fill" fill="currentColor" viewBox="0 0 16 16">
            <path
                d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
        </symbol>
    </svg>

    <div [ngClass]=" getAlertClass(isLengthValid) " role="alert" id="lengthMessage" >
        <svg class="bi flex-shrink-0 me-2" width="16" height="16" role="img"
            [attr.aria-label]=" isLengthValid ? 'Success:' : 'Danger:' ">
            <use *ngIf="!isLengthValid" xlink:href="#exclamation-triangle-fill" />
            <use *ngIf="isLengthValid" xlink:href="#check-circle-fill" />
        </svg>
        <div>
            Password must be of 6 characters length
        </div>
    </div>

    <div [ngClass]=" getAlertClass(containsLowerCaseLetter) " role="alert" id="lowerCaseLetter">
        <svg class="bi flex-shrink-0 me-2" width="16" height="16" role="img"
            [attr.aria-label]=" containsLowerCaseLetter ? 'Success:' : 'Danger:' ">
            <use *ngIf="!containsLowerCaseLetter" xlink:href="#exclamation-triangle-fill" />
            <use *ngIf="containsLowerCaseLetter" xlink:href="#check-circle-fill" />
        </svg>
        <div>
            Password must contain a lower case letter
        </div>
    </div>

    <div [ngClass]=" getAlertClass(containsUpperCaseLetter) " role="alert" id="upperCaseLetter">
        <svg class="bi flex-shrink-0 me-2" width="16" height="16" role="img"
            [attr.aria-label]=" containsUpperCaseLetter ? 'Success:' : 'Danger:' ">
            <use *ngIf="!containsUpperCaseLetter" xlink:href="#exclamation-triangle-fill" />
            <use *ngIf="containsUpperCaseLetter" xlink:href="#check-circle-fill" />
        </svg>
        <div>
            Password must contain a upper case letter
        </div>
    </div>

    <div [ngClass]=" getAlertClass(containsNumber) " role="alert" id="numberCharacter">
        <svg class="bi flex-shrink-0 me-2" width="16" height="16" role="img"
            [attr.aria-label]=" containsNumber ? 'Success:' : 'Danger:' ">
            <use *ngIf="!containsNumber" xlink:href="#exclamation-triangle-fill" />
            <use *ngIf="containsNumber" xlink:href="#check-circle-fill" />
        </svg>
        <div>
            Password must contain a number
        </div>
    </div>

    <div [ngClass]=" getAlertClass(containsSpecialCharacter) " role="alert" id="specialCharacter">
        <svg class="bi flex-shrink-0 me-2" width="16" height="16" role="img"
            [attr.aria-label]=" containsSpecialCharacter ? 'Success:' : 'Danger:' ">
            <use *ngIf="!containsSpecialCharacter" xlink:href="#exclamation-triangle-fill" />
            <use *ngIf="containsSpecialCharacter" xlink:href="#check-circle-fill" />
        </svg>
        <div>
            Password must contain a special character
        </div>
    </div>
</div>

In the above HTML, to keep the things simple, we did not use any external module and used Template Reference Variable (#pw) to access the value of the password entered by the user. We are passing this value to the component TS file through input event binding.

We have used ngClass instead of normal CSS class, so that we can change the class names later based on the strength of the password.

Also pay attention to the way, we have set the value to aria-label properties dynamically. If we do property binding directly on aria-label, we get the error like Can’t bind to ‘aria-label’ since it isn’t a known property of element name. So, instead of doing property binding directly on it, we did on attr.aria-label.

We are using *ngIf to display either exclamation or checked icon depending on whether the password is satisfying that rule or not.

CSS:

.password-strength {
    display: flex;
}

.password-strength span{    
    position: relative;    
    height: 5px;
    width: 25%;
    border-radius: 5px;
    margin: 5px;
}

.password-strength-background {
    background-color: gray;
}

.weak-password {
    background-color: red;
}

.average-password {
    background-color: orange;
}

.good-password {
    background-color: yellow;
}

.verygood-password {
    background-color: green;
}

TS:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'howtojs-passwordstrength',
  templateUrl: './passwordstrength.component.html',
  styleUrls: ['./passwordstrength.component.css']
})
export class PasswordstrengthComponent implements OnInit {
  regexForLowerLetter: RegExp = /[a-z]/;
  regexForUpperLetter: RegExp = /[A-Z]/;
  regexForNumber: RegExp = /\d/;
  regexForSpecialCharacter: RegExp = /[-+_!@#$%^&*.,?{}()\[\]]/
  passwordStrength: number = 0;

  lengthScore: number = 0;
  goodLengthScore: number = 0;
  lowerLetterScore: number = 0;
  upperLetterScore: number = 0;
  numberScore: number = 0;
  specialCharacterScore: number = 0;

  isLengthValid: boolean = false;
  containsLowerCaseLetter: boolean = false;
  containsUpperCaseLetter: boolean = false;
  containsNumber: boolean = false;
  containsSpecialCharacter: boolean = false;


  poorPasswordClass: string = 'password-strength-background';
  averagePasswordClass: string = 'password-strength-background';
  goodPasswordClass: string = 'password-strength-background';
  veryGoodPasswordClass: string = 'password-strength-background';


  constructor() { }

  ngOnInit(): void {
  }

  checkPasswordStrength(password: string) {

    if (password.length > 5) {
      this.lengthScore = 1;
      this.isLengthValid = true;
    } else {
      this.lengthScore = 0;
      this.isLengthValid = false;
    }

    if (password.match(this.regexForLowerLetter)) {
      this.lowerLetterScore = 1;
      this.containsLowerCaseLetter = true;
    } else {
      this.lowerLetterScore = 0;
      this.containsLowerCaseLetter = false;
    }

    if (password.match(this.regexForUpperLetter)) {
      this.upperLetterScore = 1;
      this.containsUpperCaseLetter = true;
    } else {
      this.upperLetterScore = 0;
      this.containsUpperCaseLetter = false;
    }

    if (password.match(this.regexForNumber)) {
      this.numberScore = 1;
      this.containsNumber = true;
    } else {
      this.numberScore = 0;
      this.containsNumber = false;
    }

    if (password.match(this.regexForSpecialCharacter)) {
      this.specialCharacterScore = 1;
      this.containsSpecialCharacter = true;
    } else {
      this.specialCharacterScore = 0;
      this.containsSpecialCharacter = false;
    }

    if (password.length > 8) {
      this.goodLengthScore = 1;
    } else {
      this.goodLengthScore = 0;
    }

    this.passwordStrength = this.lengthScore + this.lowerLetterScore + this.upperLetterScore
      + this.numberScore + this.specialCharacterScore + this.goodLengthScore;

    if (this.passwordStrength < 3) {
      this.poorPasswordClass = 'password-strength-background';
      this.averagePasswordClass = 'password-strength-background';
      this.goodPasswordClass = 'password-strength-background';
      this.veryGoodPasswordClass = 'password-strength-background';
    } else if (this.passwordStrength === 3) {
      this.poorPasswordClass = 'weak-password';
      this.averagePasswordClass = 'password-strength-background';
      this.goodPasswordClass = 'password-strength-background';
      this.veryGoodPasswordClass = 'password-strength-background';
    } else if (this.passwordStrength === 4) {
      this.poorPasswordClass = 'average-password';
      this.averagePasswordClass = 'average-password';
      this.goodPasswordClass = 'password-strength-background';
      this.veryGoodPasswordClass = 'password-strength-background';
    } else if (this.passwordStrength === 5) {
      this.poorPasswordClass = 'good-password';
      this.averagePasswordClass = 'good-password';
      this.goodPasswordClass = 'good-password';
      this.veryGoodPasswordClass = 'password-strength-background';
    } else if (this.passwordStrength === 6) {
      this.poorPasswordClass = 'verygood-password';
      this.averagePasswordClass = 'verygood-password';
      this.goodPasswordClass = 'verygood-password';
      this.veryGoodPasswordClass = 'verygood-password';
    }

  }

  getAlertClass(status: boolean) {
    if (status) {
      return "alert alert-success d-flex align-items-center small p-1"
    } else {
      return "alert alert-danger d-flex align-items-center small p-1"
    }
  }

}

In the above code, we defined properties for each rule score. We are assigning 1 or 0 based on whether the password is matching that rule. Then based on the total value of the score, we are setting the values for CSS classes to display appropriate UI. The other code is self-explanatory.

Please note that, we can achieve the same functionality in Angular 13+ in many different ways and the above mentioned method is not the only way. To keep the things straight, we kept all the code/logic in the component ts file itself and you are free to move it to any service for re-usability or separating the logic from the component.

Please check this post, if you want to implement the same with Vanilla JavaScript How to Implement Password Strength Indicator with Error Messages in JavaScript