Input validator password Angular 17

A common problem I’ve noticed in some systems is the difficulty in defining a password.

Nowadays, there are many requirements when creating a password. I remember that in the past, passwords like ‘123’ or ‘admin’ were common.

But now it’s so hard to define a password that sometimes I almost cry.

If we, as developers, have trouble defining a ‘simple password,’ just imagine the challenges our parents and grandparents face.

The truth is, we need significant improvements in UX (User Experience), particularly within banking systems!

On this post I will share a good approach to improve our UX.

 

You will learn how to create this

 

 

Start project

ng new input-validator-tooltip-angular --no-standalone
  • I chose SCSS for stylesheet
  • Server-Side Rendering I Chose: NO

  • Note: Starting from Angular v17, the module won’t be created.”. But I want module so I add: –no-standalone
  • To understand a little more about Angular CLI (ng), you can see more details here
  • Now, let’s add Angular Material
ng add @angular/material

Creating the directive

  • Creating the module
ng g m validator-password-tooltip
  • Creating the directive
ng g d validator-password-tooltip/validator-password-tooltip
  • In ValidatorPasswordTooltipModule we will add MatTooltipModule in imports and export ValidatorPasswordTooltipDirective. Like that:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatTooltipModule } from '@angular/material/tooltip';

import { ValidatorPasswordTooltipDirective } from './validator-password-tooltip.directive';


@NgModule({
  declarations: [
    ValidatorPasswordTooltipDirective
  ],
  imports: [
    CommonModule,
    MatTooltipModule
  ],
  exports:[
    ValidatorPasswordTooltipDirective
  ]
})
export class ValidatorPasswordTooltipModule {}
  • Now we are opening the ValidatorPasswordTooltipDirective and extends MatTooltip
import { Directive } from '@angular/core';
import { MatTooltip } from '@angular/material/tooltip';


@Directive({
    selector: '[validatorTooltip]',
    exportAs: 'validatorTooltip'
})
export class ValidatorPasswordTooltipDirective extends MatTooltip {}
  • Maybe you’re asking: Why do we extend MatTooltip? Because we will use it!
  • In this example I want to utilize, but if you prefer, you can create a custom tooltip. In this post you can see how.
  • Essentially we will change the behavior of MatTooltip. To understand how I opened the class and analyzed it’s structure.
  • Just add these lines inside class
@Input()
get validatorTooltip() { return this.message; }

set validatorTooltip(value: string) { 
  this.message = value + "- step1";
  this.show(); 
}
  • In the code above we are overriding the default behavior.
  • When the value changes, the function  ‘setvalidatorTooltip’ is called, so I added ‘this.show()’ to show tooltip.
  • Now we can use the created directive but first we need to import some modules. Go to app.module.ts and add ReactiveFormsModule, MatInputModule and ValidatorPasswordTooltipModule:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatInputModule } from '@angular/material/input';
import { ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ValidatorPasswordTooltipModule } from './validator-password-tooltip/validator-password-tooltip.module';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    ReactiveFormsModule, // add to use FormControl
    MatInputModule, // add to use input from material
    ValidatorPasswordTooltipModule // directive created
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
  • In app.component.ts put this code:
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {
  public password = new FormControl();
}
  • In app.component.html remove all html and put this:
<div style="display: flex; justify-content: center; align-items: center; padding-top: 50px;">
  <mat-form-field style="width: 50%;">
    <mat-label>Password</mat-label>
    <input matInput [formControl]="password" [validatorTooltip]="password.value">
  </mat-form-field>
</div>
  • The result:

  • Now it’s the time for magic!
  • Put this code in validator-password-tooltip.directive.ts:
import { Directive, Input, OnInit } from '@angular/core';
import { MatTooltip } from '@angular/material/tooltip';


@Directive({
    selector: '[validatorTooltip]',
    exportAs: 'validatorTooltip'
})
export class ValidatorPasswordTooltipDirective extends MatTooltip implements OnInit {

  ngOnInit(): void {
    super.tooltipClass = 'validator-password-tooltip';
  }

  @Input()
  get validatorTooltip() { return this.message; }

  set validatorTooltip(value: string) {
    this.message = this.getMessage(value);
    this.show();
  }

  private getMessage(value: string): string {
    const text = `${this.getTextRoleNumber(this.hasAnyNumber(value))}
    ${this.getTextRoleUpper(this.hasAnyUpper(value))}
    ${this.getTextRoleLower(this.hasAnyLower(value))}
    ${this.getTextRoleSpecial(this.hasAnySpecial(value))}
    ${this.getTextRoleLimit(this.hasLimit(value))}`;

    return text;
  }

  private getTextRoleNumber(showIcon: boolean): string { return `${this.getIcon(showIcon)} - Password must contain at least 1 number (0-9)\n` }

  private getIcon(showIcon: boolean): string { return `${showIcon ? '✅' : '❌'}` }
  
  private hasAnyNumber(value: string): boolean { return /[0-9]/.test(value) }
  
  private getTextRoleUpper(showIcon: boolean): string { return `${this.getIcon(showIcon) } - Password must contain at least 1 uppercase letter\n` }
  
  private hasAnyUpper(value: string): boolean { return /[A-Z]/.test(value) }
  
  private getTextRoleLower(showIcon: boolean): string { return `${this.getIcon(showIcon) } - Password must contain at least 1 lowercase letter\n` }
  
  private hasAnyLower(value: string): boolean { return /[a-z]/.test(value) }
  
  private getTextRoleSpecial(showIcon: boolean): string { return `${this.getIcon(showIcon) } - Password must contain at least 1 special character\n` }
  
  private hasAnySpecial(value: string): boolean { return /[^\w\d\s:]/.test(value) }
  
  private getTextRoleLimit(showIcon: boolean): string { return `${this.getIcon(showIcon)} - Password must have 7 to 20 characters without spaces\n` }
  
  private hasLimit(value: string): boolean { return /^([^\s]){7,20}$/gm.test(value) }

}

Understanding the code

set validatorTooltip(value: string) {
  this.message = this.getMessage(value);
  this.show();
}

private getMessage(value: string): string {
  const text = `${this.getTextRoleNumber(this.hasAnyNumber(value))}
                ${this.getTextRoleUpper(this.hasAnyUpper(value))}
                ${this.getTextRoleLower(this.hasAnyLower(value))}
                ${this.getTextRoleSpecial(this.hasAnySpecial(value))}
                ${this.getTextRoleLimit(this.hasLimit(value))}`;

  return text;
}
  • Here, we simply call the function ‘getMessage(value)’
  • This function will return the text that will be shown inside the tooltip
private getIcon(showIcon: boolean): string { return `${showIcon ? '✅' : '❌'}` }
  • The function above just show a simple icon (✅ or ❌)
private hasAnyNumber(value: string): boolean { return /[0-9]/.test(value) }  
  
private hasAnyUpper(value: string): boolean { return /[A-Z]/.test(value) }
  
private hasAnyLower(value: string): boolean { return /[a-z]/.test(value) }
  
private hasAnySpecial(value: string): boolean { return /[^\w\d\s:]/.test(value) }
  
private hasLimit(value: string): boolean { return /^([^\s]){7,20}$/gm.test(value) }
  • This part is probably the most complex.
  • We have a function for each rule using Regex.
  • Regex is an important area in programming mainly for data validation
  • Basically in this code I validate if the value has (number, upper…)
return /^([^\s]){7,20}$/gm.test(value)

  • Calm down. It may seem difficult to understand, but we have tools that help us with this creation
  • This site is amazing 🙂
  • And you will probably create a function that someone has already created, so it’s simple to find.
private getTextRoleNumber(showIcon: boolean): string { return `${this.getIcon(showIcon)} - Password must contain at least 1 number (0-9)\n` }

private getTextRoleUpper(showIcon: boolean): string { return `${this.getIcon(showIcon) } - Password must contain at least 1 uppercase letter\n` } 

private getTextRoleLower(showIcon: boolean): string { return `${this.getIcon(showIcon) } - Password must contain at least 1 lowercase letter\n` }  

private getTextRoleSpecial(showIcon: boolean): string { return `${this.getIcon(showIcon) } - Password must contain at least 1 special character\n` }  

private getTextRoleLimit(showIcon: boolean): string { return `${this.getIcon(showIcon)} - Password must have 7 to 20 characters without spaces\n` }
  • Here we just put the message that will be presented and call the function ‘getIcon(showIcon)’
  • That’s the result:

  • Now we just need adjust the css
  • Open styles.scss and put this:
.validator-password-tooltip {
    white-space: pre-line;

    .mdc-tooltip__surface {
        max-width: unset;
    }   
}
  • In ValidatorPasswordTooltipDirective add this:
import { Directive, Input, OnInit } from '@angular/core';
import { MatTooltip } from '@angular/material/tooltip';


@Directive({
    selector: '[validatorTooltip]',
    exportAs: 'validatorTooltip'
})
export class ValidatorPasswordTooltipDirective extends MatTooltip implements OnInit {
  ngOnInit(): void {
    super.tooltipClass = 'validator-password-tooltip';
  }
}
  • Simple right? We just change the css from MatTooltip and now we have the final result:

Conclusion

  • This approach is simple but I have been seeing a lot of system confusing the final user to do that simple task.
  • If you have ever faced any problem in setting password for any system, tell me below.
  • I hope you enjoyed it and see you next time 😀

 

Code: https://github.com/devinvestidor/input-validator-tooltip-angular

Leave a Reply