> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# Pipes

> Transform data in templates using Angular pipes and the PipeTransform interface

Pipes are simple functions used in templates to transform data for display. Angular provides built-in pipes for common transformations and allows you to create custom pipes for specific needs.

## What are Pipes?

Pipes take data as input and transform it to a desired output format. They are used in template expressions:

```typescript theme={null}
{{ value | pipeName }}
{{ value | pipeName: arg1:arg2 }}
{{ value | pipe1 | pipe2 | pipe3 }}
```

<Note>
  Pipes are pure by default, meaning they only execute when their input reference changes. This ensures optimal performance.
</Note>

## Built-in Pipes

Angular provides several built-in pipes:

### DatePipe

Format dates:

```typescript theme={null}
import { Component } from '@angular/core';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-date-demo',
  standalone: true,
  imports: [DatePipe],
  template: `
    <p>{{ today | date }}</p>
    <p>{{ today | date:'short' }}</p>
    <p>{{ today | date:'medium' }}</p>
    <p>{{ today | date:'long' }}</p>
    <p>{{ today | date:'full' }}</p>
    <p>{{ today | date:'shortDate' }}</p>
    <p>{{ today | date:'mediumTime' }}</p>
    <p>{{ today | date:'yyyy-MM-dd' }}</p>
    <p>{{ today | date:'h:mm a' }}</p>
    <p>{{ today | date:'EEEE, MMMM d, y' }}</p>
  `
})
export class DateDemoComponent {
  today = new Date();
}
```

### CurrencyPipe

Format currency values:

```typescript theme={null}
import { Component } from '@angular/core';
import { CurrencyPipe } from '@angular/common';

@Component({
  selector: 'app-currency-demo',
  standalone: true,
  imports: [CurrencyPipe],
  template: `
    <p>{{ price | currency }}</p>
    <p>{{ price | currency:'EUR' }}</p>
    <p>{{ price | currency:'GBP':'symbol':'1.2-2' }}</p>
    <p>{{ price | currency:'USD':'code' }}</p>
    <p>{{ largeAmount | currency:'USD':'symbol-narrow':'1.0-0' }}</p>
  `
})
export class CurrencyDemoComponent {
  price = 123.456;
  largeAmount = 1234567.89;
}
```

### DecimalPipe

Format numbers:

```typescript theme={null}
import { Component } from '@angular/core';
import { DecimalPipe } from '@angular/common';

@Component({
  selector: 'app-decimal-demo',
  standalone: true,
  imports: [DecimalPipe],
  template: `
    <p>{{ pi | number }}</p>
    <p>{{ pi | number:'1.2-4' }}</p>
    <p>{{ largeNumber | number:'1.0-0' }}</p>
    <p>{{ percentage | number:'1.2-2' }}</p>
  `
})
export class DecimalDemoComponent {
  pi = 3.14159265359;
  largeNumber = 1234567;
  percentage = 0.259;
}
```

### PercentPipe

Format percentages:

```typescript theme={null}
import { Component } from '@angular/core';
import { PercentPipe } from '@angular/common';

@Component({
  selector: 'app-percent-demo',
  standalone: true,
  imports: [PercentPipe],
  template: `
    <p>{{ ratio | percent }}</p>
    <p>{{ ratio | percent:'1.2-2' }}</p>
    <p>{{ 0.5 | percent }}</p>
  `
})
export class PercentDemoComponent {
  ratio = 0.259;
}
```

### UpperCasePipe and LowerCasePipe

Transform text case:

```typescript theme={null}
import { Component } from '@angular/core';
import { UpperCasePipe, LowerCasePipe, TitleCasePipe } from '@angular/common';

@Component({
  selector: 'app-case-demo',
  standalone: true,
  imports: [UpperCasePipe, LowerCasePipe, TitleCasePipe],
  template: `
    <p>{{ text | uppercase }}</p>
    <p>{{ text | lowercase }}</p>
    <p>{{ text | titlecase }}</p>
  `
})
export class CaseDemoComponent {
  text = 'hello world';
}
```

### JsonPipe

Display objects for debugging:

```typescript theme={null}
import { Component } from '@angular/core';
import { JsonPipe } from '@angular/common';

@Component({
  selector: 'app-json-demo',
  standalone: true,
  imports: [JsonPipe],
  template: `
    <pre>{{ user | json }}</pre>
  `
})
export class JsonDemoComponent {
  user = {
    name: 'John Doe',
    email: 'john@example.com',
    roles: ['admin', 'user']
  };
}
```

### SlicePipe

Extract a subset of array or string:

```typescript theme={null}
import { Component } from '@angular/core';
import { SlicePipe } from '@angular/common';

@Component({
  selector: 'app-slice-demo',
  standalone: true,
  imports: [SlicePipe],
  template: `
    <p>{{ text | slice:0:5 }}</p>
    <ul>
      @for (item of items | slice:0:3; track item) {
        <li>{{ item }}</li>
      }
    </ul>
  `
})
export class SliceDemoComponent {
  text = 'Hello World';
  items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];
}
```

## AsyncPipe

Automatically subscribe to observables:

```typescript theme={null}
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

interface User {
  id: number;
  name: string;
}

@Component({
  selector: 'app-async-demo',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    @if (users$ | async; as users) {
      <ul>
        @for (user of users; track user.id) {
          <li>{{ user.name }}</li>
        }
      </ul>
    } @else {
      <p>Loading...</p>
    }
  `
})
export class AsyncDemoComponent {
  private http = inject(HttpClient);
  users$: Observable<User[]> = this.http.get<User[]>('/api/users');
}
```

<Warning>
  The AsyncPipe automatically unsubscribes when the component is destroyed, preventing memory leaks.
</Warning>

## Creating Custom Pipes

Implement the `PipeTransform` interface:

```typescript theme={null}
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'exponential',
  standalone: true
})
export class ExponentialPipe implements PipeTransform {
  transform(value: number, exponent: number = 1): number {
    return Math.pow(value, exponent);
  }
}

// Usage
@Component({
  selector: 'app-demo',
  standalone: true,
  imports: [ExponentialPipe],
  template: `
    <p>{{ 2 | exponential }}</p>      <!-- 2 -->
    <p>{{ 2 | exponential:3 }}</p>    <!-- 8 -->
    <p>{{ 5 | exponential:2 }}</p>    <!-- 25 -->
  `
})
export class DemoComponent { }
```

## Pipe with Multiple Arguments

```typescript theme={null}
import { Pipe, PipeTransform } from '@angular/core';

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

// Usage
@Component({
  selector: 'app-demo',
  standalone: true,
  imports: [TruncatePipe],
  template: `
    <p>{{ longText | truncate }}</p>
    <p>{{ longText | truncate:20 }}</p>
    <p>{{ longText | truncate:15:'...' }}</p>
  `
})
export class DemoComponent {
  longText = 'This is a very long text that needs truncation';
}
```

## Impure Pipes

Pipes that detect all changes (use sparingly):

```typescript theme={null}
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filter',
  standalone: true,
  pure: false // Impure pipe - executes on every change detection
})
export class FilterPipe implements PipeTransform {
  transform(items: any[], searchText: string, field: string): any[] {
    if (!items || !searchText) {
      return items;
    }
    
    searchText = searchText.toLowerCase();
    
    return items.filter(item => {
      return item[field].toLowerCase().includes(searchText);
    });
  }
}

// Usage
@Component({
  selector: 'app-search',
  standalone: true,
  imports: [FilterPipe, FormsModule],
  template: `
    <input [(ngModel)]="searchText" placeholder="Search...">
    <ul>
      @for (user of users | filter:searchText:'name'; track user.id) {
        <li>{{ user.name }}</li>
      }
    </ul>
  `
})
export class SearchComponent {
  searchText = '';
  users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Charlie' }
  ];
}
```

<Note>
  Impure pipes can impact performance because they execute on every change detection cycle. Use them carefully.
</Note>

## Chaining Pipes

Combine multiple pipes:

```typescript theme={null}
import { Component } from '@angular/core';
import { DatePipe, UpperCasePipe, SlicePipe } from '@angular/common';

@Component({
  selector: 'app-chain-demo',
  standalone: true,
  imports: [DatePipe, UpperCasePipe, SlicePipe],
  template: `
    <p>{{ today | date:'fullDate' | uppercase }}</p>
    <p>{{ text | slice:0:10 | uppercase }}</p>
    <p>{{ value | number:'1.2-2' | currency:'USD' }}</p>
  `
})
export class ChainDemoComponent {
  today = new Date();
  text = 'hello world';
  value = 123.456;
}
```

## Complex Custom Pipe

Pipe with dependency injection:

```typescript theme={null}
import { Pipe, PipeTransform, inject } from '@angular/core';
import { DecimalPipe } from '@angular/common';

@Pipe({
  name: 'fileSize',
  standalone: true
})
export class FileSizePipe implements PipeTransform {
  private decimalPipe = inject(DecimalPipe);
  
  transform(bytes: number, precision: number = 2): string {
    if (bytes === 0) return '0 Bytes';
    
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    const value = bytes / Math.pow(k, i);
    const formatted = this.decimalPipe.transform(value, `1.0-${precision}`);
    
    return `${formatted} ${sizes[i]}`;
  }
}

// Usage
@Component({
  selector: 'app-demo',
  standalone: true,
  imports: [FileSizePipe],
  template: `
    <p>{{ 1024 | fileSize }}</p>        <!-- 1.00 KB -->
    <p>{{ 1048576 | fileSize }}</p>     <!-- 1.00 MB -->
    <p>{{ 1234567890 | fileSize:1 }}</p> <!-- 1.1 GB -->
  `
})
export class DemoComponent { }
```

## Async Operations in Pipes

```typescript theme={null}
import { Pipe, PipeTransform, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Pipe({
  name: 'fetchUser',
  standalone: true
})
export class FetchUserPipe implements PipeTransform {
  private http = inject(HttpClient);
  
  transform(userId: number): Observable<string> {
    return this.http.get<any>(`/api/users/${userId}`).pipe(
      map(user => user.name)
    );
  }
}

// Usage with AsyncPipe
@Component({
  selector: 'app-demo',
  standalone: true,
  imports: [FetchUserPipe, AsyncPipe],
  template: `
    <p>User: {{ userId | fetchUser | async }}</p>
  `
})
export class DemoComponent {
  userId = 123;
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Keep Pipes Pure" icon="droplet">
    Default pure pipes for better performance
  </Card>

  <Card title="Simple Transforms" icon="wand-magic-sparkles">
    Keep transformation logic simple
  </Card>

  <Card title="Use AsyncPipe" icon="clock">
    For observables to prevent memory leaks
  </Card>

  <Card title="Avoid Side Effects" icon="shield">
    Pipes should only transform data
  </Card>
</CardGroup>

1. **Keep pipes pure** - Unless you have a specific reason
2. **Avoid complex logic** - Move to services if needed
3. **Use meaningful names** - Clear and descriptive
4. **Handle null/undefined** - Defensive programming
5. **Document parameters** - Clear usage examples
6. **Test thoroughly** - Pipes are easy to unit test

## Next Steps

<CardGroup cols={2}>
  <Card title="Templates" href="/concepts/templates">
    Learn template syntax and bindings
  </Card>

  <Card title="Directives" href="/concepts/directives">
    Extend element behavior
  </Card>
</CardGroup>
