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;
})
-
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
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;
}
}
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>
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('');
}
}
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.
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
})
You’ll see the transform
method being called on every change detection cycle. Use this carefully as it may impact performance.
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:
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>
}
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' },
]);
}
}
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})/,
};
}
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)
Such valuable content! Looking forward to reading more from you.
Thanks for your feedback