Learn the Angular Pipe in-Depth + Tips on boosting performance using Pipe

Tags: webdev, javascript, angular, html
Published: 2021-06-10 12:30:21

Hello everyone 👋,

In this article, we are going to cover what is Angular Pipe, how to create it and utilize it in our template. Additionally, we will learn how to boost performance with a custom Pipe. 

### What is Pipe in Angular?

From  [Angular Documentation](https://angular.io/guide/pipes#transforming-data-using-pipes), 
> 
Use pipes to transform strings, currency amounts, dates, and other data for display. Pipes are simple functions you can use in template expressions to accept an input value and return a transformed value.

Usecase of pipe:

1. Use **DatePipe** to convert the Date object to a human-readable format.
2. **UpperCasePipe** can be used to convert text to Uppercase.
3. **CurrencyPipe** helps to transform a number to a currency string, formatted according to locale rules.

The best advantage to use Pipe is, while transforming the data, it doesn't modify the original data. Let's see it in action.

### Creating a Custom Pipe

You can create a custom Pipe only when it is not available in the [built-in Pipe](https://angular.io/guide/pipes#transforming-data-using-pipes).

We are going to create a Pipe which filters the items as fruits/vegetables based on `type` property.
```javascript
const items = [
    {
      name: 'Tomato',
      type: 'vegetables',
    },
    {
      name: 'Orange',
      type: 'fruits',
    },
    {
      name: 'Apple',
      type: 'fruits',
    },
  ];
```

Our **objective** is to show all the items in the first section, then show only fruits in the second section & vegetables in the third section.

First, let's create a Pipe with the below `ng` command.

```
ng generate pipe filter-items
```
The command creates a file as `filter-items.pipe.ts` with the following code.

```typescript
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'filterItems'
})
export class FilterItemsPipe implements PipeTransform {
 
transform(value: unknown, ...args: unknown[]): unknown {
    return null;
  }
}

```
Let's see it in detail on the created code.

1. ng command created a class and applied `@Pipe` decorator with name as a property. This is the name of the created pipe. Then it implements the `PipeTransform` interface to perform the transformation.

2. Angular invokes the `transform` method with the value of a binding as the first argument, and any parameters as the second argument in list form, and returns the transformed value.

Imagine, the `transform` is just a function, to which the original item is passed as a first argument and any parameters as the second argument in list form.

Now, update the `transform` function to filter the items based on the `type` property. 

```typescript
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'filterItems'
})
export class FilterItemsPipe implements PipeTransform {
 
transform(value: any[], type: string): any[] {
    return value.filter(el => el.type === type);
  }
}

```

### Applying the Custom Pipe to template

This is our `app.component.ts` which has `items` and a method `addItem`. 

```typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styles: []
})
export class AppComponent {

  items = [
    {
      name: 'Tomato',
      type: 'vegetables',
    },
    {
      name: 'Orange',
      type: 'fruits',
    },
    {
      name: 'Apple',
      type: 'fruits',
    },
  ];

  addItem() {
    this.items.push({name: 'Lemon', type: 'fruits'});
  }

}

```

In the `app.component.html`, we are Iterating the items and

1. Showing all the items in the first section
2. Applied `filterItems` Pipe in the 2nd section and passed `fruits` as a second argument to it.
3. Applied `filterItems` Pipe in the 3rd section and passed `vegetables` as a second argument to it.

When we apply a pipe in the template, automatically the value on which pipe is applied is passed as a first argument to `transform` and an additional argument can be passed by adding `:(colon)` and then value.

```html
<div>
  <h1>Original data</h1>
  <div *ngFor="let item of items">
    <p>{{item.name}} - {{item.type}}</p>
  </div>

  <h1>Filtered fruits</h1>
  <div *ngFor="let item of items | filterItems: 'fruits'">
    <p>{{item.name}} - {{item.type}}</p>
  </div>
  
  <h1>Filtered Vegetables</h1>
  <div *ngFor="let item of items | filterItems: 'vegetables'">
    <p>{{item.name}} - {{item.type}}</p>
  </div>

  <button type="button" (click)="addItem()">Add Item</button>

</div>
```

This is the visual representation of how our `Pipe` is applied to the template and the type is passed as a second argument.

![visual representation of our pipe](https://cdn.hashnode.com/res/hashnode/image/upload/v1623241903019/8QDcMUJlM.png)

This is the output after applying our pipe.


![Screenshot from 2021-06-09 18-36-49.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623244052103/fXY1OnWlB.png)

Yay! 😍 this is what we wanted. You can see that filtering the data happened by without modifying the original items.

Let's try clicking the `Add Item` button and see if `lemon` is shown in the fruits section.


![Screenshot from 2021-06-09 18-42-12.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623244362377/Nixqcpxkv.png)

`Lemon` is shown in the original data section, but it doesn't show in the `fruits` section. 

**Why?** 🤔

The reason is, when a Pipe is created, it will be set as a **Pure Pipe** by default. Also, in the `addItem` method, the `lemon` is pushed to the same array. So, Angular doesn't know that there is a change in the value.  [Click here](https://angular.io/guide/pipes#detecting-impure-changes-within-composite-objects) to learn more about it from `Angular` documentation.

To fix it, the Pipe can be changed to **Impure Pipe** which runs the `transform` function on all Angular Change Detection (or) create a new array every time when a new item is added to it.

First, we will see the first approach on changing to Impure Pipe.

Open the created pipe, and add `pure` to false in the `@Pipe` decorator.

```typescript
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'filterItems',
  pure: false
})
export class FilterItemsPipe implements PipeTransform {
 
transform(value: any[], type: string): any[] {
    return value.filter(el => el.type === type);
  }
}

```

Now, if you click the `Add item`, Lemon will be shown in the `fruits` section.


![Screenshot from 2021-06-09 18-50-09.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623244821984/G4740xGkF.png)

Setting `Pure` to false (Impure Pipe) solves the issue but let's discuss why it doesn't work with Pure Pipe. 

### Pure vs Impure Pipe

1. **Before Ivy**, Pure Pipe creates only **one instance of a class ** whereas Impure pipe **creates many instances** if it used in multiple places. In our example, we have used `filterItems` pipe for the 2nd and the 3rd section. So, it will create 2 instances of the class.

2. For Pure Pipe, the `transform` function in the Pipe will be called only when there is a change in the `@Input()`, change in the value passed to the pipe (for Object & Array it should be new reference) or forcefully running the change Detection with `changeDetectorRef`.  For Impure Pipe, Angular executes the `transform` every time it **detects a change with every keystroke or mouse movement.** 

If you are not using the Ivy engine, then if your page has 30 components uses Impure Pipe, and whenever there is a change in the mouse move, 120 times the `transform` function will be triggered with 30 instances of Pipe. 🤯

If you are using Ivy Engine, then be it Pure or Impure pipe, multiple instances will be created. 
But the condition on triggering the `transform` function and the no of times is called are depends on the Pure or Impure Pipe.

As the latest Angular version has Ivy set as true by default, we will see the examples considering the view engine as Ivy.

In our pipe, the `id` property is created and a unique id is assigned to the instance of the class through the `constructor`.

```typescript
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'filterItems',
  pure: false
})
export class FilterItemsPipe implements PipeTransform {
// unique id for the instance 
id;
 
// assigns the unique id for the instance
constructor() {
    this.id = Math.floor(Math.random() * 10);
    console.log('unique id => ', this.id);
  }

transform(value: any[], type: string): any[] {
    return value.filter(el => el.type === type);
  }
}

```

Refresh the application and open the console tab in the DevTools. 

As we've used pipe 2 times, one for `fruits` and the other for `vegetables`, 2 instances of the pipe is created with unique id as `6` & `3`. And the `transform` function is called 8 times, 4 for each instance.

![Screenshot from 2021-06-09 19-10-30.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623246091054/h2l8Emj4h.png)

Now, if the `Add Item` button is clicked, again `transform` function called 4 times, 2 for each instance.

![Screenshot from 2021-06-09 19-14-32.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623246316662/QH7FQj-X4.png)

Additionally, Angular runs this `transform` function every time it detects a change with every keystroke or mouse movement. 

Just Imagine, a bigger application that has 100+ components in UI with many impure pipe. 🤯

To fix this performance issue, **Pure Pipe** should be used with some modification in the application code.

### Boost Performance with Pure Pipe

Let's fix this performance issue by following the below steps.

Change the Pipe to `Pure` by setting `pure: true`

```typescript
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'filterItems',
  pure: true
})
export class FilterItemsPipe implements PipeTransform {
// unique id for the instance 
id;
 
// assigns the unique id for the instance
constructor() {
    this.id = Math.floor(Math.random() * 10);
    console.log('unique id => ', this.id);
  }

transform(value: any[], type: string): any[] {
    return value.filter(el => el.type === type);
  }
}

```
Then, open `app.component.ts` and update the code in `addItem` method.

```
 addItem() {
    // push an element doesn't change reference.
    // this.items.push({name: 'Lemon', type: 'fruits'});

    // Angular Change Detection runs when it sees a change in the Array as new reference
   this.items = [...this.items, {name: 'Lemon', type: 'fruits'}]; 
  }
```
Run the application and see the console tab.

2 instance of the pipe is created (because of Ivy), and the `transform` function is triggered 2 times, one for each.

![Screenshot from 2021-06-09 19-50-19.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623248447536/nAXH0XE4T.png)

Now, click `AddItem` and see the console.

![Screenshot from 2021-06-09 19-52-14.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623248566759/cJ3Seh7mn.png)

The `transform` function is triggered 2 times, one for each.

### Conclusion

Comparing with Pure with Impure Pipe, using Impure Pipe triggered 8 times the `transform` function first, and on clicking `AddItem`, 4 times it triggered & also whenever this is a mouse over or user interaction happens it will call multiple times again and again. But using Pure pipe, it triggers 4 times totally.

So, always use the Pure Pipe.

Thanks for reading the article, I hope you liked it!

You can connect with me on [Twitter](https://twitter.com/yuvgeek) & [Github](https://github.com/yuvgeek).