DEV Community

Cover image for Exploring the @Pipe Decorator—Decorator Series—4
vetriselvan Panneerselvam
vetriselvan Panneerselvam

Posted on

Exploring the @Pipe Decorator—Decorator Series—4

Hey Devs 👋,

Welcome back to our decorator series! It’s been a while, but I’m excited to dive back in. Today, we’re going to explore the powerful @Pipe decorator in Angular.

What is a Pipe?

A Pipe in Angular is a class with a transform method that transforms input values into a desired output without modifying the original value.

Syntax

@Pipe({
  name: string;
  pure?: boolean;
  standalone?: boolean;
})
Enter fullscreen mode Exit fullscreen mode
  • name: The name used to refer to the pipe in templates (must be in lowerCamelCase).
  • pure: A boolean indicating whether the pipe is pure (default: true).

Pipe Types: Pure vs Impure

The pure property determines how Angular treats the pipe:

  • Pure Pipe (pure: true or omitted):

    • Only re-evaluated when input reference changes.
    • Improves performance.
  • Impure Pipe (pure: false):

    • Re-evaluated on every change detection cycle.
    • Use cautiously due to performance implications.

Built-in Angular Pipes

Here are some of the most commonly used pipes in Angular:

Pipe Name Description
date Formats date values.
uppercase Transforms text to uppercase.
lowercase Transforms text to lowercase.
currency Formats a number as currency.
decimal Formats numbers with decimal points.
percent Formats numbers as percentages.
json Converts a value to JSON string.
slice Slices arrays or strings.
async Subscribes to Observable or Promise and returns the latest value.
titlecase Capitalizes the first letter of each word.
keyvalue Converts an object or Map into key-value pairs.
i18nPlural Provides pluralized strings based on locale and mapping configuration.
i18nSelect Chooses a string from a set based on a given value.

We'll explore these in more detail in a future post.

Custom Pipe Example: Phone Number Formatter

Let’s create a custom pipe that formats phone numbers.

Command:

ng g pipe phone-format
Enter fullscreen mode Exit fullscreen mode

phone-format.pipe.ts

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

@Pipe({
  name: 'phoneFormat'
})
export class PhoneFormatPipe implements PipeTransform {
  transform(value: unknown): unknown {
    if (typeof value === 'string') {
      return value.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
    }
    return value;
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage Example

HTML:

<h3>Pipe Decorator</h3>
<div class="text-wrapper">
  <div class="text">
    <textarea
      class="textarea-modern"
      [(ngModel)]="phoneNumber"
      name="subject" 
      maxlength="10"
      id="subject"
    ></textarea>
    <div class="btn-container"> 
      <button class="btn-ghost"  (click)="addNumber()" type="button">
        add
      </button>
    </div>
  </div>
  <div class="example">
    @for (item of numberArray(); track $index) {  
    <div class="card">
      <div class="index">{{$index}}</div>
      <div class="data"> Formatted value : {{ item | phoneFormat }}</div>
      <div class="data"> Raw value : {{ item }}</div>
    </div>
    }
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Component:

import { Component, signal, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { PhoneFormatPipe } from '../../pipes/phone-format.pipe';

@Component({
  selector: 'app-pipe-decorator-example',
  imports: [PhoneFormatPipe, FormsModule],
  templateUrl: './pipe-decorator-example.html',
  styleUrls: ['./pipe-decorator-example.scss']
})
export class PipeDecoratorExample {
  phoneNumber = model('');
  numberArray = signal<string[]>([]);

  addNumber() {
    this.numberArray.update(prev => [...prev, this.phoneNumber()]);
    this.phoneNumber.set('');
  }
}
Enter fullscreen mode Exit fullscreen mode

Case 1: Is the Pipe Pure or Impure?

By default, the pipe is pure, meaning the transform method is only called when the input value changes.

Image description

Try adding a console.log() inside the transform method. You’ll notice it's called only once per value addition. Now, make the pipe impure:

@Pipe({
  name: 'phoneFormat',
  pure: false // Now it's impure
})
Enter fullscreen mode Exit fullscreen mode

You’ll see the transform method being called on every change detection cycle. Use this carefully as it may impact performance.

Image description

Case 2: Passing Multiple Arguments

Yes, you can pass multiple arguments to a pipe! Let's update our example to format phone numbers differently based on country:

Image description

Updated HTML

<h3>Pipe Decorator</h3>
  <div class="table-header">
      <div class="index">Index</div>
      <div class="country">Country Name</div>
      <div class="phoneNumber">Phone Number</div>
      <div class="formattedValue">Formatted Value</div> 
    </div>
    @for (item of numberArray(); track $index) {  
    <div class="table-body">
      <div class="index">{{$index}}</div>
      <div class="country">{{item.country}}</div>
      <div class="phoneNumber">{{item.phoneNumber}}</div>
      <div class="formattedValue">{{ item.phoneNumber| phoneFormat : item.country }}</div> 
    </div>
    }
Enter fullscreen mode Exit fullscreen mode

Updated Component

@Component({
  selector: 'app-pipe-decorator-example',
  standalone: true,
  imports: [PhoneFormatPipe, FormsModule],
  templateUrl: './pipe-decorator-example.html',
  styleUrls: ['./pipe-decorator-example.scss']
})
export class PipeDecoratorExample implements OnInit {
  numberArray = signal<{ country: string, phoneNumber: string }[]>([]);

  ngOnInit(): void {
    this.numberArray.set([
      { country: 'USA', phoneNumber: '1234567890' },
      { country: 'UK', phoneNumber: '12345678900' },
      { country: 'India', phoneNumber: '1234567890' },
      { country: 'Australia', phoneNumber: '1234567890' },
      { country: 'Germany', phoneNumber: '123456789012' },
      { country: 'France', phoneNumber: '1234567890' },
      { country: 'Brazil', phoneNumber: '12345678907' },
      { country: 'Japan', phoneNumber: '12345678907' },
      { country: 'South Africa', phoneNumber: '12345678907' },
      { country: 'Russia', phoneNumber: '1234567890' },
    ]);
  }
}
Enter fullscreen mode Exit fullscreen mode

Updated Pipe Logic

@Pipe({
  name: 'phoneFormat'
})
export class PhoneFormatPipe implements PipeTransform {
  transform(value: unknown, ...args: unknown[]): unknown {
    console.log('transform', value, args);

    if (typeof value === 'string' && args.length === 0) {
      return value.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
    } else if (typeof value === 'string' && args.length && args[0]) {
      const country = args[0] as keyof typeof this.formatPatterns;
      return value.replace(
        this.patterns[country],
        this.formatPatterns[country]
      );
    }

    return value;
  }

  formatPatterns = {
    USA: '($1) $2-$3',
    UK: '$1 $2',
    India: '+91 $1 $2',
    Australia: '$1 $2 $3',
    Germany: '$1 $2',
    France: '$1 $2 $3 $4 $5',
    Brazil: '($1) $2-$3',
    Japan: '$1-$2-$3',
    'South Africa': '$1 $2 $3',
    Russia: '8 ($1) $2-$3-$4',
  };

  patterns = {
    USA: /(\d{3})(\d{3})(\d{4})/,
    UK: /(\d{5})(\d{6})/,
    India: /(\d{5})(\d{5})/,
    Australia: /(\d{4})(\d{3})(\d{3})/,
    Germany: /(\d{3})(\d{8})/,
    France: /(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/,
    Brazil: /(\d{2})(\d{5})(\d{4})/,
    Japan: /(\d{3})(\d{4})(\d{4})/,
    'South Africa': /(\d{3})(\d{3})(\d{4})/,
    Russia: /(\d{3})(\d{3})(\d{2})(\d{2})/,
  };
}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

  • Pipes are great for formatting and transforming data directly in templates.
  • Use pure pipes whenever possible for better performance.
  • Impure pipes are powerful but should be used with care.
  • Custom pipes can help encapsulate logic and improve template readability.

💬 Got questions or use cases you want to share? Drop a comment below! Let's discuss more Angular magic. ✨

✍️ Author: Vetriselvan

👨‍💻 Frontend Developer | 💡 Code Enthusiast | 📚 Lifelong Learner | ✍️ Tech Blogger | 🌍 Freelance Developer

Top comments (2)

Collapse
 
devops_fundamental profile image
DevOps Fundamental

Such valuable content! Looking forward to reading more from you.

Collapse
 
vetriselvan_11 profile image
vetriselvan Panneerselvam

Thanks for your feedback