Back to Curriculum

Pipes and Directives

📚 Lesson 7 of 8 ⏱️ 40 min

Pipes and Directives

40 min

Pipes are simple functions that transform data in Angular templates. They take input data and return formatted output, making data presentation cleaner and more maintainable. Pipes are pure by default (stateless, no side effects), meaning they only recompute when input changes. Angular provides many built-in pipes for common transformations like dates, currency, numbers, JSON, and more. Understanding pipes helps you format data in templates without cluttering component logic.

Built-in pipes include `date` (format dates), `currency` (format currency), `decimal`/`number` (format numbers), `percent` (format percentages), `uppercase`/`lowercase` (case transformation), `json` (debugging object display), `slice` (array/string slicing), `async` (unwraps Observables/Promises), and `i18nSelect`/`i18nPlural` (internationalization). Pipes can be chained using the pipe operator (`|`), enabling multiple transformations. Understanding built-in pipes helps you format data effectively without writing custom code.

Custom pipes enable you to create domain-specific transformations. Pipes are classes decorated with `@Pipe()` that implement the `PipeTransform` interface with a `transform()` method. Custom pipes can accept parameters, be pure (default, recomputes only on input change) or impure (recomputes on every change detection), and can be stateful. Common use cases include formatting phone numbers, truncating text, filtering arrays, and custom date formats. Understanding custom pipes helps you create reusable data transformations.

Directives extend HTML with custom behavior and appearance. There are three types: components (directives with templates), structural directives (modify DOM structure: `*ngIf`, `*ngFor`, `*ngSwitch`), and attribute directives (modify element appearance/behavior). Structural directives use asterisk syntax (`*ngIf`) and can add/remove DOM elements. Attribute directives change element appearance or behavior without modifying structure. Understanding directives helps you create reusable UI behaviors and extend HTML functionality.

Custom directives enable you to create reusable behaviors. Attribute directives are classes decorated with `@Directive()` that can access and modify the host element. They can respond to events, modify styles, add/remove classes, and interact with the DOM. Structural directives are more complex, requiring a `ViewContainerRef` and `TemplateRef` to create/remove views. Understanding custom directives helps you build reusable UI behaviors and extend Angular's functionality.

Best practices include using pipes for data formatting (not business logic), keeping pipes pure when possible (better performance), using built-in pipes when they meet your needs, creating custom pipes for reusable transformations, and using directives for reusable behaviors. Pipes and directives should be focused, testable, and well-documented. Understanding pipes and directives enables you to build maintainable, reusable Angular applications.

Key Concepts

  • Pipes transform data in templates for display.
  • Built-in pipes provide common formatting (date, currency, etc.).
  • Custom pipes enable domain-specific transformations.
  • Directives extend HTML with custom behavior and appearance.
  • Structural directives modify DOM structure; attribute directives modify behavior.

Learning Objectives

Master

  • Using built-in pipes for data formatting
  • Creating custom pipes for specific transformations
  • Understanding structural and attribute directives
  • Creating custom directives for reusable behaviors

Develop

  • Template transformation and presentation thinking
  • Understanding reusable UI behavior patterns
  • Designing maintainable template code

Tips

  • Use pipes for data formatting, not business logic.
  • Keep pipes pure when possible for better performance.
  • Use built-in pipes when they meet your needs.
  • Create custom directives for reusable UI behaviors.

Common Pitfalls

  • Using impure pipes unnecessarily, causing performance issues.
  • Putting business logic in pipes instead of services.
  • Not understanding directive types, using wrong directive for the task.
  • Creating directives that are too complex, making them hard to maintain.

Summary

  • Pipes transform data in templates for display.
  • Built-in pipes provide common formatting capabilities.
  • Custom pipes enable domain-specific transformations.
  • Directives extend HTML with custom behavior.
  • Understanding pipes and directives enables reusable, maintainable templates.

Exercise

Create custom pipes and directives.

// custom.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'capitalize'
})
export class CapitalizePipe implements PipeTransform {
  transform(value: string): string {
    if (!value) return value;
    return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
  }
}

@Pipe({
  name: 'filter'
})
export class FilterPipe implements PipeTransform {
  transform(items: any[], searchText: string): any[] {
    if (!items || !searchText) return items;
    
    searchText = searchText.toLowerCase();
    return items.filter(item => 
      item.name.toLowerCase().includes(searchText) ||
      item.email.toLowerCase().includes(searchText)
    );
  }
}

// highlight.directive.ts
import { Directive, ElementRef, Input, OnInit } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective implements OnInit {
  @Input() appHighlight = 'yellow';
  @Input() defaultColor = 'transparent';

  constructor(private el: ElementRef) {}

  ngOnInit() {
    this.highlight(this.defaultColor);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }

  onMouseEnter() {
    this.highlight(this.appHighlight);
  }

  onMouseLeave() {
    this.highlight(this.defaultColor);
  }
}

// user-list-with-pipes.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-user-list-with-pipes',
  template: `
    <div class="user-list">
      <input [(ngModel)]="searchText" placeholder="Search users...">
      
      <div *ngFor="let user of users | filter:searchText" 
           class="user-item"
           appHighlight="lightblue"
           (mouseenter)="onMouseEnter()"
           (mouseleave)="onMouseLeave()">
        <h3>{{ user.name | capitalize }}</h3>
        <p>Email: {{ user.email }}</p>
        <p>Age: {{ user.age }}</p>
        <p>Created: {{ user.created | date:'medium' }}</p>
        <p>Salary: {{ user.salary | currency:'USD' }}</p>
      </div>
    </div>
  `,
  styles: [`
    .user-list {
      padding: 20px;
    }
    input {
      width: 100%;
      padding: 8px;
      margin-bottom: 15px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    .user-item {
      border: 1px solid #ddd;
      padding: 10px;
      margin: 10px 0;
      border-radius: 5px;
      transition: background-color 0.3s;
    }
  `]
})
export class UserListWithPipesComponent {
  searchText = '';
  users = [
    { name: 'alice', email: 'alice@example.com', age: 25, created: new Date(), salary: 50000 },
    { name: 'bob', email: 'bob@example.com', age: 30, created: new Date(), salary: 60000 },
    { name: 'charlie', email: 'charlie@example.com', age: 35, created: new Date(), salary: 70000 }
  ];

  onMouseEnter() {
    // This will be handled by the directive
  }

  onMouseLeave() {
    // This will be handled by the directive
  }
}

Code Editor

Output