Introduction to Custom Pipes in Angular


Isn't it fun,

to instantly listen to a blog on the go? PLAY !

 
 

If you're new to Angular and the Pipes concept before we get started, let's show what a Pipe is before we move on to showing a Custom Pipe.

Angular comes with a few built-in pipes that we can use in any module we write according to the CommonModule framework.

Table of Content

Here are some usual suspects we might encounter with the built-in Pipes of Angular:

  1. DatePipe: Used for parsing Date objects
  2. UpperCasePipe: Used for uppercase-ing Strings
  3. LowerCasePipe: Used for lowercase-ing Strings
  4. CurrrencyPipe: Used for formatting currencies
  5. AsyncPipe: Used for unwrapping asynchronous values like Observables

Just like we would a function, we can think of Pipes in Angular. A function will take parameters and return something new to us - and that's just what pipes do! We should pass a valid date and return a string value that is nicely formatted for the user interface. And here, the word UI is important because pipes are usually used to transform data between our model and view (the UI)!

This is the nature of a pipe!

How do we use a pipe, then? Suppose some basic HTML portion with a Date stamp binding:

{{myDateValue|date:'M/d/yy' }}

This could be made with the formatted date as above. But that's a valid argument for using pipes! We just don't want to fetch data and then loop through all this and convert every date from a date object to a string because we'd lose the functionality of the native date object and repeat values. Using a pipe and enabling it to parse for us is super convenient!

We are ready to begin venturing into Custom Pipes now! This will allow us to use a function to generate our input and output based on what we provide. Let's dive inside!

Custom Pipes in Angular

Let's assume that an image has just been uploaded through a drag and drop zone, and we get some of the data from it. A simplified file object for which we'll work:

export class FileDemoComponent {
  file = { name: 'logo.svg', size: 2120109, type: 'image/svg' };
}
   

The name and type of properties are not what we really want to learn about Pipes - but size is the one we want. Let's put together a quick example of how we can describe our pipe use (which will convert numbers to file sizes):




  

{{ file.name }}

  

{{ file.size | filesize }}

Creating a Custom Pipe

We need to first create a class to create a Pipe definition (which would live in its file). We're going to call this our FileSizePipe, as we're transforming a numeric value into a more human-readable string value:

export class FileSizePipe {}

Now we have this setup, we need to name our pipe. We've done this in the above HTML:


{{ file.size | filesize }}

As a result, we'll call the pipe "filesize." This is done through another TypeScript decorator, the @Pipe:

import { Pipe } from '@angular/core';

@Pipe({ name: 'filesize' })
export class FileSizePipe {}

All we need to do is provide a name property that corresponds to the name of our template code as well (as you would have imagined).

Also, don't forget to register the Pipe in your @NgModule's declarations:

// ...
import { FileSizePipe } from './filesize.pipe';

@NgModule({
  declarations: [
    //...
    FileSizePipe,
  ],
})
export class AppModule {}

Pipes are more "utility" classes, so we usually want to register one inside a shared module. Simply use exports: [YourPipe] on the @NgModule if you want to use your custom Pipe elsewhere.

Pipe and Pipe Transform

After we've registered our class and added the @Pipe decorator, the next step is to implement the PipeTransform interface:

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

@Pipe({ name: 'filesize' })
export class FileSizePipe implements PipeTransform {
  transform() {}
}

This establishes a contractual requirement for our FileSizePipe to follow the following structure:

export interface PipeTransform {
  transform(value: any, ...args: any[]): any;
}

That's why we added the transform () method to our class in general.

Pipe Transform Value

This is the magic of how we're given arguments in a Pipe because we're using it through interpolation.

{{ file.size | filesize }}

As the first argument, the file.size variable is passed directly to our transform method.

 

Planning to Hire Angular Web Development Company in USA ?

Your Search ends here.

 

We may call to this as our size and type it as follows:

//...
export class FileSizePipe implements PipeTransform {
  transform(size: number) {}
}

The logic to convert the numeric value into a more readable format of megabytes can be implemented from here.

//...
export class FileSizePipe implements PipeTransform {
  transform(size: number): string {
    return (size / (1024 * 1024)).toFixed(2) + 'MB';
  }
}

Since we're appending 'MB' to the end, we're returning a form string. As a result, we'll have:


{{ file.size | filesize }}

We will now demonstrate, how to build custom Pipes with our custom arguments.

Pipes with Arguments

So, let's assume that we want to be able to specify the extension slightly differently than advertised for our use case.

Let's just add the capability for an extension before we get to the template:

//...
export class FileSizePipe implements PipeTransform {
  transform(size: number, extension: string = 'MB'): string {
    return (size / (1024 * 1024)).toFixed(2) + extension;
  }
}

Instead of appending the 'MB' to the end of the string, we used a default parameter value. This allows us to use the default 'MB' or override it when needed. This takes us to our next objective, which is to pass an argument into our Pipe:


{{ file.size | filesize:'megabyte' }}

And that is all it is to supplying an argument to our custom Pipe.: is used to distinguish different claims, for example:

{{ value | pipe:arg1 }}
{{ value | pipe:arg1:arg2 }}
{{ value | pipe:arg1:arg3 }}

Note that you can connect these pipes along, just like you can with dates and other things.

Here's how the code came together in the end:

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

@Pipe({ name: 'filesize' })
export class FileSizePipe implements PipeTransform {
  transform(size: number, extension: string = 'MB') {
    return (size / (1024 * 1024)).toFixed(2) + extension;
  }
}

Conclusion

In this blog, we have discussed pipes in angular in which we have discussed types of built-in pipes. Then, we come with our main topic which is the step-by-step implementation of custom pipes. In custom pipes, we have learned to create a custom pipe, Pipe and pipe transform, Pipe transform value, and how to use pipes with arguments.

Introduction to Custom Pipes in Angular

If you're new to Angular and the Pipes concept before we get started, let's show what a Pipe is before we move on to showing a Custom Pipe.

Angular comes with a few built-in pipes that we can use in any module we write according to the CommonModule framework.

Table of Content

Here are some usual suspects we might encounter with the built-in Pipes of Angular:

  1. DatePipe: Used for parsing Date objects
  2. UpperCasePipe: Used for uppercase-ing Strings
  3. LowerCasePipe: Used for lowercase-ing Strings
  4. CurrrencyPipe: Used for formatting currencies
  5. AsyncPipe: Used for unwrapping asynchronous values like Observables

Just like we would a function, we can think of Pipes in Angular. A function will take parameters and return something new to us - and that's just what pipes do! We should pass a valid date and return a string value that is nicely formatted for the user interface. And here, the word UI is important because pipes are usually used to transform data between our model and view (the UI)!

This is the nature of a pipe!

How do we use a pipe, then? Suppose some basic HTML portion with a Date stamp binding:

{{myDateValue|date:'M/d/yy' }}

This could be made with the formatted date as above. But that's a valid argument for using pipes! We just don't want to fetch data and then loop through all this and convert every date from a date object to a string because we'd lose the functionality of the native date object and repeat values. Using a pipe and enabling it to parse for us is super convenient!

We are ready to begin venturing into Custom Pipes now! This will allow us to use a function to generate our input and output based on what we provide. Let's dive inside!

Custom Pipes in Angular

Let's assume that an image has just been uploaded through a drag and drop zone, and we get some of the data from it. A simplified file object for which we'll work:

export class FileDemoComponent {
  file = { name: 'logo.svg', size: 2120109, type: 'image/svg' };
}
   

The name and type of properties are not what we really want to learn about Pipes - but size is the one we want. Let's put together a quick example of how we can describe our pipe use (which will convert numbers to file sizes):




  

{{ file.name }}

  

{{ file.size | filesize }}

Creating a Custom Pipe

We need to first create a class to create a Pipe definition (which would live in its file). We're going to call this our FileSizePipe, as we're transforming a numeric value into a more human-readable string value:

export class FileSizePipe {}

Now we have this setup, we need to name our pipe. We've done this in the above HTML:


{{ file.size | filesize }}

As a result, we'll call the pipe "filesize." This is done through another TypeScript decorator, the @Pipe:

import { Pipe } from '@angular/core';

@Pipe({ name: 'filesize' })
export class FileSizePipe {}

All we need to do is provide a name property that corresponds to the name of our template code as well (as you would have imagined).

Also, don't forget to register the Pipe in your @NgModule's declarations:

// ...
import { FileSizePipe } from './filesize.pipe';

@NgModule({
  declarations: [
    //...
    FileSizePipe,
  ],
})
export class AppModule {}

Pipes are more "utility" classes, so we usually want to register one inside a shared module. Simply use exports: [YourPipe] on the @NgModule if you want to use your custom Pipe elsewhere.

Pipe and Pipe Transform

After we've registered our class and added the @Pipe decorator, the next step is to implement the PipeTransform interface:

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

@Pipe({ name: 'filesize' })
export class FileSizePipe implements PipeTransform {
  transform() {}
}

This establishes a contractual requirement for our FileSizePipe to follow the following structure:

export interface PipeTransform {
  transform(value: any, ...args: any[]): any;
}

That's why we added the transform () method to our class in general.

Pipe Transform Value

This is the magic of how we're given arguments in a Pipe because we're using it through interpolation.

{{ file.size | filesize }}

As the first argument, the file.size variable is passed directly to our transform method.

 

Planning to Hire Angular Web Development Company in USA ?

Your Search ends here.

 

We may call to this as our size and type it as follows:

//...
export class FileSizePipe implements PipeTransform {
  transform(size: number) {}
}

The logic to convert the numeric value into a more readable format of megabytes can be implemented from here.

//...
export class FileSizePipe implements PipeTransform {
  transform(size: number): string {
    return (size / (1024 * 1024)).toFixed(2) + 'MB';
  }
}

Since we're appending 'MB' to the end, we're returning a form string. As a result, we'll have:


{{ file.size | filesize }}

We will now demonstrate, how to build custom Pipes with our custom arguments.

Pipes with Arguments

So, let's assume that we want to be able to specify the extension slightly differently than advertised for our use case.

Let's just add the capability for an extension before we get to the template:

//...
export class FileSizePipe implements PipeTransform {
  transform(size: number, extension: string = 'MB'): string {
    return (size / (1024 * 1024)).toFixed(2) + extension;
  }
}

Instead of appending the 'MB' to the end of the string, we used a default parameter value. This allows us to use the default 'MB' or override it when needed. This takes us to our next objective, which is to pass an argument into our Pipe:


{{ file.size | filesize:'megabyte' }}

And that is all it is to supplying an argument to our custom Pipe.: is used to distinguish different claims, for example:

{{ value | pipe:arg1 }}
{{ value | pipe:arg1:arg2 }}
{{ value | pipe:arg1:arg3 }}

Note that you can connect these pipes along, just like you can with dates and other things.

Here's how the code came together in the end:

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

@Pipe({ name: 'filesize' })
export class FileSizePipe implements PipeTransform {
  transform(size: number, extension: string = 'MB') {
    return (size / (1024 * 1024)).toFixed(2) + extension;
  }
}

Conclusion

In this blog, we have discussed pipes in angular in which we have discussed types of built-in pipes. Then, we come with our main topic which is the step-by-step implementation of custom pipes. In custom pipes, we have learned to create a custom pipe, Pipe and pipe transform, Pipe transform value, and how to use pipes with arguments.