In Angular when developing, you can sometimes find the need to transform a value before presenting it in the template. To achieve this, Angular has inbuilt functionality called pipes.

What is a pipe in Angular?

As explained in the angular official documentation, in Angular, pipes are used to transform data for display. Pipes are simple functions that you can use in template expressions to accept an input value and return a transformed value. Example:

<p> My name is {{ name | uppercase }}</p>

Why use a pipe in Angular?

To understand why pipes are important in Angular, there are two things we have to go through; The problem and SOLID software design principle.

The problem:

Imagine you want in your angular component you want to display a name in title case. In general this can be achieved by creating a function in the component that will perform the change first then add the value to the variable.

In app.component.html file:

export class AppComponent {
  name = 'JOhn DoE';

  ngOnInit(): void {
	name = this.titleCase(name)
  }

  titleCase(value: string) {
    return value.replace(
      unicodeWordMatch, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase()));
  }
}

And in app.component.ts file:

<h1>Hello {{name}}</h1>

The output will be:

Output (hello world)

This approach brings about the required results, but then in turn, it will have several problems. To name a few:

  1. The function is not testable. If we are to test the titleCase method, we have to first initialize the entire component and its dependencies.
  2. This is a violation of Single Responsibility Principle in the SOLID design pattern. Which means, each component that requires to change text to title case, it has to define this method, then use it to change the case. The titleCase method will repetitively be redefined. Any repetition in code risks removing the ability to maintain and scale the code in long term.

The alternate solution:

The alternate solution for the two cases, is to create the titleCase method in a separate .ts file then export it and import it in every component that you wish to use it in. In utils.ts :

function titleCase(value: string){
  return value.replace(
    unicodeWordMatch, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase()));
}

export default titleCase

Then again in app.component.ts change to first import the titleCase method then use it as we did before, but the this keyword is removed.

...
import titleCase from './utils';


....
export class AppComponent {
  name = 'JOhn DoE';

  ngOnInit(): void {
	  this.name = titleCase(this.name)
  }
}

And app.component.html remains the same. This method works. But it has one or more downsides.

  1. You have to import the file in every component that you are using the method. This will become very tedious during refactor. And for the methods that are mainly required to change the template then, the component is not supposed to know about it. That is why angular has it's own dependency injection mechanism
  2. You will have to manually strategically call this method to react to changes and apply them to the template. In the example code above, it is placed inside ngOnInit method, which applies the case change on component initialization. It does not know about change detection.

Pipes to the rescue, how to use Angular pipes

Pipes are meant to solve all the above challenges. In summary, you create a pipe class that extends Angular PipeTransform class, you define your logic inside the transform method, the return value of the transform method is the value that will passed to the template. Then import the pipe into the module, and use it your template.

To create a pipe, use the Angular CLI to generate the pipe.

ng g pipe title-case

The command will create a title-case.pipe.ts file with TitleCasePipe class boilerplate.

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

@Pipe({
  name: 'titleCase'
})
export class TitleCasePipe implements PipeTransform {

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

}

The transform method will be called by Angular to perform the transformation. That is where we put our logic. The class will then become:

...
export class TitleCasePipe implements PipeTransform {

  transform(value: string, ...args: unknown[]): string {
    const unicodeWordMatch = /\w\S*/g;
      return value.replace(
        unicodeWordMatch, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase()));
  }

}

The CLI generate command will also make modifications on the app module, to include the pipe in the declarations. Including the pipe inside the declarations will tell angular to load it during the run time, which means there won't be the need to import it in your components within the same module.

...
@NgModule({
  declarations: [
    AppComponent,
    TitleCasePipe
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

For all the components inside the app module, the titleCase pipe will be available.

The pipe syntax

Pipes are used inside templates, to transform data directly. The pipe syntax can be written as:

{{ variable | pipe1: argument1 : ... : argumentn | pipe2: arguments...}}
  • variable - The name of the variable of which to transform it's values. From our previous example, the variable is name.
  • pipe1 - The first name of the pipe that the variable will pass through.
  • argument1 to argumentn - List of values that should be passed to the pipe to assist the transformation. The arguments can be multiple, separated by a semi colon.
  • pipe2 - The second pipe that the first value returned should pass to. The value is passed through the pipes sequentially from left to right.

To continue with the previous example, the code inside app.component.html becomes:

<h1>hello {{name | titleCase}}</h1>

Congratulations, now you have successfully made a fully functioning pipe.

Pipes and change detection

When the value of the variable which is transformed by a pipe is changed by means of any DOM events, or other events recognized by Angular change detection system, Angular will pass the new value through the pipe, and the transformed value will be returned and assigned to the HTML template accordingly.

This directly solves the problem we faced when attempting to write functions by our selves. And also they are inherently testable, in fact, when you run the command to generate the pipe, it will generate two classes, one of them being a test for the pipe.

When to use Angular pipes

As defined, explained and demonstrated, pipes in Angular are functions that transform data in the template. Whenever you want to change some value before rendering in the template, pipes are your first choice tools.

Furthermore, the extensive reusability of pipe makes them suitable to even the largest applications. Angular ships with great pipes out of the box that you can use immediately in your project. And for the pipes that are not available in the core library, Angular provides very easy mechanism  to generate your own custom pipes using the Angular CLI. A few of those pipe will be demonstrated below.

Example pipes that are shipped with Angular out of the box

Title case pipe

Will transform text to title case. Where the first letter of each word is capitalized and the rest of the word to lower case. The words are separated by whitespace characters such as space, tab or a line-feed character.

@Component({
  selector: 'titlecase-pipe',
  template: `<div>
    <p>{{'some string' | titlecase}}</p> <!-- output: "Some String" -->
    <p>{{'one,two,three' | titlecase}}</p> <!-- output: "One,two,three" -->
    <p>{{'true|false' | titlecase}}</p> <!-- output: "True|false" -->
    <p>{{'foo-vs-bar' | titlecase}}</p> <!-- output: "Foo-vs-bar" -->
  </div>`
})
export class TitleCasePipeComponent {
}

Lower and Upper case pipes

Will transform any text/words to all lowercase.

@Component({
  selector: 'lowerupper-pipe',
  template: `<div>
    <label>Name: </label><input #name (keyup)="change(name.value)" type="text">
    <p>In lowercase: <pre>'{{"I will be loWERed" | lowercase}}'</pre>
    <p>In uppercase: <pre>'{{"I will be capitalized" | uppercase}}'</pre>
  </div>`
})
export class LowerUpperPipeComponent {
}

Percent pipe

The percent pipe will append the percent pipe to any number and return a string.

@Component({
  selector: 'percent-pipe',
  template: `<div>
    <!--output '26%'-->
    <p>A: {{a | percent}}</p>

    <!--output '0,134.950%'-->
    <p>B: {{b | percent:'4.3-5'}}</p>

    <!--output '0 134,950 %'-->
    <p>B: {{b | percent:'4.3-5':'fr'}}</p>
  </div>`
})
export class PercentPipeComponent {
  a: number = 0.259;
  b: number = 1.3495;
}

Conclusion

From this deep dive into Angular pipes, you can easily incorporate them in your application or project and fully reap their benefits. Angular pipes are one of the perks of using Angular, since you can barely find the same constructs in other front end frameworks.