Telerik blogs
AngularT2_Light_1200x303

What is the difference between pure and impure pipes, and how can we use each in Angular?

Angular provides us with an organized way to build frontend web apps. One entity that it has is pipes.

Pipes let us render items in component templates in the way we want. There are two kinds of pipes in Angular. In this article, we will look at the two types—pure and impure pipes—and what they do.

Pure and Impure Pipes

A pure pipe is a pipe that is run when a pure change is detected. A pure change is a change to a primitive JavaScript input value like strings, numbers, booleans, symbols or an object reference change.

Pure pipes must be pure functions. Pure functions take an input and return an output. They don’t have side effects.

Changes within objects are ignored with pure pipes. When any of these changes take place, the pipe will run and the latest change will be rendered.

Impure pipes are pipes that can detect changes within objects. Changes within objects include things like changing array entries or object properties.

Define a Pipe

To add a pipe into our project, we can use the ng g pipe command.

For instance, we write:

ng g pipe localedate

in the root of our project folder to create the localedate pipe. It’ll register the pipe in the module and generate the files for the pipe automatically.

Then we can go to localedate.pipe.ts and add:

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

@Pipe({
  name: "localedate",
})
export class LocaledatePipe implements PipeTransform {
  transform(value: Date, ...args: unknown[]): string {
    return value.toLocaleDateString();
  }
}

to modify the transform method of the LocaledatePipe class.

All pipes implement the PipeTransform interface, which specifies that the pipe class must have the transform method. In it, we get the value parameter and then we return a new value derived from it.

In this example, we return the localized date string by calling value.toLocaleDateString.

value can be any type of value. We use the name value to reference the pipe in our template. Then we can use it in a component by writing:

app.component.ts

import { Component } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  date = new Date(2022, 1, 1);
}

We specify the date variable that we want to format into a localized date string. And we will do that with the localedate pipe we created. Then we use it in our template with:

app.component.html

<div>{{ date | localedate }}</div>

We put the pipe name after the variable we want to format in our component template. As a result, we should see something like 2/1/2022 displayed on the screen depending on the locale we are in.

Since we did not set the type of the pipe, it is a pure pipe since pipes are pure by default.

Pipes can take arguments. And we can get them from the args parameter in the transform method. For instance, we write:

localedate.pipe.ts

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

@Pipe({
  name: "localedate",
})
export class LocaledatePipe implements PipeTransform {
  transform(value: Date, ...args: string[]): string {
    const [locale = "en"] = args;
    return value.toLocaleDateString(locale);
  }
}

to make the args rest parameter a string array in the transform method. In it, we get the first argument from the args array with destructuring.

We set locale to en by default. And we call toLocaleDateString with it to return a date string in the locale we want.

Then we update app.component.html to have:

<div>{{ date | localedate: "fr" }}</div>

to display the date in the French locale. So we should see 01/02/2022 displayed.

Impure Pipes

We can create impure pipes to re-render templates when an object’s content like object properties or array entries change.

For instance, we can create the sorted pipe by running:

ng g pipe sorted

Then in sorted.pipe.ts, we write:

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

@Pipe({
  name: "sorted",
  pure: false,
})
export class SortedPipe implements PipeTransform {
  transform(numbers: number[], ...args: unknown[]): number[] {
    return numbers.sort((a, b) => a - b);
  }
}

to add code to call numbers.sort to sort the numbers in ascending order in the transform method. A new array with the sorted numbers is returned.

We set pure to false so that we can use it to re-render the sorted numbers array when we change it.

Then in app.component.ts, we write:

import { Component } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  numbers = [Math.random(), Math.random(), Math.random()];

  generateNumber() {
    this.numbers.push(Math.random());
  }
}

We add the numbers array into the component and set it to an array with some random numbers.

Then we add the generateNumber method that appends a new random number into the numbers array with push.

Next, in app.component.html, we write:

<div>
  <button (click)="generateNumber()">Generate Number</button>
  <ul>
    <li *ngFor="let n of numbers | sorted">{{ n }}</li>
  </ul>
</div>

We add a button that calls the generateNumber method we created in the component. And then we use our sorted pipe to return a sorted version of the numbers array. We then render each value of the sorted array in a li element.

Now when we click on the Generate Number button, we see the list of numbers are automatically sorted because we make the sorted pipe impure by setting the pure property to false.

If we set pure to true or remove the pure property, we see that the numbers list won’t get sorted when we click the Generate Number button.

Making the pipe impure will make it watch for changes in the numbers array’s contents.

We can use impure pipes with objects to make it run when an object’s content changes. For instance, we make the numbers interface with:

ng g interface numbers

Then in numbers.ts, we write:

export interface Numbers {
  foo: number;
  bar: number;
  baz: number;
}

Next, in sorted.pipe.ts, we write:

import { Pipe, PipeTransform } from "@angular/core";
import { Numbers } from "./numbers";

@Pipe({
  name: "sorted",
  pure: false,
})
export class SortedPipe implements PipeTransform {
  transform(numbers: Numbers, ...args: unknown[]): Numbers {
    const sortedEntries = Object.entries(numbers).sort(
      ([, val1], [, val2]) => val1 - val2
    );
    const sortedObj = Object.fromEntries(sortedEntries);
    return sortedObj as Numbers;
  }
}

In the transform method, we get the key-value pair in numbers with Object.entries.

And then we call sort with a function that gets the value from the second entry of the array of the key-value pair array we are sorted. We return the val1 - val2 to sort them in ascending order.

Then we convert the array back to an object with Object.fromEntries and return it.

Next, in app.component.ts, we write:

import { Component } from "@angular/core";
import { Numbers } from "./numbers";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  numbers = { foo: Math.random(), bar: Math.random(), baz: Math.random() };

  generateNumbers() {
    this.numbers.foo = Math.random();
    this.numbers.bar = Math.random();
    this.numbers.baz = Math.random();
  }

  toJson(obj: Numbers) {
    return JSON.stringify(obj);
  }
}

We added the generateNumbers function to change the values of the number properties. And we have a toJson function that returns obj as a JSON string.

In app.component.html, we write:

<div>
  <button (click)="generateNumbers()">Generate Numbers</button>
  <div>{{ toJson(numbers | sorted) }}</div>
</div>

to call toJson with the numbers object transformed by our sorted impure pipe to sort the object properties so that they’re ordered by the magnitude of the numbers in ascending order.

As a result, when we click on Generate Numbers, we see the object rendered will always be sorted by the magnitude of the number properties.

Conclusion

Pipes let us render items in component templates in the way we want.

There are two kinds of pipes in Angular—pure and impure pipes. A pure pipe is a pipe that is run when a primitive JavaScript input value like strings, numbers, booleans, symbols or an object reference change.

Pure pipes must be pure functions. Pure functions take an input and return an output. They don’t have side effects. Changes within objects are ignored with pure pipes. When any changes take place, the pipe will run and the latest change will be rendered.

Impure pipes are pipes that can detect changes within objects and arrays.


About the Author

John Au-Yeung

John Au-Yeung is a frontend developer with 6+ years of experience. He is an avid blogger (visit his site at https://thewebdev.info/) and the author of Vue.js 3 By Example.

Related Posts

Comments

Comments are disabled in preview mode.