| |

Angular 11 🅰️ Built-in Pipes and Data Formatting

A pipe transforms a value for display without changing the underlying data. You write {{ value | pipeName }} in a template, and Angular passes value through the pipe’s transform method, rendering the result. Pipes are how Angular handles formatting — dates, currency, percentages, case conversion, and more — without cluttering the component class with presentation logic. Angular ships with a set of built-in pipes that cover the common cases, and you can write your own when they don’t.

Key point: Pipes are pure display transformations. They don’t mutate the value they receive — they return a new value. They run on every change detection cycle (unless pure, which most are), so keep them cheap. Built-in pipes cover dates, numbers, currency, percent, case, JSON, and slices — learn them and you’ll rarely need to format manually.


What a pipe is

A pipe is a class with a transform method, applied in a template with the | operator.

{{ value | pipeName }}

Angular calls pipeName.transform(value) and renders the result. Chains work too:

{{ value | pipeA | pipeB }}

The output of pipeA becomes the input to pipeB.

Where pipes appear:

  • Interpolation — {{ price | currency }}
  • Property binding — [title]="name | uppercase"
  • Attribute binding — [attr.aria-label]="label | lowercase"
  • Inside structural blocks — @if (name | uppercase) { ... }

Pipes are template-only. You can’t use them in the component class without importing them and calling transform manually.

What pipes are for:

  • Formatting dates, numbers, currency
  • Converting case
  • Slicing arrays and strings
  • Rendering JSON for debugging
  • Localizing values

What pipes are not for:

  • Business logic
  • State mutation
  • Heavy computation (they run on every CD cycle)
  • Anything that needs to be in the class

Why pipes exist: Without them, you’d format every value in the component class and expose a formatted string. That clutters the class with presentation concerns. Pipes keep the class focused on data and the template focused on display. It’s the same idea as formatting functions in other frameworks — reusable, declarative, testable.


Pure vs impure pipes

Most built-in pipes are pure — they run only when their input changes.

{{ user.name | uppercase }}

The uppercase pipe runs when user.name changes, not on every CD cycle. That’s efficient.

Impure pipes run on every change detection cycle, regardless of input.

@Pipe({ name: 'impureExample', pure: false })

Impure pipes are necessary when the input’s contents change but the reference doesn’t — like a mutable array. They’re expensive and should be rare.

Built-in pipes and purity:

PipePure?
date
uppercase / lowercase
currency
number
percent
json
slice
async❌ (impure)
keyvalue✅ (since v6)

The async pipe is impure by nature — it subscribes to observables and promises and updates when they emit. It’s the one built-in pipe that runs on every CD cycle.

Why purity matters: Pure pipes are cached — Angular only re-runs them when the input changes by reference. Impure pipes run constantly. For anything expensive, pure is the only reasonable choice. The async pipe is the exception because its whole job is to react to asynchronous emissions — it can’t be pure.


date — formatting dates

The date pipe formats a Date, a number (milliseconds since epoch), or an ISO string.

{{ today | date }}
{{ today | date:'short' }}
{{ today | date:'fullDate' }}
{{ today | date:'yyyy-MM-dd' }}
{{ today | date:'HH:mm:ss' }}

Common format options:

FormatExample output
'short'6/15/24, 2:30 PM
'medium'Jun 15, 2024, 2:30:00 PM
'long'June 15, 2024 at 2:30:00 PM GMT+2
'full'Saturday, June 15, 2024 at 2:30:00 PM GMT+02:00
'shortDate'6/15/24
'mediumDate'Jun 15, 2024
'longDate'June 15, 2024
'fullDate'Saturday, June 15, 2024
'shortTime'2:30 PM
'mediumTime'2:30:00 PM

Custom patterns:

SymbolMeaning
yyyy4-digit year
MM2-digit month
dd2-digit day
HH24-hour
hh12-hour
mmMinutes
ssSeconds
aAM/PM
EEEEFull weekday name
{{ today | date:'EEEE, MMMM d, yyyy' }}   <!-- Saturday, June 15, 2024 -->
{{ today | date:'h:mm a' }}                <!-- 2:30 PM -->

Timezone parameter:

{{ today | date:'short':'+0200' }}
{{ today | date:'short':'UTC' }}

Locale: The date pipe uses the app’s locale (set via LOCALE_ID). Adding registerLocaleData in your app config enables other locales.

Common mistakes:

  • Passing a string that isn’t ISO — date expects a parseable value
  • Forgetting that Date is mutable and doesn’t trigger pure pipes when mutated
  • Assuming the pipe formats in local time without checking the timezone

Why the date pipe matters: Formatting dates manually is error-prone. Timezones, leap years, locale differences — all handled by the pipe. Use it. The only alternative is doing it in the class, which duplicates logic and misses localization.


number, percent, and currency

Three pipes for numeric formatting.

number — formats a number with locale-aware separators and decimal digits.

{{ 1234.5678 | number }}                <!-- 1,234.568 -->
{{ 1234.5678 | number:'1.2-2' }}        <!-- 1,234.57 -->
{{ 1234.5678 | number:'3.0-0' }}        <!-- 1,235 -->

The format string is {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}.

percent — multiplies by 100 and formats as a percentage.

{{ 0.25 | percent }}                    <!-- 25% -->
{{ 0.2534 | percent:'1.1-1' }}          <!-- 25.3% -->

Same format string as number for the fractional part.

currency — formats as a currency value.

{{ 99.99 | currency }}                  <!-- $99.99 -->
{{ 99.99 | currency:'EUR' }}            <!-- €99.99 -->
{{ 99.99 | currency:'EUR':'symbol':'1.0-0' }}  <!-- €100 -->
{{ 99.99 | currency:'USD':'code' }}     <!-- USD 99.99 -->

Parameters for all three: The format is {minInt}.{minFrac}-{maxFrac}, plus display options for currency.

Common options:

PipeParameterEffect
number'1.2-2'Min 1 integer, 2–2 fraction
percent'1.0-0'Whole percent, no decimals
currency'EUR'Currency code
currency'symbol' / 'code'Display style

Common mistakes:

  • Forgetting percent multiplies by 100 — 0.25 becomes 25%, not 0.25%
  • Assuming $ is universal — pass a currency code for other locales
  • Over-formatting — the default is usually fine

Why these pipes are essential: Money, percentages, and numbers are everywhere in dashboards and reports. Formatting them consistently across an app is a real problem. The pipes handle locale, thousands separators, and symbol placement automatically. Use them and stop writing .toFixed(2) in templates.


uppercase, lowercase, and titlecase

Three case-conversion pipes.

{{ 'hello' | uppercase }}               <!-- HELLO -->
{{ 'HELLO' | lowercase }}               <!-- hello -->
{{ 'hello world' | titlecase }}         <!-- Hello World -->

titlecase capitalizes the first letter of each word and lowercases the rest. Useful for names, headings, and labels.

Note: titlecase was removed in Angular 8 and re-added in later versions. If you’re on an older version, check whether it’s available.

Common mistakes:

  • Using uppercase for user-facing data — it’s fine for labels, harsh for names
  • Assuming titlecase handles names with particles — "van der Berg" becomes "Van Der Berg"

Why case pipes are useful: Consistent capitalization is a small but real design concern. Doing it in the class means writing helper functions or doing it manually. The pipes handle it declaratively.


json — debugging output

The json pipe renders a value as JSON.

{{ user | json }}

Output:

{ "id": 1, "name": "Alice" }

Optional spacing:

{{ user | json:2 }}                     <!-- indented 2 spaces -->

When to use it: Development, debugging, quick inspection. Not for production UI — JSON is rarely the right display for end users.

Common mistakes:

  • Leaving json in production templates
  • Using it for objects that contain circular references — it throws

Why json exists: It’s a debugging aid. When you need to see what a value looks like, json prints it cleanly. In production, replace it with proper formatting.


slice — substrings and subarrays

The slice pipe extracts part of a string or array.

{{ 'Hello World' | slice:0:5 }}         <!-- Hello -->
{{ [1, 2, 3, 4, 5] | slice:1:3 }}       <!-- [2, 3] -->
{{ 'Hello World' | slice:-5 }}          <!-- World -->

Arguments: slice:start:end — same as Array.prototype.slice and String.prototype.slice. Negative indices count from the end.

Common patterns:

  • Truncate long strings — {{ text | slice:0:100 }}
  • Show top N items — {{ items | slice:0:5 }}
  • Show last N items — {{ items | slice:-5 }}

Common mistakes:

  • Forgetting that slice doesn’t add an ellipsis — you’ll need to add one yourself
  • Using it on null or undefined — it throws

Why slice is handy: Truncation is common in lists and previews. The pipe handles it declaratively, with the same semantics as JavaScript’s built-in slice. No helper functions needed.


async — unwrapping observables and promises

The async pipe subscribes to an observable or promise and renders the latest value.

{{ user$ | async }}

For an observable, the pipe subscribes on render and unsubscribes on destroy. For a promise, it renders the resolved value.

With optional chaining:

{{ (user$ | async)?.name }}

The parentheses matter — without them, ?. would apply to the observable, not the value.

With @if:

@if (user$ | async; as user) {
  <p>{{ user.name }}</p>
}

The as clause binds the emitted value to a local variable — cleaner than repeated async calls and avoids multiple subscriptions.

Why multiple async calls are a problem: Each | async creates a new subscription. Two | async on the same observable means two subscriptions — potentially two HTTP calls. Use as or shareReplay.

Async pipe and change detection: The async pipe is impure. It runs on every CD cycle to check for new emissions. That’s cheap, but it’s why the pipe exists as a separate mechanism rather than a pure transformation.

Common mistakes:

  • Multiple | async on the same observable
  • Forgetting to unsubscribe — the async pipe handles it automatically, so manual subscribe is rarely needed
  • Using async on a promise that rejects — the pipe doesn’t handle errors; you need catchError

Why the async pipe is the recommended pattern: It manages subscription lifecycle for you. Without it, you’d subscribe in ngOnInit and unsubscribe in ngOnDestroy — the source of countless memory leaks. The async pipe handles both, and it triggers change detection automatically. For any observable rendered in a template, it’s the tool.


keyvalue — iterating over objects

The keyvalue pipe converts an object to an array of { key, value } pairs, suitable for @for.

@for (entry of user | keyvalue; track entry.key) {
  <p>{{ entry.key }}: {{ entry.value }}</p>
}

Output:

id: 1
name: Alice
email: alice@example.com

Ordering: By default, sorted by key. You can pass a comparator:

@for (entry of user | keyvalue:compareFn; track entry.key) {

Common mistakes:

  • Using it on arrays — keyvalue is for objects
  • Expecting insertion order — the default sort is alphabetical

Why keyvalue exists: @for iterates arrays, not objects. keyvalue bridges that gap, letting you render object properties declaratively. It’s the standard way to display maps and dictionaries in templates.


Using pipes with standalone components

In standalone components, pipes must be imported like directives.

import { Component } from '@angular/core';
import { DatePipe, CurrencyPipe, UpperCasePipe } from '@angular/common';

@Component({
  selector: 'app-invoice',
  standalone: true,
  imports: [DatePipe, CurrencyPipe, UpperCasePipe],
  template: `
    <h2>{{ customer | uppercase }}</h2>
    <p>Date: {{ createdAt | date:'mediumDate' }}</p>
    <p>Total: {{ amount | currency:'EUR' }}</p>
  `
})
export class InvoiceComponent {
  customer = 'alice';
  createdAt = new Date();
  amount = 99.99;
}

Each pipe is imported individually. In older module-based Angular, CommonModule provided all of them at once.

Common mistakes:

  • Forgetting to import the pipe — template error
  • Importing CommonModule when you only need a couple of pipes — bloats the bundle

Which pipes are in CommonModule:

PipePackage
DatePipe@angular/common
CurrencyPipe@angular/common
DecimalPipe (number)@angular/common
PercentPipe@angular/common
UpperCasePipe@angular/common
LowerCasePipe@angular/common
TitleCasePipe@angular/common
JsonPipe@angular/common
SlicePipe@angular/common
AsyncPipe@angular/common
KeyValuePipe@angular/common

Why individual imports matter: Standalone components import only what they use. Importing CommonModule pulls in every pipe and directive — more code shipped than needed. Individual imports keep the bundle tight.


A full example

An invoice component using several built-in pipes.

import { Component } from '@angular/core';
import {
  DatePipe, CurrencyPipe, PercentPipe,
  UpperCasePipe, SlicePipe, AsyncPipe
} from '@angular/common';
import { Observable, of } from 'rxjs';

interface LineItem {
  sku: string;
  name: string;
  qty: number;
  price: number;
}

@Component({
  selector: 'app-invoice',
  standalone: true,
  imports: [DatePipe, CurrencyPipe, PercentPipe, UpperCasePipe, SlicePipe, AsyncPipe],
  template: `
    <h2>{{ customerName | uppercase }}</h2>
    <p>Issued: {{ issuedAt | date:'fullDate' }}</p>

    <ul>
      @for (item of items; track item.sku) {
        <li>
          {{ item.name | slice:0:20 }} —
          {{ item.qty }} × {{ item.price | currency:'EUR' }}
        </li>
      }
    </ul>

    <p>Subtotal: {{ subtotal | currency:'EUR' }}</p>
    <p>Tax: {{ taxRate | percent }}</p>
    <p><strong>Total: {{ total | currency:'EUR' }}</strong></p>

    <p>Status: {{ status$ | async }}</p>
  `
})
export class InvoiceComponent {
  customerName = 'alice johnson';
  issuedAt = new Date();
  items: LineItem[] = [
    { sku: 'A1', name: 'Wireless Keyboard Deluxe Edition', qty: 1, price: 79.99 },
    { sku: 'B2', name: 'USB-C Hub', qty: 2, price: 39.5 },
  ];
  taxRate = 0.22;
  status$: Observable<string> = of('paid');

  get subtotal(): number {
    return this.items.reduce((sum, i) => sum + i.qty * i.price, 0);
  }

  get total(): number {
    return this.subtotal * (1 + this.taxRate);
  }
}

Each pipe does one job: uppercase for the heading, date for the date, slice to truncate item names, currency for money, percent for the tax rate, and async for the status observable.

What this shows: Pipes handle display formatting cleanly, while the component class focuses on data. The getters compute values; the pipes format them.

Why this pattern scales: Every template can reuse pipes without duplicating formatting logic. Change the currency code once, and every currency pipe updates. Change the date format, and every date pipe updates. That consistency is the point.


Complete Example Session

# ============================================
# PART 1: GENERATE A COMPONENT
# ============================================

ng generate component pipe-demo
# [ CREATE src/app/pipe-demo/pipe-demo.component.ts ]
# [ CREATE src/app/pipe-demo/pipe-demo.component.html ]
# [ CREATE src/app/pipe-demo/pipe-demo.component.css ]

# ============================================
# PART 2: WRITE THE CLASS
# ============================================

cat > src/app/pipe-demo/pipe-demo.component.ts << 'EOF'
import { Component } from '@angular/core';
import {
  DatePipe, UpperCasePipe, LowerCasePipe, TitleCasePipe,
  CurrencyPipe, PercentPipe, DecimalPipe, JsonPipe,
  SlicePipe, KeyValuePipe
} from '@angular/common';

@Component({
  selector: 'app-pipe-demo',
  standalone: true,
  imports: [
    DatePipe, UpperCasePipe, LowerCasePipe, TitleCasePipe,
    CurrencyPipe, PercentPipe, DecimalPipe, JsonPipe,
    SlicePipe, KeyValuePipe
  ],
  templateUrl: './pipe-demo.component.html',
  styleUrl: './pipe-demo.component.css'
})
export class PipeDemoComponent {
  today = new Date();
  name = 'alice johnson';
  price = 1234.5678;
  rate = 0.075;
  text = 'The quick brown fox jumps over the lazy dog';
  user = { id: 1, name: 'Alice', role: 'admin' };
}
EOF

# ============================================
# PART 3: WRITE THE TEMPLATE
# ============================================

cat > src/app/pipe-demo/pipe-demo.component.html << 'EOF'
<h1>Pipes Demo</h1>

<h2>Date</h2>
<p>{{ today | date:'short' }}</p>
<p>{{ today | date:'fullDate' }}</p>
<p>{{ today | date:'yyyy-MM-dd HH:mm' }}</p>

<h2>Case</h2>
<p>{{ name | uppercase }}</p>
<p>{{ name | lowercase }}</p>
<p>{{ name | titlecase }}</p>

<h2>Numbers</h2>
<p>{{ price | number }}</p>
<p>{{ price | number:'1.2-2' }}</p>
<p>{{ price | currency:'EUR' }}</p>
<p>{{ rate | percent }}</p>

<h2>Text</h2>
<p>{{ text | slice:0:20 }}...</p>

<h2>Object</h2>
<p>{{ user | json }}</p>

<h2>Keyvalue</h2>
<ul>
  @for (entry of user | keyvalue; track entry.key) {
    <li>{{ entry.key }}: {{ entry.value }}</li>
  }
</ul>
EOF

# ============================================
# PART 4: SERVE
# ============================================

ng serve
# [ Local:   http://localhost:4200/ ]

# ============================================
# PART 5: TEST EACH PIPE
# ============================================

# In the browser, verify:
# - Date formats differ
# - Case conversions work
# - Currency shows EUR
# - Percent multiplies by 100
# - Slice truncates
# - Json shows raw object
# - Keyvalue iterates properties

# ============================================
# PART 6: CHAIN PIPES
# ============================================

cat > src/app/pipe-demo/pipe-demo.component.html << 'EOF'
<h1>Pipes Demo</h1>

<!-- Chain -->
<p>{{ name | titlecase | slice:0:10 }}</p>

<!-- In property binding -->
<img [alt]="text | slice:0:30">

<!-- In attribute binding -->
<a [attr.aria-label]="name | titlecase">Link</a>

<!-- In a block -->
@if (text | slice:0:5; as prefix) {
  <p>Prefix: {{ prefix }}</p>
}
EOF

Every built-in pipe is exercised — dates, case, numbers, currency, percent, slice, JSON, and keyvalue. Chaining and use inside blocks are demonstrated.

Why this exercise works: It covers all the built-in pipes in one template, so you see them side by side. That’s how you learn which pipe does what and how parameters work. Run it once and the vocabulary sticks.


Quick Reference

All Built-in Pipes

PipePurposeExample
dateFormat dates{{ d | date:'short' }}
uppercaseALL CAPS{{ s | uppercase }}
lowercaseall lowercase{{ s | lowercase }}
titlecaseTitle Case{{ s | titlecase }}
numberNumeric formatting{{ n | number:'1.2-2' }}
percentPercentage{{ n | percent }}
currencyCurrency{{ n | currency:'EUR' }}
jsonJSON output{{ o | json }}
sliceSubstring / subarray{{ s | slice:0:5 }}
asyncUnwrap observable/promise{{ o$ | async }}
keyvalueObject → key/value array@for (e of o | keyvalue; track e.key)

Date Formats

TokenOutput
'short'6/15/24, 2:30 PM
'medium'Jun 15, 2024, 2:30:00 PM
'long'June 15, 2024 at 2:30:00 PM
'full'Saturday, June 15, 2024 at 2:30:00 PM
'shortDate'6/15/24
'mediumDate'Jun 15, 2024
'longDate'June 15, 2024
'fullDate'Saturday, June 15, 2024
'shortTime'2:30 PM
'mediumTime'2:30:00 PM

Custom Date Symbols

SymbolMeaning
yyyyYear (4-digit)
yyYear (2-digit)
MMMMFull month
MMMShort month
MM2-digit month
dd2-digit day
EEEEFull weekday
HH24-hour
hh12-hour
mmMinutes
ssSeconds
aAM/PM

Number Format String

FormatMeaning
'1.0-0'Min 1 int, 0 fraction
'1.2-2'2 decimals fixed
'3.0-0'Min 3 int, no decimals
'1.1-3'1–3 fraction digits

Pattern: {minInt}.{minFrac}-{maxFrac}

Currency Options

ParameterEffect
'EUR'Currency code
'symbol' (default)€99.99
'code'EUR 99.99
'symbol-narrow'$ instead of US$
'1.0-0'Format digits

Case Pipes

PipeInputOutput
uppercase'hello''HELLO'
lowercase'HELLO''hello'
titlecase'hello world''Hello World'

slice Semantics

ExampleResult
'Hello' | slice:0:3'Hel'
'Hello' | slice:2'llo'
'Hello' | slice:-3'llo'
[1,2,3,4] | slice:1:3[2,3]

async Pipe

UseNotes
{{ obs$ | async }}Subscribes, renders latest
{{ (obs$ | async)?.name }}Parentheses matter
@if (obs$ | async; as v)Binds to local variable
{{ promise | async }}Renders resolved value

keyvalue Pipe

ObjectOutput
{a: 1, b: 2}[{key: 'a', value: 1}, {key: 'b', value: 2}]
Sorted by keyDefault
Comparatorkeyvalue:fn

Pipes and Standalone

NeedImport
DatePipe@angular/common
CurrencyPipe@angular/common
UpperCasePipe@angular/common
All pipesCommonModule (module-based)

Purity

PipePure
date
uppercase / lowercase / titlecase
number / percent / currency
json
slice
keyvalue
async

Best Practices

Do This:

// Use built-in pipes for formatting
{{ price | currency:'EUR' }}                     // ✅

// Chain pipes for common transformations
{{ name | titlecase | slice:0:10 }}              // ✅

// Import pipes individually in standalone components
imports: [DatePipe, CurrencyPipe]                // ✅

// Use async with `as` to avoid multiple subscriptions
@if (user$ | async; as user) { <p>{{ user.name }}</p> }  // ✅

// Use slice for truncation
{{ text | slice:0:100 }}...                      // ✅

// Use json for debugging only
{{ debugValue | json }}                          // ✅

// Use percent carefully — it multiplies by 100
{{ 0.25 | percent }}                             // ✅ renders 25%

Don’t Do This:

// Don't call pipes in the class without importing them
this.datePipe.transform(d, 'short')              // ⚠️  import needed

// Don't use json in production
{{ user | json }}                                // ⚠️  debug only

// Don't use multiple async on the same observable
{{ (user$ | async)?.name }}
{{ (user$ | async)?.email }}                     // ❌ two subscriptions

// Don't pipe heavy transformations
{{ items | customSort }}                         // ⚠️  move to class

// Don't forget parentheses with async + optional chaining
{{ user$ | async?.name }}                        // ❌ wrong

// Don't assume pipe output is formatted for all locales
{{ price | currency }}                           // ⚠️  default is USD

// Don't overuse pipes — class logic can be clearer
{{ items | filterActive | sortByDate }}          // ⚠️  do in class

Common Pitfalls

PitfallProblemSolution
Forgetting to import a pipeTemplate errorAdd to imports
Multiple async subscriptionsDuplicate HTTP callsUse as or shareReplay
json in productionUgly UIReplace with formatting
percent expectationsMultiplies by 100Know the semantics
slice without ellipsisNo indicatorAdd manually
date on invalid stringsThrowsPass Date or ISO
Pipes in classNot availableImport and call transform
Heavy work in pipeRuns on every CDMove to class or computed

Real-World Examples

1. Format a date

{{ createdAt | date:'mediumDate' }}

2. Format a currency

{{ price | currency:'EUR' }}

3. Format a percentage

{{ discountRate | percent }}

4. Format a number

{{ count | number }}

5. Uppercase a label

{{ status | uppercase }}

6. Title-case a name

{{ fullName | titlecase }}

7. Truncate a string

{{ description | slice:0:100 }}...

8. Show the first 5 items

@for (item of items | slice:0:5; track item.id) { }

9. Debug an object

{{ user | json }}

10. Unwrap an observable

{{ user$ | async }}

11. Unwrap and bind

@if (user$ | async; as user) {
  <p>{{ user.name }}</p>
}

12. Iterate an object

@for (entry of user | keyvalue; track entry.key) {
  <p>{{ entry.key }}: {{ entry.value }}</p>
}

13. Chain pipes

{{ name | titlecase | slice:0:10 }}

14. Pipe in property binding

[alt]="description | slice:0:50"

15. Pipe in attribute binding

[attr.aria-label]="label | uppercase"

16. Format a time

{{ timestamp | date:'shortTime' }}

17. Custom date pattern

{{ timestamp | date:'EEEE, MMMM d, yyyy' }}

18. Currency without cents

{{ price | currency:'USD':'symbol':'1.0-0' }}

19. Show last N items

@for (item of items | slice:-3; track item.id) { }

20. Combine async and slice

@for (item of (items$ | async) ?? [] | slice:0:5; track item.id) { }

Visual: Pipe Flow

┌──────────────────────────────────────────────┐
│  Template                                    │
│                                              │
│  {{ value | pipe }}                          │
│                                              │
└──────────────────────────────────────────────┘
       │                          │
       │                          ▼
       │                ┌────────────────────┐
       │                │  pipe.transform(v) │
       │                └────────────────────┘
       │                          │
       ▼                          ▼
┌──────────────────────────────────────────────┐
│  Rendered output                             │
└──────────────────────────────────────────────┘

Visual: Chained Pipes

┌──────────────────────────────────────────────┐
│  {{ name | titlecase | slice:0:10 }}         │
│                                              │
│  'alice johnson'                             │
│       │                                      │
│       ▼                                      │
│  titlecase → 'Alice Johnson'                 │
│       │                                      │
│       ▼                                      │
│  slice:0:10 → 'Alice John'                   │
│                                              │
└──────────────────────────────────────────────┘

Visual: Pure vs Impure

┌──────────────────────────────────────────────┐
│  Pure pipe (date, uppercase, currency)       │
│                                              │
│  Runs only when input reference changes      │
│  Cached result                               │
│  Cheap                                       │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  Impure pipe (async)                         │
│                                              │
│  Runs on every change detection cycle        │
│  No caching                                  │
│  Needed for async emissions                  │
│                                              │
└──────────────────────────────────────────────┘

Visual: Date Formats

┌──────────────────────────────────────────────┐
│  Input: 2024-06-15T14:30:00                  │
│                                              │
│  'short'      → 6/15/24, 2:30 PM             │
│  'medium'     → Jun 15, 2024, 2:30:00 PM     │
│  'long'       → June 15, 2024 at 2:30:00 PM  │
│  'full'       → Saturday, June 15, 2024...   │
│  'shortDate'  → 6/15/24                      │
│  'mediumDate' → Jun 15, 2024                 │
│  'longDate'   → June 15, 2024                ││  'fullDate'   → Saturday, June 15, 2024      │
│  'shortTime'  → 2:30 PM                      │
│  'mediumTime' → 2:30:00 PM                   │
│                                              │
└──────────────────────────────────────────────┘

Visual: Number Format String

┌──────────────────────────────────────────────┐
│  Format: {minInt}.{minFrac}-{maxFrac}        │
│                                              │
│  1234.5678 | number:'1.2-2'                  │
│       │                                      │
│       ▼                                      │
│  1,234.57                                    │
│                                              │
│  0.25 | percent:'1.0-0'                      │
│       │                                      │
│       ▼                                      │
│  25%                                         │
│                                              │
└──────────────────────────────────────────────┘

Visual: Async Pipe

┌──────────────────────────────────────────────┐
│  Component                                   │
│                                              │
│  user$ = this.http.get<User>('/api/user')    │
│                                              │
└──────────────────────────────────────────────┘
       │
       ▼
┌──────────────────────────────────────────────┐
│  {{ user$ | async }}                         │
│                                              │
│  • Subscribes to observable                  │
│  • Renders emitted value                     │
│  • Unsubscribes on destroy                   │
│                                              │
└──────────────────────────────────────────────┘
       │
       ▼
┌──────────────────────────────────────────────┐
│  With `as`                                   │
│                                              │
│  @if (user$ | async; as user) {              │
│    <p>{{ user.name }}</p>                    │
│    <p>{{ user.email }}</p>                   │
│  }                                           │
│                                              │
│  → one subscription, clean template          │
│                                              │
└──────────────────────────────────────────────┘

Visual: keyvalue Pipe

┌──────────────────────────────────────────────┐
│  Input object                                │
│                                              │
│  { id: 1, name: 'Alice' }                    │
│                                              │
└──────────────────────────────────────────────┘
                  │
                  │  keyvalue
                  ▼
┌──────────────────────────────────────────────┐
│  Array of entries                            │
│                                              │
│  [ {key: 'id', value: 1},                    │
│    {key: 'name', value: 'Alice'} ]           │
│                                              │
└──────────────────────────────────────────────┘
                  │
                  │  @for
                  ▼
┌──────────────────────────────────────────────┐
│  Rendered                                    │
│                                              │
│  id: 1                                       │
│  name: Alice                                 │
│                                              │
└──────────────────────────────────────────────┘

Visual: Pipes in Standalone Imports

┌──────────────────────────────────────────────┐
│  @Component({                                │
│    standalone: true,                         │
│    imports: [                                │
│      DatePipe,           ← pipe              │
│      CurrencyPipe,       ← pipe              │
│      UpperCasePipe,      ← pipe              │
│      SomeDirective,      ← directive         │
│      SomeComponent       ← component         │
│    ],                                        │
│    template: `...`                           │
│  })                                          │
│                                              │
│  Each pipe imported individually             │
│                                              │
└──────────────────────────────────────────────┘

Visual: Common Pipe Use Cases

┌──────────────────────────────────────────────┐
│  Dates          → date                       │
│  Money          → currency                   │
│  Percentages    → percent                    │
│  Large numbers  → number                     │
│  Case           → uppercase/lowercase/       │
│                   titlecase                  │
│  Truncation     → slice                      │
│  Debug objects  → json                       │
│  Observables    → async                      │
│  Object loops   → keyvalue                   │
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptMeaning
PipeTransform value for display
Syntax{{ value | pipe }}
Chaining{{ v | p1 | p2 }}
PureRuns when input changes
ImpureRuns every CD cycle
asyncUnwraps observable/promise
StandalonePipes imported per component
CommonModuleModule-based — all pipes

Key takeaways:

  • Pipes transform values for display without mutating the data
  • The syntax is {{ value | pipe }}; chains work left to right
  • Most built-in pipes are pure — they run only when the input changes
  • async is impure — it runs every CD cycle to react to emissions
  • date, number, percent, currency handle locale-aware formatting
  • uppercase, lowercase, titlecase convert case
  • json is a debugging aid — don’t leave it in production
  • slice truncates strings and arrays — same semantics as JS slice
  • async subscribes and unsubscribes automatically — the recommended pattern for observables
  • keyvalue turns objects into arrays for @for
  • In standalone components, import each pipe individually from @angular/common
  • Keep pipes pure and cheap — heavy work belongs in the class

Remember: Pipes are Angular’s built-in formatting layer. They keep your component class focused on data and your template focused on display. Learn the built-ins — date, currency, number, percent, case, slice, json, async, keyvalue — and you’ll rarely write formatting code by hand. When built-ins don’t fit, write a custom pipe. When they do, use them everywhere. That’s the pattern.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!