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.
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.
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.
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.
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.
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.