Angular Pipe is object, not function, when pipe created, does or how it reused, lets find out by examples:
Pure Pipe
Pure Pipe means output depends only on input, if input not changed, pipe transmission function not called:
import { Pipe, PipeTransform } from '@angular/core';
const idx = 0;
@Pipe({
name: 'toInt'
})
export class ToIntPipe implements PipeTransform {
id: string;
constructor() {
this.id = `idx-${idx}`;
console.log(`Create ${this.id} instance`);
}
transform(value: number): any {
console.log(`${this.id} transform ${value}`);
return value.toFixed(1);
}
}
<output>{{v1 | toInt}} {{v2 | toInt}}</output>
<button (click)="v1 = v1 + 1.17">Inc1</button>
<button (click)="v2 = v2 + 1.17">Inc2</button>
Here is the console output:
Create idx-0 instance
to-int.pipe.ts:17 idx-0 transform 3.13
to-int.pipe.ts:17 idx-0 transform 2.13
to-int.pipe.ts:17 idx-0 transform 5.47
to-int.pipe.ts:17 idx-0 transform 3.3
to-int.pipe.ts:17 idx-0 transform 4.47
- One
toInt
pipe instance created, serves for all two value bindings. - As document says, if value not changed, the pipe not called, because it is
pure
.
Changed means '===', for object/array, change content does not means change, create a new instance or use impure pipe.
Let's find out what happened if argument changed but not value. Add argument to ToIntPipe
:
export class ToIntPipe implements PipeTransform {
...
transform(value: number, digits: number = 1): any {
console.log(`${this.id} transform ${value}`);
return value.toFixed(digits);
}
Update page template:
<output>{{v1 | toInt}} {{v2 | toInt:digits}}</output>
<button (click)="v1 = v1 + 1.17">Inc1</button>
<button (click)="v2 = v2 + 1.17">Inc2</button>
<button (click)="digits = digits ? 0 : 2">Toggle Digits</button>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'try-ngc';
v1 = 3.13;
v2 = 2.13;
digits = 2;
}
Click toggle digits button, toggles digits 0/3, as expected, angular call transform()
on argument changes.
Impure Stateful Pipe
Mark toInt
pipe as pure: false
:
@Pipe({
name: 'toInt', pure: false,
})
export class ToIntPipe implements PipeTransform {
Here is the console log after web page reload:
to-int.pipe.ts:13 Create idx-1 instance
to-int.pipe.ts:13 Create idx-2 instance
to-int.pipe.ts:17 idx-1 transform 3.13
to-int.pipe.ts:17 idx-2 transform 2.13
to-int.pipe.ts:17 idx-1 transform 3.13
to-int.pipe.ts:17 idx-2 transform 2.13
core.js:3121 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
to-int.pipe.ts:17 idx-1 transform 3.13
to-int.pipe.ts:17 idx-2 transform 2.13
to-int.pipe.ts:17 idx-1 transform 3.13
to-int.pipe.ts:17 idx-2 transform 2.13
- Each binding creates a
toInt
pipe instance, - Without touch button, for each binding,
toInt
transform called four times.
Cache Expensive Transmission Result
Using pure pipe, because only one pure pipe object instance per template, can not save cache result inside pipe object, need some Cache Service:
@Injectable({
providedIn: 'root'
})
export class CacheService {
private _cache = new Map();
get(key: string, Func1<string, any>) {
....
}
}
@Pipe({
name: 'toInt',
})
export class ExpensivePipe implements PipeTransform {
constructor(private cache: CacheService) { }
transform(value: string): any {
return this.cache.get(value, expensiveTransform);
}
}
On the other hand, impure service has an advantage: it is stateful, saves and caches transmission result easily inside pipe object instance.
Conclusion
- Angular shares pure pipe for all value bindings
- Impure pipe created for each value binding, instance not shared
- Impure pipe
transform()
method called much more frequently than pure pipe - Pure pipe only called on: 1st render, value changes, argument changes.
- Impure pipe implement cache maybe more easier, but for best performance, pure pipe with CacheService is the best option.
网友评论