feat(pipes): add support for pure pipes
By default, pipes are pure. This means that an instance of a pipe will be reused and the pipe will be called only when its arguments change. BREAKING CHANGE Before: @Pipe({name: 'date'}) class DatePipe {} defines an impure pipe. After: @Pipe({name: 'date'}) class DatePipe {} defines a pure pipe. @Pipe({name: 'date', pure: false}) class DatePipe {} defines an impure pipe. Closes #3966
This commit is contained in:
@ -17,7 +17,8 @@ import {
|
||||
isBlank,
|
||||
isJsObject,
|
||||
BaseException,
|
||||
FunctionWrapper
|
||||
FunctionWrapper,
|
||||
normalizeBool
|
||||
} from 'angular2/src/core/facade/lang';
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
|
||||
@ -41,7 +42,7 @@ import {
|
||||
ProtoChangeDetector
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||
import {SelectedPipe, Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||
import {JitProtoChangeDetector} from 'angular2/src/core/change_detection/jit_proto_change_detector';
|
||||
|
||||
import {getDefinition} from './change_detector_config';
|
||||
@ -351,6 +352,36 @@ export function main() {
|
||||
expect(val.dispatcher.loggedValues).toEqual(['value one two default']);
|
||||
});
|
||||
|
||||
it('should not reevaluate pure pipes unless its context or arg changes', () => {
|
||||
var pipe = new CountingPipe();
|
||||
var registry = new FakePipes('pipe', () => pipe, {pure: true});
|
||||
var person = new Person('bob');
|
||||
var val = _createChangeDetector('name | pipe', person, registry);
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
expect(pipe.state).toEqual(1);
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
expect(pipe.state).toEqual(1);
|
||||
|
||||
person.name = 'jim';
|
||||
val.changeDetector.detectChanges();
|
||||
expect(pipe.state).toEqual(2);
|
||||
});
|
||||
|
||||
it('should reevaluate impure pipes neither context nor arg changes', () => {
|
||||
var pipe = new CountingPipe();
|
||||
var registry = new FakePipes('pipe', () => pipe, {pure: false});
|
||||
var person = new Person('bob');
|
||||
var val = _createChangeDetector('name | pipe', person, registry);
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
expect(pipe.state).toEqual(1);
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
expect(pipe.state).toEqual(2);
|
||||
});
|
||||
|
||||
it('should support pipes as arguments to pure functions', () => {
|
||||
var registry = new FakePipes('pipe', () => new IdentityPipe());
|
||||
var person = new Person('bob');
|
||||
@ -1116,24 +1147,6 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('pipes', () => {
|
||||
it('should support pipes', () => {
|
||||
var registry = new FakePipes('pipe', () => new CountingPipe());
|
||||
var ctx = new Person('Megatron');
|
||||
|
||||
var val = _createChangeDetector('name | pipe', ctx, registry);
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
|
||||
expect(val.dispatcher.log).toEqual(['propName=Megatron state:0']);
|
||||
|
||||
val.dispatcher.clear();
|
||||
val.changeDetector.detectChanges();
|
||||
|
||||
expect(val.dispatcher.log).toEqual(['propName=Megatron state:1']);
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing when no change', () => {
|
||||
var registry = new FakePipes('pipe', () => new IdentityPipe());
|
||||
var ctx = new Person('Megatron');
|
||||
@ -1256,13 +1269,16 @@ class MultiArgPipe implements PipeTransform {
|
||||
|
||||
class FakePipes implements Pipes {
|
||||
numberOfLookups = 0;
|
||||
pure: boolean;
|
||||
|
||||
constructor(public pipeType: string, public factory: Function) {}
|
||||
constructor(public pipeType: string, public factory: Function, {pure}: {pure?: boolean} = {}) {
|
||||
this.pure = normalizeBool(pure);
|
||||
}
|
||||
|
||||
get(type: string) {
|
||||
if (type != this.pipeType) return null;
|
||||
this.numberOfLookups++;
|
||||
return this.factory();
|
||||
return new SelectedPipe(this.factory(), this.pure);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,8 @@ export function main() {
|
||||
it('should instantiate a pipe', () => {
|
||||
var proto = new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a'}))]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
expect(pipes.get("a")).toBeAnInstanceOf(PipeA);
|
||||
|
||||
expect(pipes.get("a").pipe).toBeAnInstanceOf(PipeA);
|
||||
});
|
||||
|
||||
it('should throw when no pipe found', () => {
|
||||
@ -48,7 +49,25 @@ export function main() {
|
||||
it('should inject dependencies from the provided injector', () => {
|
||||
var proto = new ProtoPipes([PipeBinding.createFromType(PipeB, new Pipe({name: 'b'}))]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
expect(pipes.get("b").dep).toEqual("dependency");
|
||||
expect((<any>pipes.get("b").pipe).dep).toEqual("dependency");
|
||||
});
|
||||
|
||||
it('should cache pure pipes', () => {
|
||||
var proto =
|
||||
new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a', pure: true}))]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
|
||||
expect(pipes.get("a").pure).toEqual(true);
|
||||
expect(pipes.get("a")).toBe(pipes.get("a"));
|
||||
});
|
||||
|
||||
it('should NOT cache impure pipes', () => {
|
||||
var proto =
|
||||
new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a', pure: false}))]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
|
||||
expect(pipes.get("a").pure).toEqual(false);
|
||||
expect(pipes.get("a")).not.toBe(pipes.get("a"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user