in

How to Create Custom Pipes in Angular for Elegant Data Display

default image

Hey there!

As an experienced Angular developer, I want to share everything I‘ve learned about one of the most useful Angular features – pipes.

Pipes transform your output data into beautiful, readable formats right in your templates. Once you master pipes, you‘ll wonder how you ever lived without them!

In this comprehensive guide, you’ll learn:

  • Why pipes are so invaluable in Angular apps
  • How built-in pipes solve common formatting needs
  • How to create custom pipes for unique data transformations
  • Patterns for supercharging your pipes
  • When to use pure vs impure pipes
  • Pipe best practices used by the pros

Follow along and you’ll gain the skills to build custom pipes for clean, elegant data display like a pro!

Why Pipes are Indispensable in Angular

Pipes provide a way to declare display-value transformations right in your templates. For example:

<!-- Transform a date -->
{{ birthday | date }} 

<!-- Format as currency -->  
{{ price | currency }}

This is much cleaner than declaring formatting functions in your components!

According to the 2021 State of JavaScript survey, Angular ranks 3rd among front-end frameworks with 67.4% awareness.

As an Angular developer, mastering pipes will level up your skills in how to cleanly transform data in your apps.

Here are some of my favorite reasons to use pipes:

They Abstract Away Formatting Logic

Without pipes, you‘d have to declare formatting functions in your components like:

formatDate(date) {
  // date formatting logic
}

And in your template:

{{ formatDate(birthday) }}

This clutters up your components!

With pipes, you declare the formatting logic once in a pipe, then reuse it:

{{ birthday | date }}

Much cleaner!

They Make Templates More Readable

Scanning this:

{{ formatDate(order.date) | currency | titlecase | slice:0:10 }}

You can quickly grasp what‘s happening. With pipes, you almost read it like English!

Built-in Pipes Save Time

No need to reinvent the wheel for common formats like dates and currencies – just use a built-in pipe!

You Can Chain Pipes

Use the pipe operator (|) to combine multiple pipes:

{{ data | pipe1 | pipe2 | pipe3 }} 

This unlocks powerful data transformations.

You Can Create Custom Pipes

For specialized formats, make your own custom pipes tailored to your app!

Bottom line – pipes will make your Angular skills shine ✨ by handling transformations the Angular way.

Now let‘s look at some common built-in pipes…

Built-in Pipes for Every Scenario

Angular ships with a solid set of built-in pipes covering all the common formatting scenarios:

DatePipe

The DatePipe formats dates based on locale rules:

{{ dateObj | date }} <!-- 3/15/2022 -->

{{ dateObj | date:‘short‘ }} <!-- 3/15/22 -->

You can pass in different formatting presets like ‘short‘, or a custom format string.

CurrencyPipe

The CurrencyPipe formats numbers into currency strings in your locale:

<!-- USD -->
{{ 12.5 | currency }} // $12.50  

<!-- EUR -->
{{ 12.5 | currency:‘EUR‘ }} // €12.50

PercentPipe

The PercentPipe converts a number into a locale-specific percentage string:

{{ 0.25 | percent }} // 25%

LowerCasePipe / UpperCasePipe

These pipes convert text into lower or uppercase:

{{ ‘Hello‘ | uppercase }} // HELLO

{{ ‘Hello‘ | lowercase }} // hello

JsonPipe

The JsonPipe converts any object into a nicely formatted JSON string – great for debugging!

<!-- Displays formatted JSON -->
{{ myObj | json }} 

There are many more built-in pipes for common scenarios like slicing arrays, filtering, and async values.

These will cover most simple formatting needs, but for specialized transformations it‘s time to build your own custom pipe…

Creating Your Own Custom Pipe

One of Angular’s killer features is the ability to create your own pipes tailored to your app’s unique data formats.

Building a custom pipe is easy – just follow these steps:

1. Generate the Pipe with the CLI

Use the Angular CLI to generate a new pipe skeleton:

ng generate pipe my-custom-pipe

This creates:

  • my-custom-pipe.pipe.ts – The pipe class code
  • my-custom-pipe.pipe.spec.ts – Unit test file

The CLI gives you a head start!

2. Import the Pipe Decorator

In the generated .ts file, import the required Pipe decorator:

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

This provides the decorator and interface we need to build a working pipe.

3. Add the @Pipe Decorator

Above the pipe class, add the @Pipe decorator like:

@Pipe({
  name: ‘myCustomPipe‘
})

This tells Angular this class is a pipe with the given name.

4. Implement the PipeTransform Interface

Next have your pipe class implement the PipeTransform interface:

export class MyCustomPipe implements PipeTransform {

  // Our transform method

}

This enforces that your class has a transform() method.

5. Write the Transform Method

Here is where you define the pipe’s transformation logic:

transform(value: any) {
  // Transform value 
  return transformedValue; 
}

The value argument is the input to the pipe, and it returns the transformed value.

For example, a pipe that capitalizes strings:

transform(value: string): string {
  return value.toUpperCase();
}

6. Add the Pipe to the Module

Finally, register your pipe in the declarations array of your app module so Angular knows about it:

@NgModule({
  declarations: [
    AppComponent,
    MyCustomPipe
  ]
}) 

Once added here, you can use your pipe anywhere in your app!

And that’s really all there is to it! Let’s walk through an example.

Custom Pipe Example

Let’s build a pipe that formats a username by truncating it after 8 characters.

First generate it:

ng generate pipe truncate

In truncate.pipe.ts:

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

@Pipe({
  name: ‘truncate‘ 
})
export class TruncatePipe implements PipeTransform {

  transform(value: string): string {

    // Truncate value after 8 chars
    return value.substring(0, 8) + ‘...‘;

  }

}

The transform:

  1. Takes the input value
  2. Truncates it after 8 chars
  3. Adds ‘…‘ at the end

In the module:

@NgModule({
  // ...

  declarations: [
    TruncatePipe
  ]
})

Then in a template:

<!-- Truncates the username -->
<p>{{ user.name | truncate }}</p> 

Just like that, we‘ve created a reusable pipe to truncate long usernames!

Power-up Your Pipes

Beyond basic data transforms, here are some advanced patterns to take your pipes to the next level:

Parameterize Them

You can pass arguments to make pipes more configurable:

transform(value: string, limit: number): string {
  return value.substring(0, limit);
}

Used like:

{{ user.name | truncate:15 }}

Chain Pipes

Use the | operator to compose pipes:

{{ birthday | date | uppercase }}

This chains the DatePipe and UpperCasePipe.

Create Stateful Pipes

You can even give pipes state by storing values on the class:

items = [];

transform(value: any) {
  // filter value based on saved items
}

Useful for things like filtering.

Make Async Pipes

Use async pipes to work with promises and observables:

transform(value: Observable) {
  return value.pipe(map(data => {
    // transform data
  }))
} 

This unlocks asynchronous capabilities!

The sky‘s really the limit once you master building pipes. They let you declaratively transform data in your templates any way you need.

Next let‘s compare pure and impure pipes…

Pure vs Impure Pipes

There are two categories of pipes in Angular:

Pure Pipes

Pure pipes are the default when using the @Pipe decorator.

A pure pipe is only called by Angular when the input value reference changes.

For example, if user.name stays the same, a pure pipe won‘t re-evaluate.

This allows Angular to optimize performance and avoid unnecessary transforms. All built-in pipes are pure.

Impure Pipes

An impure pipe is called on every change detection cycle regardless if the input changed.

Impure pipes are useful when you need to access something imperative like the DOM, Browser API, or external REST API.

For example, an impure pipe that calls a REST API:

@Pipe({
  name: ‘apiData‘,
  pure: false
})

transform(endpoint) {
  return http.get(endpoint).pipe(
    map(data => {
      // Transform data
    })
  )
}

To define a pipe as impure, set pure: false:

@Pipe({
  name: ‘impurePipe‘,
  pure: false
})

In general, prefer pure pipes for performance unless you need impure behavior.

Now let‘s look at some best practices when working with pipes.

Pipe Best Practices

Here are some best practices I‘ve learned for working effectively with pipes:

  • Naming – Use camelCase names like myCustomPipe

  • Single responsibility – Each pipe should do one thing, keeping logic clean.

  • DRY – Reuse common pipes instead of duplicating.

  • Pure over impure – Favor pure pipes for performance, only impure when needed.

  • Unit test – Thoroughly unit test all pipes.

  • NgModule declaration – Remember to declare custom pipes.

  • No side effects – A pipe should be free of side effects and return the same value for the same input.

  • Idempotent – Pipes should be idempotent, producing the same output for duplicate calls.

  • Business logic in services – Move complex logic out of pipes into services.

Following these practices will ensure you get the most out of pipes!

Pipe FAQs

Here are answers to some frequently asked questions about pipes:

Why are pipes useful in Angular?

Pipes transform output data into a readable, consumable format for display in the UI. They encapsulate formatting logic in a reusable way.

What‘s the difference between built-in and custom pipes?

Built-in pipes like DatePipe cover common formatting scenarios. Custom pipes allow you to create application-specific transformations not covered by the built-ins.

How do you pass arguments to a pipe?

Use the colon syntax for arguments like:

{{ value | pipe:arg1:arg2 }}

What is the difference between pure and impure pipes?

Pure pipes only run when the input value changes while impure pipes run on every change detection cycle.

How do you chain pipes together?

Use the pipe operator (|) to chain like:

{{ value | pipe1 | pipe2 }}

The pipes execute left-to-right, passing the output of one to the input of the next.

Should pipes have side effects?

No, pipes should be free of side effects and return the same value for the same input to avoid bugs.

I hope these FAQs help clear up some common questions around pipes!

Summary

Here‘s a quick recap of what we covered:

  • Pipes encapsulate display-value transformations in a declarative & reusable way.
  • Built-in pipes like DatePipe and CurrencyPipe handle common formatting needs.
  • You can create custom pipes for unique data transformations.
  • Pipe chaining composes multiple pipes.
  • Pure pipes are optimal for performance.
  • Follow best practices like single responsibility to avoid issues.

Pipes are one of Angular‘s most useful features. Mastering them will take your formatting skills to the next level!

For more Angular tips, check out these tutorials:

I hope this guide gives you the knowledge to harness the power of Angular pipes. Happy coding!

Written by