Angular in Angular is best learned by connecting the rule to a feature module in a dashboard app. Start with the smallest component or service, observe the output, and then add one realistic constraint so the concept becomes practical.
The key habit for this lesson is to watch template binding as it changes. That makes the topic easier to debug, easier to explain in interviews, and easier to use in real code without memorizing isolated syntax.
Pipes are a simple and expressive way to transform values directly inside Angular templates. They take a value as input, apply some formatting or transformation, and return the result for display. Pipes are used with the pipe operator |. Instead of changing the original data in the component class, a pipe changes how the value appears in the UI. This makes templates easier to read and keeps presentation-specific formatting logic close to where the value is displayed.
For example, a component may store a raw date object, an unformatted price, a lowercase name, or a long block of text. Pipes let the template decide how those values should be shown to the user. A date can be formatted, a number can be displayed as currency, text can be converted to uppercase, or a long sentence can be shortened. This is one of the reasons pipes are so common in Angular applications: they improve readability while reducing repeated display logic.
The general syntax looks like this:
If the pipe needs additional options, those are passed using : after the pipe name.
Multiple pipes can also be chained together. The output of one pipe becomes the input of the next pipe.
{{ value | pipeName }}
{{ price | currency:'USD':'symbol':'1.2-2' }}
{{ name | uppercase | slice:0:5 }}
Angular includes several built-in pipes that solve common presentation needs without extra code. These are often the first pipes beginners use.
| Pipe | Purpose | Example |
|---|---|---|
| DatePipe | Formats date values | {{ today | date:'dd/MM/yyyy' }} |
| UpperCasePipe | Converts text to uppercase | {{ name | uppercase }} |
| LowerCasePipe | Converts text to lowercase | {{ name | lowercase }} |
| TitleCasePipe | Capitalizes words | {{ title | titlecase }} |
| CurrencyPipe | Formats numbers as currency | {{ price | currency:'USD' }} |
| DecimalPipe | Formats decimal precision | {{ pi | number:'1.2-4' }} |
| PercentPipe | Formats numbers as percentages | {{ ratio | percent }} |
| JsonPipe | Displays objects as JSON | {{ data | json }} |
| SlicePipe | Slices strings or arrays | {{ items | slice:0:3 }} |
| AsyncPipe | Subscribes to Promises or Observables | {{ user$ | async }} |
| KeyValuePipe | Turns object properties into iterable key-value pairs | @for (item of obj | keyvalue; track item.key) |
When using standalone Angular components, built-in pipes usually need to be imported into the component. The example below shows common pipe usage in one place.
This example shows why pipes are convenient. The raw values stay simple in the component, while the template decides how those values should appear to the user.
import { Component } from '@angular/core';
import {
DatePipe,
CurrencyPipe,
UpperCasePipe,
DecimalPipe,
PercentPipe,
TitleCasePipe,
SlicePipe
} from '@angular/common';
@Component({
selector: 'app-pipes-demo',
standalone: true,
imports: [DatePipe, CurrencyPipe, UpperCasePipe, DecimalPipe, PercentPipe, TitleCasePipe, SlicePipe],
template: `
<p>Today: {{ today | date:'fullDate' }}</p>
<p>Short date: {{ today | date:'dd/MM/yyyy' }}</p>
<p>Name: {{ name | uppercase }}</p>
<p>Title: {{ title | titlecase }}</p>
<p>Price: {{ price | currency:'USD':'symbol':'1.2-2' }}</p>
<p>Pi: {{ pi | number:'1.3-5' }}</p>
<p>Ratio: {{ ratio | percent:'1.0-1' }}</p>
<p>{{ name | uppercase | slice:0:5 }}</p>
`
})
export class PipesDemoComponent {
today = new Date();
name = 'tutorials logic';
title = 'angular pipes introduction';
price = 1234.5;
pi = 3.14159265;
ratio = 0.825;
}
DatePipe is one of the most commonly used pipes because applications often need to display dates in user-friendly formats. You can show a full date, short date, month name, time, or a custom pattern. This is useful in dashboards, reports, blog posts, booking systems, and admin panels.
CurrencyPipe, DecimalPipe, and PercentPipe are common in e-commerce, finance, analytics, and reporting. They help make numbers readable without adding formatting logic directly in the component class.
JsonPipe is especially useful while debugging. It should not usually be treated as a final production display tool for users, but it is excellent for inspecting objects during development.
The AsyncPipe deserves special attention because it behaves differently from display-only pipes. It subscribes to an Observable or Promise, returns the latest emitted value, and automatically handles cleanup when the view is destroyed. This makes it extremely useful in Angular applications that rely on RxJS or asynchronous data sources.
Without AsyncPipe, developers often subscribe manually in the component and then remember to clean up later. The async pipe reduces that boilerplate and keeps the template expressive.
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { interval, map } from 'rxjs';
@Component({
selector: 'app-clock',
standalone: true,
imports: [AsyncPipe],
template: `
<p>Seconds passed: {{ seconds$ | async }}</p>
`
})
export class ClockComponent {
seconds$ = interval(1000).pipe(
map(value => value + 1)
);
}
The KeyValuePipe is useful when you need to iterate over object properties in a template. Since templates are often designed around arrays, this pipe helps convert object data into an iterable form.
import { Component } from '@angular/core';
import { KeyValuePipe } from '@angular/common';
@Component({
selector: 'app-settings-view',
standalone: true,
imports: [KeyValuePipe],
template: `
@for (item of settings | keyvalue; track item.key) {
<p>{{ item.key }}: {{ item.value }}</p>
}
`
})
export class SettingsViewComponent {
settings = {
theme: 'dark',
language: 'English',
notifications: true
};
}
When the built-in pipes do not solve your formatting need, you can create a custom pipe. A custom pipe is declared using the @Pipe() decorator and must implement the PipeTransform interface. The main logic goes inside the transform() method.
Custom pipes are a good fit for small reusable view transformations such as truncating text, formatting labels, displaying initials, or converting status codes into readable output. If the logic becomes heavy or has side effects, it is usually better placed in a service or component method.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
standalone: true
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 50, ellipsis = '...'): string {
if (!value) return '';
if (value.length <= limit) return value;
return value.substring(0, limit) + ellipsis;
}
}
import { Component } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';
@Component({
selector: 'app-article',
standalone: true,
imports: [TruncatePipe],
template: `
<p>{{ longText | truncate }}</p>
<p>{{ longText | truncate:20 }}</p>
<p>{{ longText | truncate:30:' [read more]' }}</p>
`
})
export class ArticleComponent {
longText = 'Angular is a powerful framework for building modern web applications with TypeScript.';
}
By default, Angular pipes are pure. A pure pipe runs only when Angular detects that the input reference has changed. This makes pure pipes efficient and suitable for most formatting needs. An impure pipe, on the other hand, runs on every change detection cycle. That makes impure pipes more flexible, but also more expensive.
Beginners often hear about impure pipes in the context of filtering arrays, but this needs care. In modern Angular, filtering in the template with an impure pipe is usually not the best default choice for performance-sensitive applications.
This works, but because the pipe runs frequently, it can become expensive in larger lists. It is usually better to compute filtered results in the component or with signals so Angular recalculates only when needed.
| Type | Runs when | Performance | Typical use |
|---|---|---|---|
| Pure (default) | Input reference changes | Fast | Formatting values for display |
| Impure (pure: false) | Every change detection cycle | Slower | Rare special cases with mutable inputs |
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter',
standalone: true,
pure: false
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchText: string, field: string): any[] {
if (!items || !searchText) return items;
return items.filter(item =>
item[field]?.toLowerCase().includes(searchText.toLowerCase())
);
}
}
In modern Angular, especially with signals, many transformations that might once have been written as impure pipes are now better expressed using computed(). Computed signals only recalculate when their dependencies change, which is more efficient and often easier to reason about.
This pattern is often a better fit than an impure filtering pipe because the logic stays explicit and recomputation happens only when the dependent signals actually change.
import { Component, signal, computed } from '@angular/core';
interface User { id: number; name: string; role: string; }
@Component({
selector: 'app-user-list',
standalone: true,
template: `
<input (input)="search.set($any($event.target).value)" placeholder="Search users..." />
<p>Showing {{ filtered().length }} of {{ users().length }} users</p>
@for (user of filtered(); track user.id) {
<div>{{ user.name }} - {{ user.role }}</div>
}
`
})
export class UserListComponent {
search = signal('');
users = signal<User[]>([
{ id: 1, name: 'Alice', role: 'Admin' },
{ id: 2, name: 'Bob', role: 'Developer' },
{ id: 3, name: 'Charlie', role: 'Designer' },
]);
filtered = computed(() =>
this.users().filter(u =>
u.name.toLowerCase().includes(this.search().toLowerCase())
)
);
}
Use a pipe when the logic is mainly about display formatting. For example, converting text to title case, truncating text, showing a date, formatting a price, or presenting a JSON object for debugging are all good pipe use cases.
Avoid putting heavy business logic, network calls, or mutation-driven behavior inside pipes. A pipe should not become a hidden service or a complicated data-processing layer. If the logic needs external dependencies, has side effects, or is central to application behavior rather than presentation, it usually belongs in the component, a service, or a computed signal instead.
Pipes are one of Angular's cleanest features for template-level transformations. They make values easier to display, keep templates expressive, and reduce formatting clutter in components. Built-in pipes handle many common needs immediately, while custom pipes let you create reusable view transformations for your own application. Once you understand when to use pure pipes, when to avoid impure ones, and when a computed signal or service is a better fit, pipes become a very practical part of Angular development.
Use Angular when the program needs a clear answer to a specific problem, not because the keyword looks familiar. In a real Angular task, first name the input, then name the transformation, then name the output. This small discipline shows whether the topic is being used correctly or only copied from an example.
A reliable practice flow is: create the smallest working component or service, add one normal case, add one edge case such as missing, repeated, empty, or boundary input, and then confirm the result with browser console and Angular error output. If the result surprises you, reduce the code until the behavior is visible again.
The most common trap here is copying the syntax before understanding the behavior. Avoid it by writing one sentence before the code that explains why Angular is the right choice. After the code runs, verify the lesson by doing this: change one input and explain the changed output.
Copying the syntax before understanding the behavior.
Write the expected behavior first, then make the example prove it.
Practicing only the perfect input.
Also test missing, repeated, empty, or boundary input before considering the lesson complete.
Looking only at the final output.
Trace template binding through each important step.
Use it when the problem matches the behavior shown in the example and when the result can be verified through browser console and Angular error output.
Start with a tiny case, then test missing, repeated, empty, or boundary input. The main warning sign is copying the syntax before understanding the behavior.
Trace template binding, predict the result, run the example, and compare your prediction with the actual output.
Explore 500+ free tutorials across 20+ languages and frameworks.