Custom Directives and Pipes
Custom Directives and Pipes
Building Custom Directives
Custom directives let you attach reusable DOM behavior to any element — without creating a full component.
A directive that highlights an element on hover:
// highlight.directive.ts
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() appHighlight: string = '#f0f4ff';
constructor(private el: ElementRef) {}
@HostListener('mouseenter') onMouseEnter(): void {
this.el.nativeElement.style.backgroundColor = this.appHighlight;
}
@HostListener('mouseleave') onMouseLeave(): void {
this.el.nativeElement.style.backgroundColor = '';
}
}
<!-- Use it on any element -->
<li *ngFor="let course of courses" [appHighlight]="'#e8f5e9'">
{{ course.name }}
</li>
A directive that auto-focuses an input on load:
// autofocus.directive.ts
import { Directive, ElementRef, OnInit } from '@angular/core';
@Directive({ selector: '[appAutofocus]' })
export class AutofocusDirective implements OnInit {
constructor(private el: ElementRef) {}
ngOnInit(): void {
setTimeout(() => this.el.nativeElement.focus(), 0);
}
}
<input appAutofocus placeholder="This gets focused automatically">
Building Custom Pipes
Custom pipes let you transform data for display in any way you need — beyond the built-in date, currency, and uppercase pipes.
A pipe that truncates long text:
// truncate.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'truncate' })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit: number = 100, trail: string = '...'): string {
if (!value) return '';
return value.length > limit ? value.substring(0, limit) + trail : value;
}
}
<p>{{ course.description | truncate:80 }}</p>
<p>{{ course.description | truncate:50:'… read more' }}</p>
A pipe that filters a list:
// filter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'filter' })
export class FilterPipe implements PipeTransform {
transform(items: any[], field: string, value: string): any[] {
if (!items || !value) return items;
return items.filter(item =>
item[field]?.toLowerCase().includes(value.toLowerCase())
);
}
}
<li *ngFor="let course of courses | filter:'level':'Beginner'">
{{ course.name }}
</li>
Pure vs Impure Pipes
By default, Angular only re-runs a pipe when its input reference changes — this is a pure pipe, and it's fast. An impure pipe reruns on every change-detection cycle, which is slower but necessary when the pipe reads from mutable data such as arrays or objects.
// An impure pipe — use sparingly
@Pipe({ name: 'liveFilter', pure: false })
export class LiveFilterPipe implements PipeTransform {
transform(items: any[], searchTerm: string): any[] {
if (!searchTerm) return items;
return items.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}
}
Only make a pipe impure when you genuinely need it. Impure pipes that do heavy computation will slow your app down noticeably.










