feat(pipes): support arguments in transform function

This commit is contained in:
Pouria Alimirzaei 2015-06-26 00:22:06 +04:30 committed by vsavkin
parent f0e962c55e
commit 600d53c68e
19 changed files with 74 additions and 41 deletions

View File

@ -240,6 +240,8 @@ export class ChangeDetectorJITGenerator {
_genPipeCheck(r: ProtoRecord): string { _genPipeCheck(r: ProtoRecord): string {
var context = this._localNames[r.contextIndex]; var context = this._localNames[r.contextIndex];
var argString = r.args.map((arg) => this._localNames[arg]).join(", ");
var oldValue = this._fieldNames[r.selfIndex]; var oldValue = this._fieldNames[r.selfIndex];
var newValue = this._localNames[r.selfIndex]; var newValue = this._localNames[r.selfIndex];
var change = this._changeNames[r.selfIndex]; var change = this._changeNames[r.selfIndex];
@ -259,7 +261,7 @@ export class ChangeDetectorJITGenerator {
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef}); ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef});
} }
${newValue} = ${pipe}.transform(${context}); ${newValue} = ${pipe}.transform(${context}, [${argString}]);
if (${oldValue} !== ${newValue}) { if (${oldValue} !== ${newValue}) {
${newValue} = ${UTIL}.unwrapValue(${newValue}); ${newValue} = ${UTIL}.unwrapValue(${newValue});
${change} = true; ${change} = true;

View File

@ -233,10 +233,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
_pipeCheck(proto: ProtoRecord, throwOnChange: boolean) { _pipeCheck(proto: ProtoRecord, throwOnChange: boolean) {
var context = this._readContext(proto); var context = this._readContext(proto);
var args = this._readArgs(proto);
var pipe = this._pipeFor(proto, context); var pipe = this._pipeFor(proto, context);
var prevValue = this._readSelf(proto); var prevValue = this._readSelf(proto);
var currValue = pipe.transform(context, args);
var currValue = pipe.transform(context);
if (!isSame(prevValue, currValue)) { if (!isSame(prevValue, currValue)) {
currValue = ChangeDetectionUtil.unwrapValue(currValue); currValue = ChangeDetectionUtil.unwrapValue(currValue);

View File

@ -16,12 +16,13 @@ import {
} from 'angular2/src/facade/lang'; } from 'angular2/src/facade/lang';
import {WrappedValue, Pipe, BasePipe, PipeFactory} from './pipe'; import {WrappedValue, Pipe, BasePipe, PipeFactory} from './pipe';
import {ChangeDetectorRef} from '../change_detector_ref';
@CONST() @CONST()
export class IterableChangesFactory implements PipeFactory { export class IterableChangesFactory implements PipeFactory {
supports(obj): boolean { return IterableChanges.supportsObj(obj); } 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<any> = null): any {
if (this.check(collection)) { if (this.check(collection)) {
return WrappedValue.wrap(this); return WrappedValue.wrap(this);
} else { } else {

View File

@ -1,5 +1,6 @@
import {isBlank, isPresent, Json, CONST} from 'angular2/src/facade/lang'; import {isBlank, isPresent, Json, CONST} from 'angular2/src/facade/lang';
import {Pipe, BasePipe, PipeFactory} from './pipe'; import {Pipe, BasePipe, PipeFactory} from './pipe';
import {ChangeDetectorRef} from '../change_detector_ref';
/** /**
* Implements json transforms to any object. * Implements json transforms to any object.
@ -27,8 +28,8 @@ import {Pipe, BasePipe, PipeFactory} from './pipe';
* @exportedAs angular2/pipes * @exportedAs angular2/pipes
*/ */
@CONST() @CONST()
export class JsonPipe extends BasePipe { export class JsonPipe extends BasePipe implements PipeFactory {
transform(value): string { return Json.stringify(value); } transform(value, args: List<any> = null): string { return Json.stringify(value); }
create(cdRef): Pipe { return this } create(cdRef: ChangeDetectorRef): Pipe { return this }
} }

View File

@ -1,6 +1,6 @@
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang'; import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang';
import {ChangeDetectorRef} from '../change_detector_ref';
import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe'; import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe';
/** /**
@ -10,7 +10,7 @@ import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe';
export class KeyValueChangesFactory implements PipeFactory { export class KeyValueChangesFactory implements PipeFactory {
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); } 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); } supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
transform(map): any { transform(map, args: List<any> = null): any {
if (this.check(map)) { if (this.check(map)) {
return WrappedValue.wrap(this); return WrappedValue.wrap(this);
} else { } else {

View File

@ -1,5 +1,6 @@
import {isString, StringWrapper, CONST} from 'angular2/src/facade/lang'; 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. * Implements lowercase transforms to text.
@ -30,7 +31,7 @@ export class LowerCasePipe implements Pipe {
onDestroy(): void { this._latestValue = null; } onDestroy(): void { this._latestValue = null; }
transform(value: string): string { transform(value: string, args: List<any> = null): string {
if (this._latestValue !== value) { if (this._latestValue !== value) {
this._latestValue = value; this._latestValue = value;
return StringWrapper.toLowerCase(value); return StringWrapper.toLowerCase(value);
@ -44,8 +45,8 @@ export class LowerCasePipe implements Pipe {
* @exportedAs angular2/pipes * @exportedAs angular2/pipes
*/ */
@CONST() @CONST()
export class LowerCaseFactory { export class LowerCaseFactory implements PipeFactory {
supports(str): boolean { return isString(str); } supports(str): boolean { return isString(str); }
create(): Pipe { return new LowerCasePipe(); } create(cdRef: ChangeDetectorRef): Pipe { return new LowerCasePipe(); }
} }

View File

@ -1,5 +1,6 @@
import {isBlank, CONST} from 'angular2/src/facade/lang'; import {isBlank, CONST} from 'angular2/src/facade/lang';
import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe'; import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe';
import {ChangeDetectorRef} from '../change_detector_ref';
/** /**
* @exportedAs angular2/pipes * @exportedAs angular2/pipes
@ -8,7 +9,7 @@ import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe';
export class NullPipeFactory implements PipeFactory { export class NullPipeFactory implements PipeFactory {
supports(obj): boolean { return NullPipe.supportsObj(obj); } 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); } supports(obj): boolean { return NullPipe.supportsObj(obj); }
transform(value): WrappedValue { transform(value, args: List<any> = null): WrappedValue {
if (!this.called) { if (!this.called) {
this.called = true; this.called = true;
return WrappedValue.wrap(null); return WrappedValue.wrap(null);

View File

@ -46,7 +46,7 @@ export class ObservablePipe implements Pipe {
} }
} }
transform(obs: Observable): any { transform(obs: Observable, args: List<any> = null): any {
if (isBlank(this._subscription)) { if (isBlank(this._subscription)) {
this._subscribe(obs); this._subscribe(obs);
return null; return null;
@ -94,5 +94,5 @@ export class ObservablePipe implements Pipe {
export class ObservablePipeFactory implements PipeFactory { export class ObservablePipeFactory implements PipeFactory {
supports(obs): boolean { return ObservableWrapper.isObservable(obs); } supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
create(cdRef): Pipe { return new ObservablePipe(cdRef); } create(cdRef: ChangeDetectorRef): Pipe { return new ObservablePipe(cdRef); }
} }

View File

@ -1,4 +1,5 @@
import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang'; 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 * Indicates that the result of a {@link Pipe} transformation has changed even though the reference
@ -43,7 +44,7 @@ var _wrappedIndex = 0;
* *
* onDestroy() {} * onDestroy() {}
* *
* transform(value) { * transform(value, args = []) {
* return `${value}${value}`; * return `${value}${value}`;
* } * }
* } * }
@ -54,7 +55,7 @@ var _wrappedIndex = 0;
export interface Pipe { export interface Pipe {
supports(obj): boolean; supports(obj): boolean;
onDestroy(): void; onDestroy(): void;
transform(value: any): any; transform(value: any, args: List<any>): any;
} }
/** /**
@ -74,12 +75,12 @@ export interface Pipe {
export class BasePipe implements Pipe { export class BasePipe implements Pipe {
supports(obj): boolean { return true; } supports(obj): boolean { return true; }
onDestroy(): void {} onDestroy(): void {}
transform(value: any): any { return _abstract(); } transform(value: any, args: List<any>): any { return _abstract(); }
} }
export interface PipeFactory { export interface PipeFactory {
supports(obs): boolean; supports(obs): boolean;
create(cdRef): Pipe; create(cdRef: ChangeDetectorRef): Pipe;
} }
function _abstract() { function _abstract() {

View File

@ -1,6 +1,6 @@
import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {isBlank, isPresent, isPromise, CONST} from 'angular2/src/facade/lang'; 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'; import {ChangeDetectorRef} from '../change_detector_ref';
/** /**
@ -45,7 +45,7 @@ export class PromisePipe implements Pipe {
} }
} }
transform(promise: Promise<any>): any { transform(promise: Promise<any>, args: List<any> = null): any {
if (isBlank(this._sourcePromise)) { if (isBlank(this._sourcePromise)) {
this._sourcePromise = promise; this._sourcePromise = promise;
promise.then((val) => { promise.then((val) => {
@ -81,8 +81,8 @@ export class PromisePipe implements Pipe {
* @exportedAs angular2/pipes * @exportedAs angular2/pipes
*/ */
@CONST() @CONST()
export class PromisePipeFactory { export class PromisePipeFactory implements PipeFactory {
supports(promise): boolean { return isPromise(promise); } supports(promise): boolean { return isPromise(promise); }
create(cdRef): Pipe { return new PromisePipe(cdRef); } create(cdRef: ChangeDetectorRef): Pipe { return new PromisePipe(cdRef); }
} }

View File

@ -1,5 +1,6 @@
import {isString, StringWrapper, CONST} from 'angular2/src/facade/lang'; 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. * Implements uppercase transforms to text.
@ -30,7 +31,7 @@ export class UpperCasePipe implements Pipe {
onDestroy(): void { this._latestValue = null; } onDestroy(): void { this._latestValue = null; }
transform(value: string): string { transform(value: string, args: List<any> = null): string {
if (this._latestValue !== value) { if (this._latestValue !== value) {
this._latestValue = value; this._latestValue = value;
return StringWrapper.toUpperCase(value); return StringWrapper.toUpperCase(value);
@ -44,8 +45,8 @@ export class UpperCasePipe implements Pipe {
* @exportedAs angular2/pipes * @exportedAs angular2/pipes
*/ */
@CONST() @CONST()
export class UpperCaseFactory { export class UpperCaseFactory implements PipeFactory {
supports(str): boolean { return isString(str); } supports(str): boolean { return isString(str); }
create(): Pipe { return new UpperCasePipe(); } create(cdRef: ChangeDetectorRef): Pipe { return new UpperCasePipe(); }
} }

View File

@ -181,7 +181,8 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
visitPipe(ast: BindingPipe): number { visitPipe(ast: BindingPipe): number {
var value = ast.exp.visit(this); 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 { visitKeyedAccess(ast: KeyedAccess): number {

View File

@ -28,7 +28,7 @@ export class CSSClass {
} }
onCheck(): void { onCheck(): void {
var diff = this._pipe.transform(this._rawClass); var diff = this._pipe.transform(this._rawClass, null);
if (isPresent(diff) && isPresent(diff.wrapped)) { if (isPresent(diff) && isPresent(diff.wrapped)) {
if (diff.wrapped instanceof IterableChanges) { if (diff.wrapped instanceof IterableChanges) {
this._applyArrayChanges(diff.wrapped); this._applyArrayChanges(diff.wrapped);

View File

@ -55,7 +55,7 @@ export class NgFor {
} }
onCheck() { onCheck() {
var diff = this._pipe.transform(this._ngForOf); var diff = this._pipe.transform(this._ngForOf, null);
if (isPresent(diff)) this._applyChanges(diff.wrapped); if (isPresent(diff)) this._applyChanges(diff.wrapped);
} }

View File

@ -20,7 +20,7 @@ export class NgStyle {
} }
onCheck() { onCheck() {
var diff = this._pipe.transform(this._rawStyle); var diff = this._pipe.transform(this._rawStyle, null);
if (isPresent(diff) && isPresent(diff.wrapped)) { if (isPresent(diff) && isPresent(diff.wrapped)) {
this._applyChanges(diff.wrapped); this._applyChanges(diff.wrapped);
} }

View File

@ -304,6 +304,8 @@ class _CodegenState {
String _genPipeCheck(ProtoRecord r) { String _genPipeCheck(ProtoRecord r) {
var context = _localNames[r.contextIndex]; var context = _localNames[r.contextIndex];
var argString = r.args.map((arg) => this._localNames[arg]).join(", ");
var oldValue = _fieldNames[r.selfIndex]; var oldValue = _fieldNames[r.selfIndex];
var newValue = _localNames[r.selfIndex]; var newValue = _localNames[r.selfIndex];
var change = _changeNames[r.selfIndex]; var change = _changeNames[r.selfIndex];
@ -322,7 +324,7 @@ class _CodegenState {
$pipe = $_PIPE_REGISTRY_ACCESSOR.get('$pipeType', $context, $cdRef); $pipe = $_PIPE_REGISTRY_ACCESSOR.get('$pipeType', $context, $cdRef);
} }
$newValue = $pipe.transform($context); $newValue = $pipe.transform($context, [$argString]);
if (!$_IDENTICAL_CHECK_FN($oldValue, $newValue)) { if (!$_IDENTICAL_CHECK_FN($oldValue, $newValue)) {
$newValue = $_UTIL.unwrapValue($newValue); $newValue = $_UTIL.unwrapValue($newValue);
$change = true; $change = true;

View File

@ -300,6 +300,7 @@ var _availableDefinitions = [
'{z: 1}', '{z: 1}',
'{z: a}', '{z: a}',
'name | pipe', 'name | pipe',
"name | pipe:'one':address.city",
'value', 'value',
'a', 'a',
'address.city', 'address.city',

View File

@ -333,6 +333,15 @@ export function main() {
val.changeDetector.detectChanges(); val.changeDetector.detectChanges();
expect(val.dispatcher.loggedValues).toEqual(['bob state:0']); 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', () => { it('should notify the dispatcher on all changes done', () => {
@ -861,7 +870,7 @@ class CountingPipe implements Pipe {
supports(newValue) { return true; } supports(newValue) { return true; }
transform(value) { return `${value} state:${this.state ++}`; } transform(value, args = null) { return `${value} state:${this.state ++}`; }
} }
class OncePipe implements Pipe { class OncePipe implements Pipe {
@ -872,7 +881,7 @@ class OncePipe implements Pipe {
onDestroy() { this.destroyCalled = true; } onDestroy() { this.destroyCalled = true; }
transform(value) { transform(value, args = null) {
this.called = true; this.called = true;
return value; return value;
} }
@ -883,7 +892,7 @@ class IdentityPipe implements Pipe {
onDestroy() {} onDestroy() {}
transform(value) { return value; } transform(value, args = null) { return value; }
} }
class WrappedPipe implements Pipe { class WrappedPipe implements Pipe {
@ -891,7 +900,18 @@ class WrappedPipe implements Pipe {
onDestroy() {} 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 { class FakePipeRegistry extends PipeRegistry {

View File

@ -1473,7 +1473,7 @@ class DoublePipe implements Pipe {
supports(obj) { return true; } supports(obj) { return true; }
transform(value) { return `${value}${value}`; } transform(value, args = null) { return `${value}${value}`; }
} }
@Injectable() @Injectable()