Custom Pipes — Pure vs Impure
Custom pipes let you create your own data transformations for specific needs.
Built-in pipes cover many use cases, but sometimes you need something specific — that's where custom pipes come in.
"Custom pipe = apna special recipe — jo market mein nahi milta."
Custom pipe use cases:
- 🔹 Truncate text (show only first N characters + "...")
- 🔹 Filter/search an array
- 🔹 Sort array by property
- 🔹 Highlight search terms in text
- 🔹 Format phone numbers
- 🔹 Convert units (kg to lbs, celsius to fahrenheit)
Any transformation you need to reuse across your app is a good candidate for a custom pipe.
Creating a custom pipe is straightforward:
ng g p pipes/truncate
This generates:
import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'truncate',\n standalone: true\n})\nexport class TruncatePipe implements PipeTransform {\n transform(value: string, limit: number = 20): string {\n return value.length > limit\n ? value.substring(0, limit) + '...'\n : value;\n }\n}
Usage: {{ longText | truncate:30 }}
Key points:
- @Pipe decorator with a name — this is how you use it in templates
- PipeTransform interface — requires a
transform()method - standalone: true — works without NgModule in Angular 18
- The
valueparameter is the data being piped; additional parameters are pipe arguments
"PipeTransform interface = recipe format — transform method likho, Angular bhajega."
Add multiple parameters to make your pipe flexible:
transform(value: string, limit: number = 20, suffix: string = '...'): string {\n return value.length > limit\n ? value.substring(0, limit) + suffix\n : value;\n}\n\n// Usage\n{{ text | truncate:30:'>>' }} → First 30 chars + ">>"\n{{ text | truncate:10:'...read more' }}
"Jitne arguments chahiye, utne params add karo — flexible bhai."
Arguments are separated by colons: {{ value | pipeName:arg1:arg2:arg3 }}
Default values make your pipe easy to use with minimal configuration.
The most important concept: Pure vs Impure pipes.
PURE pipes (default):
@Pipe({ name: 'truncate', pure: true }) // or just omit pure
- ✅ Only re-runs when the INPUT REFERENCE changes
- ✅ Very fast — doesn't check on every change detection
- ✅ Perfect for 99% of use cases
- ❌ Won't detect changes inside an object if reference hasn't changed
IMPURE pipes:
@Pipe({ name: 'filterArray', pure: false })
- ❌ Re-runs on EVERY change detection cycle (100+ times/second!)
- ❌ Slow — can cause performance issues
- ✅ Detects changes inside objects even without reference change
- ✅ Use when filtering/sorting arrays where content changes but reference stays same
"Pure = smart filter — jab data badle tab hi check karo."
"Impure = har second check karo — slow but thorough."
When should you actually use an impure pipe?
- Filtering an array based on search term — array reference doesn't change, but filtered output changes
- Sorting an array where sort key changes dynamically
"90% cases mein pure hi kaam karta hai — impure sirf jab data reference same hai but content different."
Better alternative: Use signals + computed() instead of impure pipes:
// Component\nsearchTerm = signal('');\nfilteredItems = computed(() =>\n this.items().filter(item => item.name.includes(this.searchTerm()))\n);\n\n// Template — pure and fast!\n@for (item of filteredItems(); track item.id) { ... }
Signals + computed gives you the same result as an impure pipe but with MUCH better performance.
Key Takeaways
- ✅ Custom pipes implement PipeTransform with a transform() method.
- ✅ Pure pipes (default) only re-run when input reference changes — fast and efficient.
- ✅ Impure pipes re-run on every change detection — slow, use sparingly.
- ✅ Pipe parameters are separated by colons: {{ value | pipe:arg1:arg2 }}.
- ✅ Use computed() signals instead of impure pipes for better performance.
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login