Angular Pipes Tutorial

In this Angular tutorial we learn how pipes help us transform data and display such data in a certain way.

We cover Angular's built-in pipes as well as creating our own custom pipes.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What are Pipes

Pipes help us transform data to display it in a certain way. They are called pipes because they use the pipe | operator in the syntax.

For example, let’s say we want to display the current date in DD/MM/YYYY format.

The default Javascript Date() object will display the date as follows.

Thu Jan 14 2021 09:29:33 GMT+0200

In this case, that’s not what we want it to look like. So, we can simply add a pipe operator followed by the structure we want.

Example:
<p>{{ date | date:'d/M/y' }}</p>

Which will display the date in the format we want.

14/1/2021

Lesson project setup

For this lesson there is no initial setup other than having an app.

To keep things simple, we will be working directly from inside the main ‘app.component’ component and will create whatever else we need on a step-by-step basis.

How to use Pipes

To use a pipe, we specify the data we want to transform, followed by the pipe operator and the structure we want the data to be transformed into.

Syntax:
{{ data | structure }}

Angular provides us with many built-in pipes for common structures. We can, of course, build our own custom pipes as well.

  • Lowercase pipe
  • Uppercase pipe
  • Titlecase pipe
  • Date pipe
  • Currency pipe
  • Json pipe
  • Percent pipe
  • Decimal pipe
  • Slice pipe
  • Async pipe

Pipes are easy enough for us to combine many of them in a single example. We will look at the Async pipe separately though.

Let’s set up some values in our main component’s logic.

Example: app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

  title = "This is a test title for pipe transforms";
  date = new Date();
  pi = 3.14159265359;

  jsonObj = {
    fname: 'John',
    lname: 'Doe',
    age: 30,
    address: {
      a1: 'New York',
      a2: 'USA'
    }
  };

  weekdays = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
}

In the example above we have a simple string, a date object, a number with decimal places, a json object and an array with 7 elements.

We’re going to use pipes to output these, and more, in different structures in the View of the main component.

Example: app.component.html
<h2>Uppercase pipe</h2>
<p>{{ title | uppercase }}</p>

<h2>Lowercase pipe</h2>
<p>{{ title | lowercase }}</p>

<h2>Titlecase pipe</h2>
<p>{{ title | titlecase }}</p>

<h2>Date pipe</h2>
<p>{{ date | date }}</p>

<h2>Custom Date pipe</h2>
<p>(d/M/y): {{ date | date: 'd/M/y' }}</p>
<p>(dd/MM/yy): {{ date | date: 'dd/MM/yy' }}</p>
<p>(d/MM/y): {{ date | date: 'd/MM/y' }}</p>

<h2>Short Time pipe</h2>
<p>{{ date | date: 'shortTime' }}</p>

<h2>Currency pipe</h2>
<p>(USD): {{ 3891.55 | currency: 'USD' }}</p>

<h2>Decimal pipe</h2>
<p>Pi: {{ pi }}</p>
<p>(1.2-2): {{ pi | number: '1.2-2' }}</p>
<p>1 is for the number before the decimal. 2-2 is the amount of numbers after the decimal</p>
<p>(1.4-4): {{ pi | number: '1.4-4' }}</p>
<p>Notice the numbers after the decimal are rounded up from 159 to 16.</p>

<h2>Percent pipe</h2>
<p>(0.50233): {{ 0.50233 | percent }}</p>

<h2>JSON pipe</h2>
<p>(No transform): {{ jsonObj }}</p>
<p>{{ jsonObj | json }}</p>

<h2>Slice pipe</h2>
<p>(2:5): {{ weekdays | slice: 2:5 }}</p>
<p>The 2:5 is the start and end index of the slice</p>
<p>(1:3): {{ weekdays | slice: 1:3 }}</p>
<p>The end index is optional</p>
<p>(0): {{ weekdays | slice: 0 }}</p>

<h2>Slice pipe with ngFor</h2>
<p>Slices can be used in loops with ngFor</p>
<ul>
  <li *ngFor="let weekday of weekdays | slice:2">{{ weekday }}</li>
</ul>

The example above shows Angular’s built-in pipes with some variations on the structures.

Pipes can also be used with Promises and Observables, although it’s mostly used to unwrap their values directly inside the template.

The Async pipe subscribes to a Promise or Observable and returns the latest value that was emitted. When a new value is emitted, the pipe then marks the component to be checked for changes.

When the component is destroyed, the pipe will automatically unsubscribe to avoid any potential memory leaks.

Let’s have a look at a simple example and go through it step by step.

1. First, we’ll create a Promise called ‘name’ that is of type string .

Example: app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

  name: Promise<string|null>;
}

2. Next, we’ll create a method called getName() that will assign some text to the name object after 5 seconds, asynchronously.

Example: app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

  name: Promise<string|null>;

  getName(): Promise<string> {
    return new Promise((resolve, reject) => {
      // After 5 seconds, add the name 'John Doe'
      setTimeout(() => {
        resolve("John Doe")
      }, 5000)
    })
  }
}

3. Next, we’ll assign it directly to the name object in the constructor.

Example: app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

  name: Promise<string|null>;

  getName(): Promise<string> {
    return new Promise((resolve, reject) => {
      // After 5 seconds, add the name 'John Doe'
      setTimeout(() => {
        resolve("John Doe")
      }, 5000)
    })
  }

  constructor() {
    this.name = this.getName();
  }
}

For the first 5 seconds, ‘name’ is a promise of type null . After 5 seconds it becomes a Promise of type string with text inside of it.

4. To access the string that we have inside the Promise, we use the Async pipe.

Example: app.component.html
<p>{{ name | async }}</p>

For the first 5 seconds, we don’t see anything, then the name appears.

As a good practice, we can show a message until the Promise is fulfilled by performing a null check on the unwrapped Promise.

Example: app.component.html
<!-- We can use a ternary expression -->
<p>{{ (name | async) ? (name | async) : "Please wait..." }}</p>

<!-- Or, we can use the ngIf directive with else -->
<p *ngIf="name | async; else loading">{{ name | async }}</p>

<ng-template #loading>
  <p>Loading...</p>
</ng-template>

We can use either a ternary expression or the ngIf directive.

The ternary expression is faster to type and easy to read at a glance. However, many developers consider ternary expressions a bad practice because they can be chained into an unreadable mess that’s hard to debug.

How to create custom Pipes

Angular allows us to create our own custom pipes.

As always, we can use the CLI to generate it and let Angular automatically register the pipe in the app.module.ts file.

To create a new custom pipe, we use the pipe keyword with the standard generation command.

Syntax: CLI command
ng generate pipe custom_pipe_name

So, let’s create a new pipe called ‘sqrt’ that we will use to calculate the square root of a number.

Example: Command
ng generate pipe sqrt

The command will create two files.

  • sqrt.pipe.spec.ts which is the file we use for unit testing. We won’t use it now so it can be safely deleted.
  • sqrt.pipe.ts which is the logic for our pipe.

Angular will also import and register the pipe in the app.module.ts file in the declarations array.

The file should look something like the following initially.

Example: sqrt.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sqrt'
})
export class SqrtPipe implements PipeTransform {

  transform(value: unknown, ...args: unknown[]): unknown {
    return null;
  }

}

Angular requires us to use the transform() method to contain the pipe’s logic.

1. We will only work with a single value of type number, so we can remove the rest from the method’s argument list and change the type and return value to number.

Example: sqrt.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sqrt'
})
export class SqrtPipe implements PipeTransform {

  transform(value: number): number {
    return null;
  }

}

2. Next, we’ll add our logic. To keep things simple we will just use the built-in Javascript sqrt() method.

Example: sqrt.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sqrt'
})
export class SqrtPipe implements PipeTransform {

  transform(value: number): number {
    return Math.sqrt(value);
  }

}

3. Finally, we can use our new pipe in a component.

Example: app.component.html
<p>{{ 25 | sqrt }}</p>
<p>{{ 100 | sqrt }}</p>
<p>{{ 175068 | sqrt }}</p>

Of course, this particular example is not very useful because Math.sqrt() already exists but it serves as an easy example.