Dynamic Tooltip with Angular Pipe: Performance Orientation
Dynamic Tooltip with Angular Pipe
Most of the time we have a requirement to transform our data in to a different style or formats from what we received by API services.
✋ A pipe takes in data as input and transforms it to a desired output.
Angular providers many Built-In Pipes as well as we can also create our custom pipes as per our requirement.
import { Pipe, PipeTransform } from '@angular/core';
In this Blog we will learn use of pipes with Tool tip.
Why need to implement tooltip with Pipe ❓
✅ Simple Message in Tooltip
If we have to apply a simple message in tooltip like ‘Name is required’, so we can directly append tooltip in html with form control.
✅ Conditional Tooltip
Sometimes we have to display tooltip with various checks. Examples of User name field:
- Show tooltip If Field is blank
- If field’s value is less then 6 – show tooltip message for length.
- If username is found duplicate show tooltip message for that.
▶️ If we do this in html , it will have various if else conditions checks.
▶️ We can call a method from class file where we can put all cases and can return message from there.
TS File ▶
getTooltip(): string {
const field = this.form.controls.userName;
// put our cases
return message;
}
HTML ▶
<input type="text" formControlName = "userName" [tooltip] = "getTooltip()" />
In above method problem is for each change detection this method will be called and that impact page performance. SO to overcome we can use pure pipes. Pure Pipes only execute when that particular field value is changed.
Let’s start coding :

HTML template
<div class="card m-3">
<h5 class="card-header">Implementation of Tooltip using Pipe</h5>
<div class="card-body">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div class="form-row">
<div class="form-group col">
<label>First Name</label>
<input type="text" formControlName="firstName" class="form-control" required
[ngClass]="{ 'is-invalid': submitted && f.firstName.errors }"
[title] ="f.firstName.errors | regTooltip : 'firstName'" />
</div>
<div class="form-group col">
<label>Email</label>
<input type="text" formControlName="email" class="form-control" required
[ngClass]="{ 'is-invalid': submitted && f.email.errors }"
[title] ="registerForm.controls.email.errors | regTooltip : 'email'" />
</div>
</div>
<div class="form-group">
</div>
<div class="form-row">
<div class="form-group col">
<label>Password</label>
<input type="password" formControlName="password" class="form-control"
[ngClass]="{ 'is-invalid': submitted && f.password.errors }"
[title] ="f.password.errors | regTooltip : 'password'" />
</div>
<div class="form-group col">
<label>Confirm Password</label>
<input type="password" formControlName="confirmPassword" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.confirmPassword.errors }" />
<div *ngIf="submitted && f.confirmPassword.errors" class="invalid-feedback">
<div *ngIf="f.confirmPassword.errors.required">Confirm Password is required</div>
<div *ngIf="f.confirmPassword.errors.mustMatch">Passwords must match</div>
</div>
</div>
</div>
<div class="form-group form-check">
<input type="checkbox" formControlName="acceptTerms" id="acceptTerms" class="form-check-input" [ngClass]="{ 'is-invalid': submitted && f.acceptTerms.errors }" />
<label for="acceptTerms" class="form-check-label">Accept Terms & Conditions</label>
<div *ngIf="submitted && f.acceptTerms.errors" class="invalid-feedback">Accept Ts & Cs is required</div>
</div>
<div class="text-center">
<button class="btn btn-primary mr-1">Submit</button>
<button class="btn btn-secondary" type="reset" (click)="onReset()">Reset</button>
</div>
</form>
</div>
</div>
TS File
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-add-todo',
templateUrl: './add-todo.component.html',
styleUrls: ['./add-todo.component.scss']
})
export class AddTodoComponent implements OnInit {
registerForm: FormGroup;
submitted = false;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.registerForm = this.formBuilder.group({
firstName: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', Validators.required],
acceptTerms: [false, Validators.requiredTrue]
});
}
// convenience getter for easy access to form fields
get f() { return this.registerForm.controls; }
onSubmit() {
this.submitted = true;
}
onReset() {
this.submitted = false;
this.registerForm.reset();
}
}
RegisterFormTooltipPipe (register-form-tooltip.pipe.ts)
import { Pipe, PipeTransform } from '@angular/core';
import { ValidationErrors } from '@angular/forms';
@Pipe({
name: 'regTooltip'
})
export class RegisterFormTooltipPipe implements PipeTransform {
transform(errors: ValidationErrors, control: string): string {
let message = '';
switch (errors && control) {
case 'firstName':
message = 'Name is required';
break;
case 'email':
if (errors.required) {
message = 'Email is required';
} else if (errors.email) {
message = 'Email must be a valid email address';
}
break;
case 'password':
if (errors.required) {
message = 'Password is required';
} else if (errors.minlength) {
message = 'Password must be at least 6 characters'
}
break;
}
console.log('error on : ', control, errors);
return message;
}
}
App Module file
import { RegisterFormTooltipPipe } from './registration-form-tooltip.pipe';
declarations: [
RegisterFormTooltipPipe
],
index.html scripts (Here we have used bootstrap)
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
FINAL UI

UNIT TEST CASES OF ANGULAR PIPE
import { RegisterFormTooltipPipe } from "./registration-form-tooltip.pipe";
describe('RegisterFormTooltipPipe', () => {
let pipe;
it('create an instance', () => {
expect(pipe).toBeTruthy();
});
beforeEach(() => {
pipe = new RegisterFormTooltipPipe();
});
describe('should run transform method', () => {
it('firstName should be required ', () => {
const message = pipe.transform({ required: true }, 'firstName');
expect(message).toBeDefined();
});
it('password should be required ', () => {
const message = pipe.transform({ required: true }, 'password');
expect(message).toBeDefined();
});
});
});
Visit Bootstrap blog:
https://getbootstrap.com/docs/4.5/getting-started/introduction/