From 600d53c68e07a7897dcf2f6e2560c75b07a76cda Mon Sep 17 00:00:00 2001 From: Pouria Alimirzaei Date: Fri, 26 Jun 2015 00:22:06 +0430 Subject: [PATCH] feat(pipes): support arguments in transform function --- .../change_detection_jit_generator.ts | 4 ++- .../dynamic_change_detector.ts | 5 ++-- .../pipes/iterable_changes.ts | 5 ++-- .../src/change_detection/pipes/json_pipe.ts | 7 +++-- .../pipes/keyvalue_changes.ts | 6 ++-- .../change_detection/pipes/lowercase_pipe.ts | 9 +++--- .../src/change_detection/pipes/null_pipe.ts | 5 ++-- .../change_detection/pipes/observable_pipe.ts | 4 +-- .../src/change_detection/pipes/pipe.ts | 9 +++--- .../change_detection/pipes/promise_pipe.ts | 8 +++--- .../change_detection/pipes/uppercase_pipe.ts | 9 +++--- .../change_detection/proto_change_detector.ts | 3 +- modules/angular2/src/directives/class.ts | 2 +- modules/angular2/src/directives/ng_for.ts | 2 +- modules/angular2/src/directives/ng_style.ts | 2 +- .../change_detector_codegen.dart | 4 ++- .../change_detector_config.ts | 1 + .../change_detection/change_detector_spec.ts | 28 ++++++++++++++++--- .../test/core/compiler/integration_spec.ts | 2 +- 19 files changed, 74 insertions(+), 41 deletions(-) diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.ts b/modules/angular2/src/change_detection/change_detection_jit_generator.ts index 4b805cbcb7..0731a742a7 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -240,6 +240,8 @@ export class ChangeDetectorJITGenerator { _genPipeCheck(r: ProtoRecord): string { var context = this._localNames[r.contextIndex]; + var argString = r.args.map((arg) => this._localNames[arg]).join(", "); + var oldValue = this._fieldNames[r.selfIndex]; var newValue = this._localNames[r.selfIndex]; var change = this._changeNames[r.selfIndex]; @@ -259,7 +261,7 @@ export class ChangeDetectorJITGenerator { ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef}); } - ${newValue} = ${pipe}.transform(${context}); + ${newValue} = ${pipe}.transform(${context}, [${argString}]); if (${oldValue} !== ${newValue}) { ${newValue} = ${UTIL}.unwrapValue(${newValue}); ${change} = true; diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index 3e3b288c42..70918213aa 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -233,10 +233,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector { _pipeCheck(proto: ProtoRecord, throwOnChange: boolean) { var context = this._readContext(proto); + var args = this._readArgs(proto); + var pipe = this._pipeFor(proto, context); var prevValue = this._readSelf(proto); - - var currValue = pipe.transform(context); + var currValue = pipe.transform(context, args); if (!isSame(prevValue, currValue)) { currValue = ChangeDetectionUtil.unwrapValue(currValue); diff --git a/modules/angular2/src/change_detection/pipes/iterable_changes.ts b/modules/angular2/src/change_detection/pipes/iterable_changes.ts index 18b419651a..9a7e8bfc5d 100644 --- a/modules/angular2/src/change_detection/pipes/iterable_changes.ts +++ b/modules/angular2/src/change_detection/pipes/iterable_changes.ts @@ -16,12 +16,13 @@ import { } from 'angular2/src/facade/lang'; import {WrappedValue, Pipe, BasePipe, PipeFactory} from './pipe'; +import {ChangeDetectorRef} from '../change_detector_ref'; @CONST() export class IterableChangesFactory implements PipeFactory { supports(obj): boolean { return IterableChanges.supportsObj(obj); } - create(cdRef): Pipe { return new IterableChanges(); } + create(cdRef: ChangeDetectorRef): Pipe { return new IterableChanges(); } } /** @@ -89,7 +90,7 @@ export class IterableChanges extends BasePipe { } } - transform(collection): any { + transform(collection, args: List = null): any { if (this.check(collection)) { return WrappedValue.wrap(this); } else { diff --git a/modules/angular2/src/change_detection/pipes/json_pipe.ts b/modules/angular2/src/change_detection/pipes/json_pipe.ts index e15c1b1237..de5b8533b4 100644 --- a/modules/angular2/src/change_detection/pipes/json_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/json_pipe.ts @@ -1,5 +1,6 @@ import {isBlank, isPresent, Json, CONST} from 'angular2/src/facade/lang'; import {Pipe, BasePipe, PipeFactory} from './pipe'; +import {ChangeDetectorRef} from '../change_detector_ref'; /** * Implements json transforms to any object. @@ -27,8 +28,8 @@ import {Pipe, BasePipe, PipeFactory} from './pipe'; * @exportedAs angular2/pipes */ @CONST() -export class JsonPipe extends BasePipe { - transform(value): string { return Json.stringify(value); } +export class JsonPipe extends BasePipe implements PipeFactory { + transform(value, args: List = null): string { return Json.stringify(value); } - create(cdRef): Pipe { return this } + create(cdRef: ChangeDetectorRef): Pipe { return this } } diff --git a/modules/angular2/src/change_detection/pipes/keyvalue_changes.ts b/modules/angular2/src/change_detection/pipes/keyvalue_changes.ts index f1ee23d343..16be78b276 100644 --- a/modules/angular2/src/change_detection/pipes/keyvalue_changes.ts +++ b/modules/angular2/src/change_detection/pipes/keyvalue_changes.ts @@ -1,6 +1,6 @@ import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang'; - +import {ChangeDetectorRef} from '../change_detector_ref'; import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe'; /** @@ -10,7 +10,7 @@ import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe'; export class KeyValueChangesFactory implements PipeFactory { supports(obj): boolean { return KeyValueChanges.supportsObj(obj); } - create(cdRef): Pipe { return new KeyValueChanges(); } + create(cdRef: ChangeDetectorRef): Pipe { return new KeyValueChanges(); } } /** @@ -31,7 +31,7 @@ export class KeyValueChanges extends BasePipe { supports(obj): boolean { return KeyValueChanges.supportsObj(obj); } - transform(map): any { + transform(map, args: List = null): any { if (this.check(map)) { return WrappedValue.wrap(this); } else { diff --git a/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts b/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts index 6d41e5c220..7684e4d4e6 100644 --- a/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts @@ -1,5 +1,6 @@ import {isString, StringWrapper, CONST} from 'angular2/src/facade/lang'; -import {Pipe} from './pipe'; +import {Pipe, PipeFactory} from './pipe'; +import {ChangeDetectorRef} from '../change_detector_ref'; /** * Implements lowercase transforms to text. @@ -30,7 +31,7 @@ export class LowerCasePipe implements Pipe { onDestroy(): void { this._latestValue = null; } - transform(value: string): string { + transform(value: string, args: List = null): string { if (this._latestValue !== value) { this._latestValue = value; return StringWrapper.toLowerCase(value); @@ -44,8 +45,8 @@ export class LowerCasePipe implements Pipe { * @exportedAs angular2/pipes */ @CONST() -export class LowerCaseFactory { +export class LowerCaseFactory implements PipeFactory { supports(str): boolean { return isString(str); } - create(): Pipe { return new LowerCasePipe(); } + create(cdRef: ChangeDetectorRef): Pipe { return new LowerCasePipe(); } } diff --git a/modules/angular2/src/change_detection/pipes/null_pipe.ts b/modules/angular2/src/change_detection/pipes/null_pipe.ts index 4037368d96..c14a711fec 100644 --- a/modules/angular2/src/change_detection/pipes/null_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/null_pipe.ts @@ -1,5 +1,6 @@ import {isBlank, CONST} from 'angular2/src/facade/lang'; import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe'; +import {ChangeDetectorRef} from '../change_detector_ref'; /** * @exportedAs angular2/pipes @@ -8,7 +9,7 @@ import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe'; export class NullPipeFactory implements PipeFactory { supports(obj): boolean { return NullPipe.supportsObj(obj); } - create(cdRef): Pipe { return new NullPipe(); } + create(cdRef: ChangeDetectorRef): Pipe { return new NullPipe(); } } /** @@ -21,7 +22,7 @@ export class NullPipe extends BasePipe { supports(obj): boolean { return NullPipe.supportsObj(obj); } - transform(value): WrappedValue { + transform(value, args: List = null): WrappedValue { if (!this.called) { this.called = true; return WrappedValue.wrap(null); diff --git a/modules/angular2/src/change_detection/pipes/observable_pipe.ts b/modules/angular2/src/change_detection/pipes/observable_pipe.ts index db02f1e130..fb14b84b80 100644 --- a/modules/angular2/src/change_detection/pipes/observable_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/observable_pipe.ts @@ -46,7 +46,7 @@ export class ObservablePipe implements Pipe { } } - transform(obs: Observable): any { + transform(obs: Observable, args: List = null): any { if (isBlank(this._subscription)) { this._subscribe(obs); return null; @@ -94,5 +94,5 @@ export class ObservablePipe implements Pipe { export class ObservablePipeFactory implements PipeFactory { supports(obs): boolean { return ObservableWrapper.isObservable(obs); } - create(cdRef): Pipe { return new ObservablePipe(cdRef); } + create(cdRef: ChangeDetectorRef): Pipe { return new ObservablePipe(cdRef); } } diff --git a/modules/angular2/src/change_detection/pipes/pipe.ts b/modules/angular2/src/change_detection/pipes/pipe.ts index 883665df99..203947bd27 100644 --- a/modules/angular2/src/change_detection/pipes/pipe.ts +++ b/modules/angular2/src/change_detection/pipes/pipe.ts @@ -1,4 +1,5 @@ import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang'; +import {ChangeDetectorRef} from '../change_detector_ref'; /** * Indicates that the result of a {@link Pipe} transformation has changed even though the reference @@ -43,7 +44,7 @@ var _wrappedIndex = 0; * * onDestroy() {} * - * transform(value) { + * transform(value, args = []) { * return `${value}${value}`; * } * } @@ -54,7 +55,7 @@ var _wrappedIndex = 0; export interface Pipe { supports(obj): boolean; onDestroy(): void; - transform(value: any): any; + transform(value: any, args: List): any; } /** @@ -74,12 +75,12 @@ export interface Pipe { export class BasePipe implements Pipe { supports(obj): boolean { return true; } onDestroy(): void {} - transform(value: any): any { return _abstract(); } + transform(value: any, args: List): any { return _abstract(); } } export interface PipeFactory { supports(obs): boolean; - create(cdRef): Pipe; + create(cdRef: ChangeDetectorRef): Pipe; } function _abstract() { diff --git a/modules/angular2/src/change_detection/pipes/promise_pipe.ts b/modules/angular2/src/change_detection/pipes/promise_pipe.ts index 3821484495..9c35e771ba 100644 --- a/modules/angular2/src/change_detection/pipes/promise_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/promise_pipe.ts @@ -1,6 +1,6 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {isBlank, isPresent, isPromise, CONST} from 'angular2/src/facade/lang'; -import {Pipe, WrappedValue} from './pipe'; +import {Pipe, PipeFactory, WrappedValue} from './pipe'; import {ChangeDetectorRef} from '../change_detector_ref'; /** @@ -45,7 +45,7 @@ export class PromisePipe implements Pipe { } } - transform(promise: Promise): any { + transform(promise: Promise, args: List = null): any { if (isBlank(this._sourcePromise)) { this._sourcePromise = promise; promise.then((val) => { @@ -81,8 +81,8 @@ export class PromisePipe implements Pipe { * @exportedAs angular2/pipes */ @CONST() -export class PromisePipeFactory { +export class PromisePipeFactory implements PipeFactory { supports(promise): boolean { return isPromise(promise); } - create(cdRef): Pipe { return new PromisePipe(cdRef); } + create(cdRef: ChangeDetectorRef): Pipe { return new PromisePipe(cdRef); } } diff --git a/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts b/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts index 78a9729184..ba9883ae4e 100644 --- a/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts @@ -1,5 +1,6 @@ import {isString, StringWrapper, CONST} from 'angular2/src/facade/lang'; -import {Pipe} from './pipe'; +import {Pipe, PipeFactory} from './pipe'; +import {ChangeDetectorRef} from '../change_detector_ref'; /** * Implements uppercase transforms to text. @@ -30,7 +31,7 @@ export class UpperCasePipe implements Pipe { onDestroy(): void { this._latestValue = null; } - transform(value: string): string { + transform(value: string, args: List = null): string { if (this._latestValue !== value) { this._latestValue = value; return StringWrapper.toUpperCase(value); @@ -44,8 +45,8 @@ export class UpperCasePipe implements Pipe { * @exportedAs angular2/pipes */ @CONST() -export class UpperCaseFactory { +export class UpperCaseFactory implements PipeFactory { supports(str): boolean { return isString(str); } - create(): Pipe { return new UpperCasePipe(); } + create(cdRef: ChangeDetectorRef): Pipe { return new UpperCasePipe(); } } diff --git a/modules/angular2/src/change_detection/proto_change_detector.ts b/modules/angular2/src/change_detection/proto_change_detector.ts index 9e2d4ef7d2..4330a7fc42 100644 --- a/modules/angular2/src/change_detection/proto_change_detector.ts +++ b/modules/angular2/src/change_detection/proto_change_detector.ts @@ -181,7 +181,8 @@ class _ConvertAstIntoProtoRecords implements AstVisitor { visitPipe(ast: BindingPipe): number { var value = ast.exp.visit(this); - return this._addRecord(RecordType.PIPE, ast.name, ast.name, [], null, value); + var args = this._visitAll(ast.args); + return this._addRecord(RecordType.PIPE, ast.name, ast.name, args, null, value); } visitKeyedAccess(ast: KeyedAccess): number { diff --git a/modules/angular2/src/directives/class.ts b/modules/angular2/src/directives/class.ts index 53a1330f7c..57b5242e90 100644 --- a/modules/angular2/src/directives/class.ts +++ b/modules/angular2/src/directives/class.ts @@ -28,7 +28,7 @@ export class CSSClass { } onCheck(): void { - var diff = this._pipe.transform(this._rawClass); + var diff = this._pipe.transform(this._rawClass, null); if (isPresent(diff) && isPresent(diff.wrapped)) { if (diff.wrapped instanceof IterableChanges) { this._applyArrayChanges(diff.wrapped); diff --git a/modules/angular2/src/directives/ng_for.ts b/modules/angular2/src/directives/ng_for.ts index 8378b838ca..f97fa7dd00 100644 --- a/modules/angular2/src/directives/ng_for.ts +++ b/modules/angular2/src/directives/ng_for.ts @@ -55,7 +55,7 @@ export class NgFor { } onCheck() { - var diff = this._pipe.transform(this._ngForOf); + var diff = this._pipe.transform(this._ngForOf, null); if (isPresent(diff)) this._applyChanges(diff.wrapped); } diff --git a/modules/angular2/src/directives/ng_style.ts b/modules/angular2/src/directives/ng_style.ts index 359deab0a9..61be845d5f 100644 --- a/modules/angular2/src/directives/ng_style.ts +++ b/modules/angular2/src/directives/ng_style.ts @@ -20,7 +20,7 @@ export class NgStyle { } onCheck() { - var diff = this._pipe.transform(this._rawStyle); + var diff = this._pipe.transform(this._rawStyle, null); if (isPresent(diff) && isPresent(diff.wrapped)) { this._applyChanges(diff.wrapped); } diff --git a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart index 85cf30d93e..bca0194b55 100644 --- a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart @@ -304,6 +304,8 @@ class _CodegenState { String _genPipeCheck(ProtoRecord r) { var context = _localNames[r.contextIndex]; + var argString = r.args.map((arg) => this._localNames[arg]).join(", "); + var oldValue = _fieldNames[r.selfIndex]; var newValue = _localNames[r.selfIndex]; var change = _changeNames[r.selfIndex]; @@ -322,7 +324,7 @@ class _CodegenState { $pipe = $_PIPE_REGISTRY_ACCESSOR.get('$pipeType', $context, $cdRef); } - $newValue = $pipe.transform($context); + $newValue = $pipe.transform($context, [$argString]); if (!$_IDENTICAL_CHECK_FN($oldValue, $newValue)) { $newValue = $_UTIL.unwrapValue($newValue); $change = true; diff --git a/modules/angular2/test/change_detection/change_detector_config.ts b/modules/angular2/test/change_detection/change_detector_config.ts index 098b917aad..44bcb83af6 100644 --- a/modules/angular2/test/change_detection/change_detector_config.ts +++ b/modules/angular2/test/change_detection/change_detector_config.ts @@ -300,6 +300,7 @@ var _availableDefinitions = [ '{z: 1}', '{z: a}', 'name | pipe', + "name | pipe:'one':address.city", 'value', 'a', 'address.city', diff --git a/modules/angular2/test/change_detection/change_detector_spec.ts b/modules/angular2/test/change_detection/change_detector_spec.ts index 8b3edd5a53..20ca3a550d 100644 --- a/modules/angular2/test/change_detection/change_detector_spec.ts +++ b/modules/angular2/test/change_detection/change_detector_spec.ts @@ -333,6 +333,15 @@ export function main() { val.changeDetector.detectChanges(); expect(val.dispatcher.loggedValues).toEqual(['bob state:0']); }); + + it('should support arguments in pipes', () => { + var registry = new FakePipeRegistry('pipe', () => new MultiArgPipe()); + var address = new Address('two'); + var person = new Person('value', address); + var val = _createChangeDetector("name | pipe:'one':address.city", person, registry); + val.changeDetector.detectChanges(); + expect(val.dispatcher.loggedValues).toEqual(['value one two default']); + }); }); it('should notify the dispatcher on all changes done', () => { @@ -861,7 +870,7 @@ class CountingPipe implements Pipe { supports(newValue) { return true; } - transform(value) { return `${value} state:${this.state ++}`; } + transform(value, args = null) { return `${value} state:${this.state ++}`; } } class OncePipe implements Pipe { @@ -872,7 +881,7 @@ class OncePipe implements Pipe { onDestroy() { this.destroyCalled = true; } - transform(value) { + transform(value, args = null) { this.called = true; return value; } @@ -883,7 +892,7 @@ class IdentityPipe implements Pipe { onDestroy() {} - transform(value) { return value; } + transform(value, args = null) { return value; } } class WrappedPipe implements Pipe { @@ -891,7 +900,18 @@ class WrappedPipe implements Pipe { onDestroy() {} - transform(value) { return WrappedValue.wrap(value); } + transform(value, args = null) { return WrappedValue.wrap(value); } +} + +class MultiArgPipe implements Pipe { + transform(value, args = null) { + var arg1 = args[0]; + var arg2 = args[1]; + var arg3 = args.length > 2 ? args[2] : 'default'; + return `${value} ${arg1} ${arg2} ${arg3}`; + } + supports(obj): boolean { return true; } + onDestroy(): void {} } class FakePipeRegistry extends PipeRegistry { diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts index e5bf701014..75f629ad12 100644 --- a/modules/angular2/test/core/compiler/integration_spec.ts +++ b/modules/angular2/test/core/compiler/integration_spec.ts @@ -1473,7 +1473,7 @@ class DoublePipe implements Pipe { supports(obj) { return true; } - transform(value) { return `${value}${value}`; } + transform(value, args = null) { return `${value}${value}`; } } @Injectable()