In this post we will focus on Pipes: definition, built-in, custom implementation and testing. To better understand the advantages they offer we will focus on performance optimization. I will create a component and will display some data related to the students that will be transformed using pipes.
For this article I have created a stack blitz project and we will discuss all the implementations step by step here.
Definition
According to Angular Official Documentation: “Pipes are simple functions used to transform data into a suitable display format.”.
Pipes work as displayed below: your input goes through the pipe where it’s transformed through a function to the desired display value.

Built-in pipes and Usage
Angular provides some built-in pipes that we can take advantage of. Pipes provided are both impure pipes such as async and pure pipes such as Currency Pipe, Decimal Pipe, LowerCase Pipe, UpperCase Pipe etc. A list with all the built-in pipes are provided here: Angular Pipes.
Let’s see how we can use them, by considering the titleCase which is a built-in pipe.
<p>{{ag dEv bLOg’ | titlecase}}</p> <!– output is expected to be “Ag Dev Blog” →
So we are basically telling that the text that will be displayed in this paragraph, will be transformed by the titleCase pipe and in the browser you will see the transformed value. When we create a custom pipe, later in this article we will get a better understanding on how it works.
Chaining
Another important concept relating to pipes is chaining. Chaining means using multiple pipes to achieve the desired transformation. The output of the transformation of the first pipe will serve as input to the second pipe and so on until we have transformed the data to the desired output.
In the example I have provided, we have a list of students with names, degrees and points. Degrees have these values: Informatics, Economics and Architecture. Let’s suppose we want to visualize degrees uppercase and only the first three letters. We take advantage of pipes chaining and built-in pipes: uppercase and slice as implemented in pipes chaining:
<div matLine>
{{student.name | titlecase}} :
{{student.degree | uppercase | slice: 0: 3}}
</div>
Custom Pipe Implementation
Each of the students has points, which is an array of numbers and we would like to concat these values into a string with a custom separator. In order to implement this custom pipe we can use cli to generate it automatically:
ng generate pipe concat
We can also create it manually by creating a new class ConcatPipe, which will use @Pipe Decorator and will implement the PipeTransform Interface. PipeTransform Interface contains the transform function which can be customized upon our needs regarding the arguments and the return value. After implementing we should declare it inside the Module.
After choosing any of the ways mentioned above, in the end we will have this implementation:
Pipe({
name: 'concat'
})
export class ConcatPipe<T> implements PipeTransform {
transform(values: Array<T>, separator?: string): any {
return !!values ? values.join(separator) : '';
}
}
Now we can use this pipe to concat the points into a string:
<div matLine>{{student.points | concat }}</div>
If we want join points using and operator we just change the separator value as below:
<div matLine>{{student.points | concat: ' and ' }}</div>
Testing Pipes
After implementing pipes, assuming that we will be using this transformation in many places in our app it is a good practise to test, so we can be sure we will always get a correct result. For concat pipe we want to make sure this behavior:
- Pipe instance gets created successfully.
- If we do not provide a separator, comma will be used to join the values.
- If a specific separator is provided, the joined result will be using that.
- If there is only one element, it should use no separator.
- If the array is null or undefined, the result should be an empty string.
Here you can find all the tests implemented and if you run them all the cases are covered as expected.
Performance
After we have understood how pipes work, now our focus will be on performance optimization. We will use concat pipe to transform the data and also will declare the function getPoint inside the student component like this:
getPoint(student: Student): string {
const points = student.points;
return !!points ? points.join() : '';
}
Basically the function is the same as in our concat pipe, so the result will be the same. If you print a message in both functions: getPoints and concat transform we will see the differences.
Each of the students has a Add Point button to add a new random point for the selected student.
If you click on the button, you will see that getPoints function will be called in our example three times, the total number of elements. Angular has detected a change in the list and will re-render the content, so each of the elements will call getPoints. While the pipe transform function is called only once, because it is a pure function that has detected only one change.
For our list with three elements it is still fine, but if you consider a larger list and more interaction in the UI using a function inside the component will cause performance issues.
Another advantage of using pipes, is code reusability. You can declare a pipe once, and take advantage of the transform function throughout the application.
Pure and Impure Pipes
Angular provides two types of pipes: pure and impure. By default, when you create a custom pipe it will be pure. In order to understand the distinction between these two types, we will need to first understand pure and impure functions.
By definition, a function is called pure if it always returns the same value when the same arguments are passed. On the other hand an impure function does not return the same value for the same input because it depends on external values.
Same logic will be applied to pipes. A pure pipe is a pure function that will detect a change in the input: either a value change for a primitive data type or a reference change and then will return the same value for the same arguments. While an impure pipe can be dependent on some external variables so will produce a different output overtime or the inputs can be mutable.
Let’s consider JsonPipe, even though it does not have an internal state, the inputs passed are mutable. If the input is mutable, we need to call the pipe on every change detection cycle.
To display the difference between these two types of pipes I have created a pure pipe and an impure pipe that calculate the sum of elements. The difference in implementation, is that the impure pipe is marked as ”pure: false”:
@Pipe({
name: 'impureSum',
pure: false
})
Now when you add a new point a student, you will see that the pure pipe will transform the data of only the changed student, while the impure pipe will update all the elements.
As you can see the impure pipe is less efficient and you should be careful when using because this may overuse your system resources in case of inappropriate use.
Conclusions
To sum up, in this article we went step by step from an introduction to pipes to creating a new custom pipe to concat the elements of an array. After creating the concat pipe we created the tests to ensure that the pipe will behave correctly regardless of the input. Next step was to analyze the performance optimization pipes offered. Lastly, we went over and explained the differences between pure and impure pipes and also implemented the pure and impure sum pipes.