Custom Pipes
Besides providing a set of standard out-of-box pipes the Angular framework provides support for creating your custom ones.
To create a custom pipe you need to import the @Pipe
decorator and apply it to your class.
import { Pipe } from '@angular/core';
@Pipe({
name: 'customDate'
})
export class CustomDatePipe {
// ...
}
You can give your class any name you want.
The Angular is going to use the @Pipe
decorator metadata when parsing component templates, in our case the pipe gets used as customDate
:
<element>
{{ <expression> | customDate }}
</element>
Also, your class should implement a PipeTransform
interface with a transform
method:
interface PipeTransform {
transform(value: any, ...args: any[]) : any
}
At runtime, the Angular calls your pipe's transform method providing original input value together with optional pipe parameters.
For example, if your myPipe
pipe expects to receive 3 additional parameters you can declare your transform
method like the following:
transform(value: string, p1: number, p2: number, p3: number) : string {
return value;
}
To get type checking support from TypeScript, you can also provide type definitions for your parameters and method return type.
You can now use your pipe in HTML templates like in the example below:
<element>
{{ 'hello world' | myPipe:1:2:3 }}
</element>
Implementing Custom Pipe
We are going to create a simple pipe that takes a numeric input value for a size of the file in bytes and produces a user-friendly output by transforming it to Kilobytes, Megabytes or greater units.
Let's start creating a custom pipe with a new project custom-pipes
by utilizing the Angular CLI tool.
ng new custom-pipes
You can use Angular CLI to generate the file-size
pipe scaffold.
ng g pipe pipes/file-size
The command above generates a pipe, basic unit test and updates main application module so that you can use this pipe across all the application components.
installing pipe
create src/app/pipes/file-size.pipe.spec.ts
create src/app/pipes/file-size.pipe.ts
update src/app/app.module.ts
The default pipe scaffold already contains a @Pipe
decorator applied and inherits the PipeTransform
interface:
// src/app/pipes/file-size.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize'
})
export class FileSizePipe implements PipeTransform {
transform(value: any, args?: any): any {
return null;
}
}
First, let's change the signature of the transform
method to accept bytes
and decimals
parameters of a number
type.
We also make both of them optional by providing default values and change the return type of the method to a string
.
// src/app/pipes/file-size.pipe.ts
@Pipe({
name: 'fileSize'
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number = 0, decimals: number = 2): string {
return null;
}
}
Converting Bytes
There are many different ways to convert a file size from bytes to other units of measurement.
For this example we are going to take the accepted answer from the following Stackoverflow question: Correct way to convert size in bytes to KB, MB, GB in Javascript
You can see the final implementation of the pipe below:
@Pipe({
name: 'fileSize'
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number = 0, decimals: number = 2): string {
if (bytes === 0) {
return '0 Bytes';
}
const k = 1024,
dm = decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
If no bytes
value provided the pipe returns a "0 Bytes" string,
for all other cases if converts input number to the most appropriate measuring unit.
The transform
method also takes into account the number of decimals to use after the point,
provided within the second decimals
parameter.
By default, it is going to take two numbers.
In order to see the pipe in action open the main application component template and append the following HTML block:
<h2>fileSize</h2>
<ul>
<li>520.12345 => {{ 520.12345 | fileSize }}</li>
<li>520.12345 => {{ 520.12345 | fileSize:4 }}</li>
<li>1024 => {{ 1024 | fileSize }}</li>
<li>5,347,737.6 => {{ 5347737.6 | fileSize }}</li>
<li>1,288,490,188.8 => {{ 1288490188.8 | fileSize }}</li>
</ul>
Now run the application with ng serve --open
command and you should see the following output: