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
- First of all I created the repository input-validator-tooltip-angular on GitHub
- Second I created the angular 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