Tutorials Logic, IN info@tutorialslogic.com

Angular Pipes Built in Custom Pipes

Angular Pipes Built in Custom Pipes

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.

What are Pipes in Angular?

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.

Why Pipes Are Useful

  • Cleaner templates by avoiding repeated formatting code in the component.
  • Reusable display logic because the same pipe can be used in many templates.
  • Better separation of concerns because presentation formatting stays separate from business logic.
  • Readable templates since a pipe often explains intent clearly, such as currency, date, or uppercase.
  • Built-in convenience because Angular already ships with many common transformation pipes.

Basic Pipe Syntax

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.

Pipe Syntax

Pipe Syntax
{{ value | pipeName }}

Pipe with Parameters

Pipe with Parameters
{{ price | currency:'USD':'symbol':'1.2-2' }}

Chained Pipes

Chained Pipes
{{ name | uppercase | slice:0:5 }}

Built-in Pipes

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)

Using Built-in Pipes in a Component

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.

Built-in Pipes Usage

Built-in Pipes Usage
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;
}

Frequently Used Built-in Pipes

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.

AsyncPipe

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.

AsyncPipe Example

AsyncPipe Example
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)
    );
}

KeyValuePipe

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.

KeyValuePipe Example

KeyValuePipe Example
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
    };
}

Creating a Custom Pipe

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.

Custom Pipe - Truncate Text

Custom Pipe - Truncate Text
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;
    }
}

Creating a Custom Pipe

Creating a Custom Pipe
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.';
}

Pure vs Impure Pipes

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

Impure Pipe Example

Impure Pipe Example
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())
        );
    }
}

Pipes with Signals in Modern Angular

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.

computed() Instead of Impure Pipe

computed() Instead of Impure Pipe
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())
        )
    );
}

When to Use a Pipe and When Not To

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.

Common Beginner Mistakes

  • Using a pipe for heavy logic that should really live in a component or service.
  • Assuming AsyncPipe is only a formatter when it actually manages subscriptions too.
  • Overusing impure pipes for filtering or sorting large data sets.
  • Forgetting to import standalone pipes when using them in standalone components.
  • Expecting a pipe to permanently change the original value instead of only changing the displayed result.

Summary

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.

Applied guide for Angular

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.

  • Identify the exact problem solved by Angular.
  • Trace template binding before and after the main operation.
  • Keep one intentionally broken version and explain the fix.
  • Connect the example to a feature module in a dashboard app so the idea feels concrete.
Key Takeaways
  • I can explain where Angular fits inside a feature module in a dashboard app.
  • I can point to the exact template binding affected by this topic.
  • I tested a normal case and an edge case involving missing, repeated, empty, or boundary input.
  • I verified the result with browser console and Angular error output instead of assuming it worked.
  • I can describe the main mistake: copying the syntax before understanding the behavior.
Common Mistakes to Avoid
WRONG Copying the syntax before understanding the behavior.
RIGHT Write the expected behavior first, then make the example prove it.
A one-line expectation turns the code from copied syntax into a testable idea.
WRONG Practicing only the perfect input.
RIGHT Also test missing, repeated, empty, or boundary input before considering the lesson complete.
The edge case is where most interview follow-up questions begin.
WRONG Looking only at the final output.
RIGHT Trace template binding through each important step.
Tracing makes debugging faster because you can see the first incorrect state.

Practice Tasks

  • Build one small component or service that demonstrates Angular in a feature module in a dashboard app.
  • Change the example to include missing, repeated, empty, or boundary input and record the difference.
  • Break the example by deliberately copying the syntax before understanding the behavior, then write the corrected version.
  • Explain the finished example in five bullet points: input, operation, output, failure case, and verification.

Frequently Asked Questions

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.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.