Angular Pipes

Angular Pipes

All you need to know

ยท

3 min read

What is a Pipe?

A pipe is a function that transforms an input value into an output value before rendering it in the template.

The class requires to implement the PipeTransform interface.

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

@Pipe({
  name: 'errorMessage'
})
export class ErrorPipe implements PipeTransform {

  transform(value: any, args?: any): any {
    return 'Error: ' + value;
  }
}

You can use it in the code like this

<div *ngIf="error">
    <p>{{ error | errorMessage }}</p>
</div>

Why not just use a function in the template?

The problem with using a function in the template is that it will be triggered during the change detection process.

Letโ€™s have this example:

@Component({
  selector: 'app-error-message',
  template: `
        <div *ngIf="error">
            <p>{{getErrorMessage()}}</p>
        </div>
  `,
})
export class MyLibComponent {

    @Input() error: string = ''

    getErrorMessage() {
        return `๐Ÿ›‘ Error: ${this.error} ๐Ÿ›‘`;
    }

}

Because of not knowing what the result of getErrorMessage() will be, Angular Change Detection algorithm will re-run that function each time the change detection cycle will be triggered.

Imagine clicking somewhere in the app and having to re-render this message.

A pipe that solves this problem is called a PURE PIPE


Pure and Impure pipes

Pure pipes

  • Pure pipes get triggered only when a primitive value or reference is changed

  • By default, the pipe comes as pure.

  • More efficient than impure pipes due to change detection.

  • The result is memoized and every time you get to call the pipe with the parameter you will get the same result.

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

/// In this example, the pipe will be triggered only 
/// when the reference of value changes
///
/// If for example I would change only value.error, 
/// the pipe will not be triggered

@Pipe({
    name: 'errorMessage',
    pure: true /// IS SET BY DEFAULT
    standalone: true
})
export class ErrorPipe implements PipeTransform {

  transform(value: Raport, args?: any): any {
    return 'Error: ' + value.error;
  }
}

export interface Raport {
    id: string;
    name: string;
    error: string;
}

Impure pipes

  • Impure pipes get triggered even if the nested values are changed.

  • Change Detection will be triggered even if the value is the same

/// In this example, the pipe will be triggered
/// when value.error changes

@Pipe({
  name: 'errorMessage',
  pure: false
})

Diagram

A visual representation always comes in handy:


Can I use it just in the template or can I use it in TS files as well?

To use pipes in the TS files, you will have to inject the pipe into the component constructor.

@Component({
  selector: 'app-error-message',
  template: `
        <div *ngIf="error">
            <p>{{displayMessage}}</p>
        </div>
  `,
  import: [ErrorPipe]
})
export class MyLibComponent implements OnInit {

    @Input() error: string = '';
    displayMessage: string = '';

    constructor(private errorPipe: ErrorPipe) { }

    ngOnInit() {
        this.displayMessage = this.getErrorMessage();
    }

    getErrorMessage() {
        return this.errorPipe.transform(this.error);
    }
}

If you want to improve DX, you can extract the logic of the pipe in a function so every time you will want to use the pipe in the TS file you will not have to inject the pipe, declare it in the constructor and call the transform method.

const errorMessage = (message: string) => `Error: ${message}`
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'errorMessage',
    standalone: true
})
export class ErrorPipe implements PipeTransform {
  transform(value: string, args?: any): string {
    return errorMessage(value);
  }
}
import { errorMessage } from './errorMessage';
import { OnInit } from '@angular/core';

@Component({
  selector: 'app-error-message',
  template: `
        <div *ngIf="error">
            <p>{{displayMessage}}</p>
        </div>
  `,
})
export class MyLibComponent implements OnInit {

    @Input() error: string = '';
    displayMessage: string = '';

    constructor() { }

    ngOnInit() {
        this.displayMessage = errorMessage(error);
    }

}

For further reading, always consult the Angular Documentation or Angular Github Repo.

ย