In Angular, pipes are used to transform data before displaying it in templates. Most pipes are pure, meaning they re-evaluate only when the input value changes. However, Angular also supports impure pipes, which are re-evaluated on every change detection cycle, regardless of whether the input data has changed.
This article dives deep into impure pipes, how they work, when to use them, and the pitfalls to avoid.
What is an Impure Pipe?
An impure pipe is a custom or built-in pipe that Angular executes on every change detection cycle. Impure pipes are set by specifying pure: false
in the @Pipe
decorator.
Key Characteristics of Impure Pipes
- Re-evaluates on every change detection cycle.
- Tracks changes in objects, arrays, or other mutable data structures.
- Can impact application performance if not used carefully.
Pure vs. Impure Pipes
Feature | Pure Pipe | Impure Pipe |
---|---|---|
Change Detection | Re-evaluates only when the input reference changes. | Re-evaluates on every change detection cycle. |
Use Case | Handles immutable data effectively. | Handles mutable or frequently changing data. |
Performance Impact | High performance. | May degrade performance if overused. |
Decorator Configuration | @Pipe({ name: 'pipeName' }) | @Pipe({ name: 'pipeName', pure: false }) |
When to Use an Impure Pipe
Impure pipes are useful for scenarios where:
- You Work with Mutable Data Structures
- Arrays or objects are modified without creating new instances.
- Real-Time Updates Are Needed
- Data changes frequently and needs to be reflected immediately.
- Custom Filtering or Sorting
- Pipes that filter or sort arrays dynamically based on user interaction.
Implementing an Impure Pipe
Example: A Filter Pipe
Let’s create a custom impure pipe that filters an array of objects based on a search term.
Step 1: Generate the Pipe
Use the Angular CLI to generate a pipe:
bashCopy codeng generate pipe filter
Step 2: Define the Pipe
Open filter.pipe.ts
and implement the filtering logic. Set pure: false
in the @Pipe
decorator to make it impure.
typescriptCopy codeimport { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter',
pure: false, // Marking the pipe as impure
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchTerm: string, property: string): any[] {
if (!items || !searchTerm) {
return items;
}
return items.filter(item =>
item[property].toLowerCase().includes(searchTerm.toLowerCase())
);
}
}
Step 3: Declare the Pipe
Add the pipe to the declarations
array in the module file (e.g., app.module.ts
).
typescriptCopy codeimport { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { FilterPipe } from './filter.pipe';
@NgModule({
declarations: [
AppComponent,
FilterPipe,
],
imports: [
BrowserModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
Step 4: Use the Pipe in a Template
You can now use the filter
pipe in your template.
Template (app.component.html
):
htmlCopy code<div>
<input [(ngModel)]="searchTerm" placeholder="Search items" />
<ul>
<li *ngFor="let item of items | filter:searchTerm:'name'">
{{ item.name }}
</li>
</ul>
</div>
Component (app.component.ts
):
typescriptCopy codeimport { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
searchTerm = '';
items = [
{ name: 'Angular' },
{ name: 'React' },
{ name: 'Vue' },
];
}
Advantages of Impure Pipes
- Real-Time Updates
Reflects changes in mutable objects and arrays immediately. - Ease of Use
Simplifies filtering, sorting, and other operations directly in templates. - Flexibility
Handles scenarios where immutable data is not feasible or practical.
Pitfalls and How to Avoid Them
1. Performance Impact
Impure pipes are executed on every change detection cycle, which can degrade performance in large applications.
Solution:
- Use impure pipes only when necessary.
- Prefer immutable data and pure pipes where possible.
2. Overuse in Complex Applications
Chaining multiple impure pipes or using them in large lists can lead to excessive computations.
Solution:
- Perform heavy computations in the component instead of the template.
3. Unintended Behavior
Impure pipes might re-execute unnecessarily, even when the actual data hasn’t changed.
Solution:
- Ensure the pipe is used only for cases where real-time updates are required.
Best Practices for Impure Pipes
- Use for Small Data Sets
- Impure pipes are best suited for small, frequently changing data sets.
- Optimize with TrackBy
- Use
trackBy
with*ngFor
to optimize rendering.
<li *ngFor="let item of items | filter:searchTerm:'name'; trackBy: trackByFn"> {{ item.name }} </li>
- Use
- Combine with Observables
- Use Angular’s reactive programming features for more efficient handling of dynamic data.
- Limit Usage to Templates
- Avoid using impure pipes in performance-critical components or deeply nested templates.
Alternatives to Impure Pipes
- Custom Methods in Components
- Perform transformations in component logic instead of using pipes.
getFilteredItems(): any[] { return this.items.filter(item => item.name.toLowerCase().includes(this.searchTerm.toLowerCase()) ); }
Template:htmlCopy code<li *ngFor="let item of getFilteredItems()"> {{ item.name }} </li>
- Reactive Streams
- Use RxJS operators to handle real-time updates efficiently.
filteredItems$ = this.searchTerm$.pipe( debounceTime(300), distinctUntilChanged(), switchMap(searchTerm => this.items.filter(item => item.name.includes(searchTerm)) ) );
Conclusion
Impure pipes in Angular provide a straightforward way to handle dynamic transformations in templates. However, their frequent execution during change detection cycles requires careful use to avoid performance bottlenecks. By understanding when to use impure pipes and following best practices, you can leverage their power effectively while maintaining application performance. For most use cases, consider alternatives like pure pipes, component logic, or reactive streams to achieve optimal results.