Angular Pipes are used for transforming the data.
Angular provides many built-in pipes that serve many of data transformation requirements.
But there are certain scenarios where built-in pipes do not fulfill the purpose and you need to write your own logic to transform data.
For such cases, angular provides support to create our own custom pipes containing self-written logic.
A custom pipe is a typescript class(just like a component, module, service or some other custom class) with a pre-defined structure but having our own logic.

Steps to create a custom pipe

In order to create a custom pipe, you need to follow some steps to that your pipe is written as per the pre-defined contract which is also followed by angular built-in pipes.

  1. Create a typescript class implementing PipeTransform interface from @angular/core package.
  2. This interface has a single method transform whose signature looks as below:

    transform(value: any, ...args: any[]): any;
    Above signature means that it should accept 1 value which is mandatory and one or more arguments which are optional. The values can be of any type. Also, it should return a value(which is obvious).
  3. The typescript class should implement the above stated transform method. It is this method which will contain the custom data transformation logic performed by the pipe.
  4. Annotate the class with @Pipe annotation from @angular/core package to indicate that this class will be a pipe.
    This annotation will have a name field whose value will become the name of the pipe. Example, if we consider the built-in angular uppercase pipe, the value of this field would be uppercase.
  5. Finally, register this pipe class inside declarations array of the application’s root module or the module in which you are using this pipe

Thus a custom pipe with the name custompipe would look as below

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
     name: 'custompipe'
})
export class SubstringPipe implements PipeTransform {
   transform(value: string, arguments?: any):any {
      // add transformation logic here and return value
      return value;
   }
}

Please note the ? after arguments declaration.
In typescript, it indicates that this argument is optional while calling a method. In the HTML template, a pipe will be called using its name as shown below

{{ value / variable | custompipe }}

If there are arguments for passing to the pipe, then they are separated by colon(:) as below

{{ value / variable | custompipe:argument1:argument2 }}

Creating Custom Pipe: Example

Suppose you want to create a pipe which accepts a string as value and returns a portion of this string.
This is often useful to generate summaries where a large content is shortened to show only the summary.
Let’s start creating this pipe by following the above listed steps.

Create a class which implements PipeTransform interface and its transform method and is annotated with @Pipe annotation. Name of the pipe will be substring.

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

@Pipe({    
  name: 'substring' 
}) 
export class SubstringPipe implements PipeTransform {    
  transform(value : string, args? : any) : any {       
    return value.substring(0, 50) + '...';    
  } 
}

This pipe will return the first 50 characters of the string supplied and appends three periods(…) to the result.

It will be used in the HTML template as

<div> 
  {{ 'This is a website providing solution to technical problems' | substring }} 
</div>

Note that the pipe can also be applied to a variable declared in the component class which holds some string value.
And don’t forget to add this pipe to the declarations array of the component’s module where you are using the pipe as below

@NgModule({ 
  imports: [       
    CommonModule,
    ChildModule
  ], 
  declarations: [
    ParentComponent,
    SubstringPipe
  ], 
  exports: [ParentComponent] 
})

otherwise you will get an error like

The pipe ‘substring’ could not be found (“

<div>
{{ [ERROR ->]’this is a website providing solution to technical problems’ | substring }}
</div>

Adding arguments to Pipe

The above pipe is a generic pipe that just returns first 50 characters of the string which it receives.
Now we want to customize it so that we can take control of the length and portion of string returned.
Till now, it returns the string starting from the beginning but we want that it returns the string from and till the location supplied to it.
For these purposes, we add arguments to it. Let’s see how.

transform() method supports any number of arguments to it.
We modify the transform() method to receive 2 arguments: a starting index and an end index and modify the logic so that it returns the string only between these two indexes.

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
   name: 'substring'
})
export class SubstringPipe implements PipeTransform {
   transform(value: string, startIndex?: number, endIndex?: number):any {
       // check if no arguments are supplied then return 50 characters: Default behavior
       if (!startIndex && startIndex != 0) {
          return value.substring(0, 50) + '....';
       }
       // check if only start index is provided, then return string starting from start index till the end
       if (!endIndex && endIndex != 0) {
          return value.substring(startIndex);
       }
       // if both are provided, then return string between those
       return value.substring(startIndex, endIndex) + '...';
   }
}

Note that both startIndex and endIndex are optional and are of type number instead of any.
When you are sure about the type of arguments, then you are free to declare them with their expected type else give them any type.
Following are the usages of this pipe both with and without arguments and their output

<!-- Without any arguments, substring of 50 characters 
Output: This is a website providing solution to technical .... --> 
<div>
  {{ 'This is a website providing solution to technical problems' | substring }} 
</div> 

<!-- With only start index as argument with value 0, meaning start of string 
Output: This is a website providing solution to technical problems --> 
<div>
  {{ 'This is a website providing solution to technical problems' | substring:0 }} 
</div> 

<!-- With only start index as argument with value 3, meaning string from 3rd character 
Output: s is a website providing solution to technical problems --> 
<div>
  {{ 'This is a website providing solution to technical problems' | substring:3 }} 
</div> 

<!-- With both start and index as arguments with value 0 and 20, meaning string of 20 characters 
Output: This is a website pr... --> 
<div>
  {{ 'This is a website providing solution to technical problems' | substring:0:20 }} 
</div>

Note that arguments are separated by colon(:) while calling the pipe.

Practical Usage of Custom Pipe

Creating custom pipes can be useful to get data in the required format which is not always possible by built-in methods. Some practical scenarios where custom pipes can be utilized are:

  1. Sorting an array. Provide an array to the pipe and it will return the sorted array. An argument can be used to decide whether the sorting should be in ascending or descending order.
  2. Adding some default text or symbol to a string. Suppose you have a string and you want to append an asterisk to it. Custom pipe can be useful here. Instead of asterisk, you can pass the symbol or text to append as an argument.
  3. Raising a number to a power. Create a pipe which generates cube of a number or number raised to the power of 10. Pass the power to be raised as argument.

There are many more usages of a custom pipe and you will be able to figure it out on your own once you learn about creating your own pipe.