Reader Stacks

Angular Directives, ng-container, and Custom Pipes

Directives change a DOM element's behavior or appearance, ng-container groups elements without adding an extra wrapper, and pipes transform a value for display — three distinct Angular template tools.

Directives, ng-container, and pipes are three distinct Angular template features that are easy to conflate when first learning the framework — each solves a genuinely different problem.

What a directive actually is

A directive is a class that attaches behavior to a DOM element — Angular ships with built-in structural directives (*ngIf, *ngFor, which add or remove elements from the DOM) and attribute directives (ngClass, ngStyle, which change an existing element's appearance or behavior without adding/removing it).

Creating a custom attribute directive

@Directive({
    selector: '[appHighlight]'
})
export class HighlightDirective {
    constructor(private el: ElementRef) {}

    @HostListener('mouseenter') onMouseEnter() {
        this.el.nativeElement.style.backgroundColor = 'yellow';
    }

    @HostListener('mouseleave') onMouseLeave() {
        this.el.nativeElement.style.backgroundColor = '';
    }
}

Hover over this text

This directive attaches hover-highlight behavior to any element carrying the appHighlight attribute — reusable across as many elements as needed without duplicating the mouseenter/mouseleave logic on each one.

What ng-container is for


    

{{ user.name }}

{{ user.email }}

ng-container groups multiple elements under a single structural directive (like *ngIf) without rendering an actual wrapping DOM element — using a

instead would work functionally the same, but adds an extra div to the rendered HTML that ng-container avoids, which matters when that extra wrapper would break CSS flex/grid layout or table structure.

Using ng-container to combine two structural directives


    
  • {{ item.name }}
  • Angular doesn't allow two structural directives (like *ngFor and *ngIf) on the same element directly — nesting them across an ng-container and the actual element is the standard workaround.

    What a pipe does

    {{ user.createdAt | date:'mediumDate' }}
    {{ price | currency:'USD' }}
    {{ description | uppercase }}

    A pipe transforms a value for display in the template without changing the underlying data — date, currency, and uppercase are all built in.

    Creating a custom pipe

    @Pipe({ name: 'truncate' })
    export class TruncatePipe implements PipeTransform {
        transform(value: string, limit: number = 50): string {
            return value.length > limit ? value.substring(0, limit) + '...' : value;
        }
    }
    {{ post.body | truncate:100 }}

    A custom pipe follows the same pattern as the built-in ones — implement PipeTransform, register it in a module (or mark it standalone: true in newer Angular versions), and use it in a template with the same pipe (|) syntax.

    How these three fit together

    A directive changes behavior or structure, ng-container is a structural tool for organizing directives without adding markup, and a pipe transforms a displayed value — a single template commonly uses all three together, as in the ng-container example above combining a structural directive with a value that could just as easily be piped for formatting.