LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 3816 min read

Custom Directives & Pipes

Build your own reusable DOM behavior with a custom attribute directive, and your own display transformation with a custom pipe.

Building a Custom Attribute Directive

The @Directive decorator, combined with an ElementRef reference to the host element, lets you build reusable DOM behavior — like a highlight-on-hover effect.

import { Directive, ElementRef, HostListener, inject } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
private el = inject(ElementRef);
@HostListener('mouseenter')
onMouseEnter() {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
@HostListener('mouseleave')
onMouseLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
}
<p appHighlight>Hover over me!</p>

Accepting Configuration via @Input

A directive can accept an @Input(), including one sharing its name with the directive's own selector, for configuration passed directly through the attribute.

@Directive({ selector: '[appHighlight]', standalone: true })
export class HighlightDirective {
private el = inject(ElementRef);
@Input('appHighlight') color = 'yellow';
@HostListener('mouseenter')
onMouseEnter() {
this.el.nativeElement.style.backgroundColor = this.color;
}
}
<p [appHighlight]="'lightblue'">Hover over me!</p>

Building a Custom Pipe

The @Pipe decorator, combined with a transform method, lets you build your own reusable display transformation.

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
standalone: true,
})
export class TruncatePipe implements PipeTransform {
transform(value: string, maxLength: number = 50): string {
if (value.length <= maxLength) return value;
return value.slice(0, maxLength) + '…';
}
}
<p>{{ post.body | truncate:100 }}</p>

Pure vs Impure Pipes

By default, a pipe is pure — it only re-runs when its input reference changes, exactly like a memoized function. Marking it impure (pure: false) makes it re-run on every change detection cycle, which is rarely needed and can hurt performance if overused.

@Pipe({ name: 'filterActive', standalone: true, pure: false })
export class FilterActivePipe implements PipeTransform {
transform(items: Item[]): Item[] {
return items.filter(item => item.active);
}
}
Prefer a Pure Pipe or a computed() Signal

An impure pipe re-running on every check can be expensive for larger arrays — often, deriving the filtered result with a computed() signal in the component is a better-performing alternative.

FAQs

When the behavior is purely about modifying an existing element (styling, event handling) rather than rendering its own distinct markup — that's exactly what a component is for instead.

Yes — transform(value, param1, param2) accepts as many parameters as needed, each separated by a colon in the template: value | myPipe:param1:param2.

Summary

Custom directives extend DOM behavior, and custom pipes extend display transformations — both following the same decorator-based pattern as components and services. Next, you'll look at TypeScript patterns specific to Angular.

Next Lesson →

TypeScript with Angular