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:
@ -236,13 +236,18 @@ export class ChangeDetectorJITGenerator {
|
||||
var newValue = this._names.getLocalName(r.selfIndex);
|
||||
|
||||
var pipe = this._names.getPipeName(r.selfIndex);
|
||||
var pipeType = r.name;
|
||||
var read = `
|
||||
var pipeName = r.name;
|
||||
|
||||
var init = `
|
||||
if (${pipe} === ${UTIL}.uninitialized) {
|
||||
${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}');
|
||||
${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeName}');
|
||||
}
|
||||
${newValue} = ${pipe}.transform(${context}, [${argString}]);
|
||||
`;
|
||||
var read = `${newValue} = ${pipe}.pipe.transform(${context}, [${argString}]);`;
|
||||
|
||||
var contexOrArgCheck = r.args.map((a) => this._names.getChangeName(a));
|
||||
contexOrArgCheck.push(this._names.getChangeName(r.contextIndex));
|
||||
var condition = `!${pipe}.pure || (${contexOrArgCheck.join(" || ")})`;
|
||||
|
||||
var check = `
|
||||
if (${oldValue} !== ${newValue}) {
|
||||
@ -254,7 +259,13 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
`;
|
||||
|
||||
return r.shouldBeChecked() ? `${read}${check}` : read;
|
||||
var genCode = r.shouldBeChecked() ? `${read}${check}` : read;
|
||||
|
||||
if (r.isUsedByOtherRecord()) {
|
||||
return `${init} if (${condition}) { ${genCode} } else { ${newValue} = ${oldValue}; }`;
|
||||
} else {
|
||||
return `${init} if (${condition}) { ${genCode} }`;
|
||||
}
|
||||
}
|
||||
|
||||
_genReferenceCheck(r: ProtoRecord): string {
|
||||
|
@ -12,6 +12,7 @@ import {ChangeDetectionStrategy, isDefaultChangeDetectionStrategy} from './const
|
||||
import {implementsOnDestroy} from './pipe_lifecycle_reflector';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {DirectiveIndex} from './directive_record';
|
||||
import {SelectedPipe} from './pipes';
|
||||
|
||||
|
||||
/**
|
||||
@ -193,9 +194,9 @@ export class ChangeDetectionUtil {
|
||||
protos[selfIndex - 1]; // self index is shifted by one because of context
|
||||
}
|
||||
|
||||
static callPipeOnDestroy(pipe: any): void {
|
||||
if (implementsOnDestroy(pipe)) {
|
||||
pipe.onDestroy();
|
||||
static callPipeOnDestroy(selectedPipe: SelectedPipe): void {
|
||||
if (implementsOnDestroy(selectedPipe.pipe)) {
|
||||
(<any>selectedPipe.pipe).onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,38 +333,39 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
|
||||
_pipeCheck(proto: ProtoRecord, throwOnChange: boolean, values: any[]) {
|
||||
var context = this._readContext(proto, values);
|
||||
var args = this._readArgs(proto, values);
|
||||
var selectedPipe = this._pipeFor(proto, context);
|
||||
if (!selectedPipe.pure || this._argsOrContextChanged(proto)) {
|
||||
var args = this._readArgs(proto, values);
|
||||
var currValue = selectedPipe.pipe.transform(context, args);
|
||||
|
||||
var pipe = this._pipeFor(proto, context);
|
||||
var currValue = pipe.transform(context, args);
|
||||
if (proto.shouldBeChecked()) {
|
||||
var prevValue = this._readSelf(proto, values);
|
||||
if (!isSame(prevValue, currValue)) {
|
||||
currValue = ChangeDetectionUtil.unwrapValue(currValue);
|
||||
|
||||
if (proto.shouldBeChecked()) {
|
||||
var prevValue = this._readSelf(proto, values);
|
||||
if (!isSame(prevValue, currValue)) {
|
||||
currValue = ChangeDetectionUtil.unwrapValue(currValue);
|
||||
if (proto.lastInBinding) {
|
||||
var change = ChangeDetectionUtil.simpleChange(prevValue, currValue);
|
||||
if (throwOnChange) this.throwOnChangeError(prevValue, currValue);
|
||||
|
||||
if (proto.lastInBinding) {
|
||||
var change = ChangeDetectionUtil.simpleChange(prevValue, currValue);
|
||||
if (throwOnChange) this.throwOnChangeError(prevValue, currValue);
|
||||
this._writeSelf(proto, currValue, values);
|
||||
this._setChanged(proto, true);
|
||||
|
||||
this._writeSelf(proto, currValue, values);
|
||||
this._setChanged(proto, true);
|
||||
|
||||
return change;
|
||||
return change;
|
||||
|
||||
} else {
|
||||
this._writeSelf(proto, currValue, values);
|
||||
this._setChanged(proto, true);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
this._writeSelf(proto, currValue, values);
|
||||
this._setChanged(proto, true);
|
||||
this._setChanged(proto, false);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
this._setChanged(proto, false);
|
||||
this._writeSelf(proto, currValue, values);
|
||||
this._setChanged(proto, true);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
this._writeSelf(proto, currValue, values);
|
||||
this._setChanged(proto, true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,6 +414,10 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
return false;
|
||||
}
|
||||
|
||||
_argsOrContextChanged(proto: ProtoRecord): boolean {
|
||||
return this._argsChanged(proto) || this.changes[proto.contextIndex];
|
||||
}
|
||||
|
||||
_readArgs(proto: ProtoRecord, values: any[]) {
|
||||
var res = ListWrapper.createFixedSize(proto.args.length);
|
||||
var args = proto.args;
|
||||
|
@ -1,3 +1,7 @@
|
||||
import {PipeTransform} from './pipe_transform';
|
||||
|
||||
export interface Pipes { get(name: string): PipeTransform; }
|
||||
export interface Pipes { get(name: string): SelectedPipe; }
|
||||
|
||||
export class SelectedPipe {
|
||||
constructor(public pipe: PipeTransform, public pure: boolean) {}
|
||||
}
|
@ -102,6 +102,11 @@ export class ProtoRecordBuilder {
|
||||
rec.args.forEach(recordIndex => this.records[recordIndex - 1].argumentToPureFunction =
|
||||
true);
|
||||
}
|
||||
if (rec.mode === RecordType.Pipe) {
|
||||
rec.args.forEach(recordIndex => this.records[recordIndex - 1].argumentToPureFunction =
|
||||
true);
|
||||
this.records[rec.contextIndex - 1].argumentToPureFunction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,8 @@ export class ProtoRecord {
|
||||
isUsedByOtherRecord(): boolean { return !this.lastInBinding || this.referencedBySelf; }
|
||||
|
||||
shouldBeChecked(): boolean {
|
||||
return this.argumentToPureFunction || this.lastInBinding || this.isPureFunction();
|
||||
return this.argumentToPureFunction || this.lastInBinding || this.isPureFunction() ||
|
||||
this.isPipeRecord();
|
||||
}
|
||||
|
||||
isPipeRecord(): boolean { return this.mode === RecordType.Pipe; }
|
||||
|
Reference in New Issue
Block a user