Skip to content

Angular Pipes Explained — Transform Data in Templates

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Angular Pipes Explained. We cover key concepts, practical examples, and best practices to help you master this topic.

Angular pipes transform data directly in your templates, letting you format dates, currencies, strings, and more without altering the underlying component properties.

What You'll Learn

  • How pipes work as template transformers
  • Built-in pipes: DatePipe, CurrencyPipe, DecimalPipe, PercentPipe, AsyncPipe
  • How to chain and parameterize pipes
  • How to create custom pipes
  • When to use pure vs impure pipes

Why It Matters

Pipes keep your templates clean and your component logic focused on data, not formatting. Instead of writing toLocaleDateString() in every component, you apply | date in the template and Angular handles the rest.

Real-World Use

The Durga Antivirus Pro dashboard displays scan timestamps formatted as "2 hours ago", threat counts with commas, and scan durations in minutes and seconds. Each uses a pipe applied directly in the template.

flowchart LR
    A[Raw Data] -->|pipe| B[Transformed Display]
    A -->|DatePipe| C["2026-06-28"]
    A -->|CurrencyPipe| D["$1,234.56"]
    A -->|CustomPipe| E["Custom Format"]
    style A fill:#f97316,color:#fff

Built-in Pipes

Angular ships with several useful pipes. They are available when you import CommonModule.

DatePipe

Formats date values:

import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";

@Component({
  selector: "app-date-demo",
  standalone: true,
  imports: [CommonModule],
  template: `
    <p>Short: {{ today | date:"short" }}</p>
    <p>Medium: {{ today | date:"medium" }}</p>
    <p>Custom: {{ today | date:"MMMM d, yyyy" }}</p>
    <p>Time ago: {{ today | date:"shortTime" }}</p>
  `
})
export class DateDemoComponent {
  today = new Date();
}

Expected output: Date displayed in short, medium, custom, and time-only formats. Short might be "6/28/26, 3:00 PM".

Pipe parameters follow the pipe name with a colon. The first parameter is the format string. DatePipe uses a subset of the Unicode date format patterns.

CurrencyPipe

Formats numbers as currency:

@Component({
  selector: "app-price-display",
  standalone: true,
  imports: [CommonModule],
  template: `
    <p>USD: {{ price | currency }}</p>
    <p>EUR: {{ price | currency:"EUR":"symbol":"1.2-2" }}</p>
    <p>JPY: {{ price | currency:"JPY":"symbol":"1.0-0" }}</p>
  `
})
export class PriceDisplayComponent {
  price = 1234.5678;
}

Expected output: "$1,234.57", "EUR1,234.57", "JPY1,235" depending on locale.

The parameters are: currency code, display format (symbol/code/narrow), and digit format. The digit format "1.2-2" means at least 1 integer digit, at least 2 and at most 2 decimal digits.

DecimalPipe and PercentPipe

Format numbers with decimal places or as percentages:

@Component({
  selector: "app-stats-display",
  standalone: true,
  imports: [CommonModule],
  template: `
    <p>Decimal: {{ largeNumber | number:"1.0-2" }}</p>
    <p>Percent: {{ fraction | percent:"1.2-2" }}</p>
  `
})
export class StatsDisplayComponent {
  largeNumber = 1234567.8912;
  fraction = 0.758;
}

Expected output: "1,234,567.89" and "75.80%".

DecimalPipe adds grouping separators and controls decimal places. PercentPipe multiplies the value by 100 and adds the percent sign.

AsyncPipe

Subscribes to observables and returns the latest value. It also handles unsubscription automatically:

import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
import { Observable, interval, map } from "rxjs";

@Component({
  selector: "app-clock",
  standalone: true,
  imports: [CommonModule],
  template: `
    <p>Current time: {{ time$ | async | date:"mediumTime" }}</p>
    <p>Count: {{ counter$ | async }}</p>
  `
})
export class ClockComponent {
  time$ = new Observable<string>(observer => {
    setInterval(() => observer.next(new Date().toISOString()), 1000);
  });

  counter$ = interval(1000).pipe(map(i => i + 1));
}

Expected output: A live clock that updates every second and a counter that increments.

You can chain pipes. Here async unwraps the observable value, then date formats it. The async pipe is essential for reactive Angular apps because it eliminates manual subscribe/unsubscribe code.

Custom Pipes

Create custom pipes when built-in pipes do not meet your needs:

import { Pipe, PipeTransform } from "@angular/core";

@Pipe({
  name: "truncate",
  standalone: true
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, maxLength = 50, suffix = "..."): string {
    if (!value) return "";
    if (value.length <= maxLength) return value;
    return value.substring(0, maxLength).trimEnd() + suffix;
  }
}

Usage:

@Component({
  selector: "app-description",
  standalone: true,
  imports: [TruncatePipe],
  template: `
    <p>{{ longText | truncate:30 }}</p>
    <p>{{ longText | truncate:20:" ---" }}</p>
  `
})
export class DescriptionComponent {
  longText = "This is a very long description that should be truncated in the view.";
}

Expected output: "This is a very long descrip..." and "This is a very long ---".

A custom pipe implements PipeTransform with a transform method. The first parameter is the input value; additional parameters come after colons in the template.

Pure vs Impure Pipes

Pipes are pure by default, meaning Angular only re-evaluates them when the input value reference changes:

@Pipe({
  name: "filterList",
  pure: false, // impure: re-evaluates on every change detection
  standalone: true
})
export class FilterListPipe implements PipeTransform {
  transform(items: string[], searchTerm: string): string[] {
    if (!items || !searchTerm) return items;
    return items.filter(item => item.toLowerCase().includes(searchTerm.toLowerCase()));
  }
}

Expected output: An impure pipe re-filters the list on every keystroke even if the array reference does not change.

Pure pipes are efficient because they only run when the input changes. Impure pipes run on every change detection cycle, which can hurt performance on large datasets. Use impure pipes only when necessary, like for live-search filtering.

Common Mistakes

  1. Forgetting to import CommonModule — Built-in pipes need CommonModule imported in your standalone component or NgModule.

  2. Using async pipe without an observable — Passing a plain value to async pipe returns null. Only use it with Observable, Promise, or Subject.

  3. Mutating pipe input — Pure pipes check reference equality. If you mutate an array and do not replace it, the pipe does not re-run.

  4. Chaining too many pipes — Each pipe runs on every change detection cycle. Chaining many pipes can impact performance.

  5. Ignoring locale settings — Currency and date formats depend on the LOCALE_ID. Set it in the app configuration for consistent formatting.

Practice Questions

  1. What is an Angular pipe? A pipe is a template operator that transforms data before display without changing the original value.

  2. How do you pass parameters to a pipe? Append a colon and the parameter: value | pipeName:"param1":"param2".

  3. What is the difference between pure and impure pipes? Pure pipes run only when the input reference changes. Impure pipes run on every change detection cycle.

  4. What does the async pipe do? It subscribes to an observable or promise and returns the latest emitted value. It also handles unsubscription.

  5. How do you create a custom pipe? Create a class with @Pipe({ name: "pipeName" }) that implements PipeTransform.

Challenge

Build a custom searchFilter pipe that filters an array of objects by multiple fields (name, email, and role). Use it with an input field and display the filtered results. The pipe should be impure to react to input changes.

FAQ

Can I use pipes with signals in Angular 17+?

Yes, pipes work with signals. Use {{ mySignal() | date }} to format signal values.

What pipes are available in CommonModule?

DatePipe, CurrencyPipe, DecimalPipe, PercentPipe, AsyncPipe, JsonPipe, KeyValuePipe, SlicePipe, I18nPluralPipe, I18nSelectPipe.

Does Angular have a built-in sorting pipe?

No, because sorting depends on the specific data structure. Create a custom pipe or sort in the component.

Why did Angular remove the FilterPipe from built-ins?

Angular recommends moving filtering logic to the component for performance. Impure pipes are inefficient for large arrays.

Can a pipe be stateful?

Yes, impure pipes can hold state, but avoid it because they are re-created on every change detection cycle.

Mini Project

Build a FileSizePipe that converts bytes to human-readable format (KB, MB, GB). Then build a FileExplorerComponent that lists files with their names, sizes (using the pipe), and modified dates (using DatePipe). Add a search input that filters the list using a custom filter pipe.

@Pipe({
  name: "fileSize",
  standalone: true
})
export class FileSizePipe implements PipeTransform {
  transform(bytes: number): string {
    if (bytes === 0) return "0 B";
    const units = ["B", "KB", "MB", "GB", "TB"];
    const k = 1024;
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + units[i];
  }
}

What's Next

Continue with services and Dependency Injection:

Angular Services, Angular DI, Angular Components

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro