Menu

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.