How to Build Custom Pipes in Angular

In simple terms, pipes allow us to write display value transformation right inside the template. Angular comes with stock or built in pipes to transform your values to what you want the user to see. For instance, the Date Pipe, allows you to change the date format and other aspects related to the date.

To use a pipe inside the template, you use the pipe operator (|), the pipe on the right and the value on the left.

{{ dateValue | date }}

And you can pass arguments, such as the date format you wish to use or how you want a currency to be formatted.

{{ dateValue | date:format }}

And, you can chain multiple pipes if you wish, you just need to add the pipe operator after every pipe.

{{ value | pipe1 | pipe2 }}

The great thing about pipes is that they are applied to variables right before display and doesn’t require you to change the original value. Pipes make you code readable and re-usable, as compared to using components methods to transform method. This also makes it easier to track bugs and update if need arises. The built-in pipes are great, but can take you so far, now let’s see how we can build our own pipe.

Overview

Before we can get into the thick of it, let’s first go over what we are trying to achieve in this post. Let’s say you have a country code (ISO Standard) and wanted to transform that to either country name or currency used by the said country. This is a very simple pipe, but most pipes are mostly like this.

Your list of countries is in this format:

CA: {
  name: 'Canada',
  native: 'Canada',
  phone: '1',
  continent: 'NA',
  capital: 'Ottawa',
  currency: 'CAD',
  languages: ['en', 'fr']
},

Given the country code, you might want to get the country name, currency or official language. One way to achieve this, is to build a service, then inject it into the component controller. And then create a method to call it from your template. This requires you to add at least several lines of code to both your template and component controller (class). But with a pipe, it’s a matter of calling the pipe and passing an argument of the field you want:

{{ 'CA' | countries:'currency' }}

With a single line of code, you can transform your country code to the country name, currency or capital city of the country you want.

Building our Custom Pipe

We will start by creating a new angular project using Angular CLI (Angular 7, but this will work with Angular 6, 5 and I am guessing 4). If you already have a project, you can skip this part out.

$ ng new pipes-demo

NB: If you are using Angular 7, you will be prompted to add routing and which style sheet to use, select no and CSS in each case. This is because of CLI Prompts introduced in Angular 7.

Next, let’s use Angular CLI to generate a pipe, we shall call the pipe countries.

$ ng generate pipe countries

If you open the newly created pipe, it will look like this:

...
@Pipe({
  name: 'countries'
})
export class CountriesPipe implements PipeTransform {
  transform(value: any, args?: any): any {
    return null;
  }
}

Inside the transform method of the class Countries Pipe, is where our logic will end up. We will start by renaming the methods parameters, from value and args to code and field. Then, we can simply get the country through the by code (Our list is keyed using country code) and the field we want just like any other array.

transform(code: string, field: string): any {
  if (code) {
    return CountriesList[code][field];
  }
  return null;
}

NB: CountriesList is an object containing list of countries.

You can add any amount of logic inside the transform method, that you would add inside components controller. For instance, you can filter an array that is not keyed:

const country = CountriesList.filter(country => {
  if (country.code === value) {
     return country;
  }
});

return country[0][field];

Unlike component methods, a pipe can be used through your project without a lot of extra effort.

TIP: You can also inject services and other dependencies in your pipe to use them to transform your data, if need arises.

Using Pipes with Lazy Loading

If you are using lazy loading, the same limitation that apply to components apply to pipes. For instance, you cannot declare a custom pipe in more than one module. And a pipe must be declared in the module closest to the component you are using. To work around this, you need a shared module. This is where you will declare and export your pipe or pipes, among other directives and components.

You can generate a module inside the project using the ng generate module command:

$ ng generate module pipes-module

And then declare your pipe and export it inside the module:

@NgModule({
  declarations: [CountriesPipe],
  imports: [CommonModule],
  exports: [CountriesPipe]
})
export class PipesModuleModule {}

Now, you can just import the module in any lazy loaded module you want to use the pipes in.

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

You can learn more about lazy loading in Angular here.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.