diff --git a/modules/angular2/change_detection.ts b/modules/angular2/change_detection.ts index a6efae9908..26c6819503 100644 --- a/modules/angular2/change_detection.ts +++ b/modules/angular2/change_detection.ts @@ -29,8 +29,5 @@ export { KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory, - PipeFactory, - BasePipe, - NullPipe, - NullPipeFactory + BasePipe } from 'angular2/src/change_detection/change_detection'; diff --git a/modules/angular2/src/change_detection/change_detection.ts b/modules/angular2/src/change_detection/change_detection.ts index bbf553a736..561b1d582f 100644 --- a/modules/angular2/src/change_detection/change_detection.ts +++ b/modules/angular2/src/change_detection/change_detection.ts @@ -1,22 +1,20 @@ import {JitProtoChangeDetector} from './jit_proto_change_detector'; import {PregenProtoChangeDetector} from './pregen_proto_change_detector'; import {DynamicProtoChangeDetector} from './proto_change_detector'; -import {PipeFactory, Pipe} from './pipes/pipe'; import {Pipes} from './pipes/pipes'; import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs'; import {DefaultIterableDifferFactory} from './differs/default_iterable_differ'; import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs'; import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; -import {AsyncPipeFactory} from './pipes/async_pipe'; +import {AsyncPipe} from './pipes/async_pipe'; import {UpperCasePipe} from './pipes/uppercase_pipe'; import {LowerCasePipe} from './pipes/lowercase_pipe'; import {JsonPipe} from './pipes/json_pipe'; -import {LimitToPipeFactory} from './pipes/limit_to_pipe'; +import {LimitToPipe} from './pipes/limit_to_pipe'; import {DatePipe} from './pipes/date_pipe'; import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe'; -import {NullPipeFactory} from './pipes/null_pipe'; import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces'; -import {Inject, Injectable, OpaqueToken, Optional} from 'angular2/di'; +import {Injector, Inject, Injectable, OpaqueToken, Optional, Binding} from 'angular2/di'; import {List, StringMap, StringMapWrapper} from 'angular2/src/facade/collection'; import {CONST, CONST_EXPR, isPresent, BaseException} from 'angular2/src/facade/lang'; @@ -55,10 +53,28 @@ export {ChangeDetectorRef} from './change_detector_ref'; export {Pipes} from './pipes/pipes'; export {IterableDiffers, IterableDiffer, IterableDifferFactory} from './differs/iterable_differs'; export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs'; -export {WrappedValue, Pipe, PipeFactory, BasePipe} from './pipes/pipe'; -export {NullPipe, NullPipeFactory} from './pipes/null_pipe'; +export {WrappedValue, Pipe, BasePipe} from './pipes/pipe'; +function createPipes(inj: Injector): Pipes { + return new Pipes( + { + "async": AsyncPipe, + "uppercase": UpperCasePipe, + "lowercase": LowerCasePipe, + "json": JsonPipe, + "limitTo": LimitToPipe, + "number": DecimalPipe, + "percent": PercentPipe, + "currency": CurrencyPipe, + "date": DatePipe + }, + inj); +} + +export const defaultPipes: Binding = + CONST_EXPR(new Binding(Pipes, {toFactory: createPipes, deps: [Injector]})); + /** * Structural diffing for `Object`s and `Map`s. */ @@ -71,73 +87,6 @@ export const keyValDiff: KeyValueDifferFactory[] = export const iterableDiff: IterableDifferFactory[] = CONST_EXPR([CONST_EXPR(new DefaultIterableDifferFactory())]); -/** - * Async binding to such types as Observable. - */ -export const async: List = - CONST_EXPR([CONST_EXPR(new AsyncPipeFactory()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Uppercase text transform. - */ -export const uppercase: List = - CONST_EXPR([CONST_EXPR(new UpperCasePipe()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Lowercase text transform. - */ -export const lowercase: List = - CONST_EXPR([CONST_EXPR(new LowerCasePipe()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Json stringify transform. - */ -export const json: List = - CONST_EXPR([CONST_EXPR(new JsonPipe()), CONST_EXPR(new NullPipeFactory())]); - -/** - * LimitTo text transform. - */ -export const limitTo: List = - CONST_EXPR([CONST_EXPR(new LimitToPipeFactory()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Number number transform. - */ -export const decimal: List = - CONST_EXPR([CONST_EXPR(new DecimalPipe()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Percent number transform. - */ -export const percent: List = - CONST_EXPR([CONST_EXPR(new PercentPipe()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Currency number transform. - */ -export const currency: List = - CONST_EXPR([CONST_EXPR(new CurrencyPipe()), CONST_EXPR(new NullPipeFactory())]); - -/** - * Date/time formatter. - */ -export const date: List = - CONST_EXPR([CONST_EXPR(new DatePipe()), CONST_EXPR(new NullPipeFactory())]); - - -export const defaultPipes: Pipes = CONST_EXPR(new Pipes({ - "async": async, - "uppercase": uppercase, - "lowercase": lowercase, - "json": json, - "limitTo": limitTo, - "number": decimal, - "percent": percent, - "currency": currency, - "date": date -})); - export const defaultIterableDiffers = CONST_EXPR(new IterableDiffers(iterableDiff)); export const defaultKeyValueDiffers = CONST_EXPR(new KeyValueDiffers(keyValDiff)); 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 6eb3914684..7b48951ecd 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -184,10 +184,7 @@ export class ChangeDetectorJITGenerator { var read = ` if (${pipe} === ${UTIL}.uninitialized) { - ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}', ${context}, ${cdRef}); - } else if (!${pipe}.supports(${context})) { - ${pipe}.onDestroy(); - ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}', ${context}, ${cdRef}); + ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}', ${cdRef}); } ${newValue} = ${pipe}.transform(${context}, [${argString}]); `; diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index 21eaa4b141..2d1818f98d 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -267,13 +267,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector { _pipeFor(proto: ProtoRecord, context) { var storedPipe = this._readPipe(proto); - if (isPresent(storedPipe) && storedPipe.supports(context)) { - return storedPipe; - } - if (isPresent(storedPipe)) { - storedPipe.onDestroy(); - } - var pipe = this.pipes.get(proto.name, context, this.ref); + if (isPresent(storedPipe)) return storedPipe; + + var pipe = this.pipes.get(proto.name, this.ref); this._writePipe(proto, pipe); return pipe; } diff --git a/modules/angular2/src/change_detection/pipes/async_pipe.ts b/modules/angular2/src/change_detection/pipes/async_pipe.ts index 878af7e832..ce8e483728 100644 --- a/modules/angular2/src/change_detection/pipes/async_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/async_pipe.ts @@ -1,6 +1,7 @@ import {isBlank, isPresent, isPromise, CONST, BaseException} from 'angular2/src/facade/lang'; import {Observable, Promise, ObservableWrapper} from 'angular2/src/facade/async'; -import {Pipe, WrappedValue, PipeFactory} from './pipe'; +import {Injectable} from 'angular2/di'; +import {Pipe, WrappedValue, InvalidPipeArgumentException} from './pipe'; import {ChangeDetectorRef} from '../change_detector_ref'; @@ -52,6 +53,7 @@ var _observableStrategy = new ObservableStrategy(); * * ``` */ +@Injectable() export class AsyncPipe implements Pipe { _latestValue: Object = null; _latestReturnedValue: Object = null; @@ -62,8 +64,6 @@ export class AsyncPipe implements Pipe { constructor(public _ref: ChangeDetectorRef) {} - supports(obj: any): boolean { return true; } - onDestroy(): void { if (isPresent(this._subscription)) { this._dispose(); @@ -98,13 +98,13 @@ export class AsyncPipe implements Pipe { this._strategy.createSubscription(obj, value => this._updateLatestValue(obj, value)); } - _selectStrategy(obj: Observable | Promise) { + _selectStrategy(obj: Observable | Promise): any { if (isPromise(obj)) { return _promiseStrategy; } else if (ObservableWrapper.isObservable(obj)) { return _observableStrategy; } else { - throw new BaseException(`Async pipe does not support object '${obj}'`); + throw new InvalidPipeArgumentException(AsyncPipe, obj); } } @@ -122,13 +122,4 @@ export class AsyncPipe implements Pipe { this._ref.requestCheck(); } } -} - -/** - * Provides a factory for [AsyncPipe]. - */ -@CONST() -export class AsyncPipeFactory implements PipeFactory { - supports(obj: any): boolean { return true; } - create(cdRef: ChangeDetectorRef): Pipe { return new AsyncPipe(cdRef); } -} +} \ No newline at end of file diff --git a/modules/angular2/src/change_detection/pipes/date_pipe.ts b/modules/angular2/src/change_detection/pipes/date_pipe.ts index 5dde144c70..b04f88c13b 100644 --- a/modules/angular2/src/change_detection/pipes/date_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/date_pipe.ts @@ -5,12 +5,13 @@ import { Date, DateWrapper, CONST, + isBlank, FunctionWrapper } from 'angular2/src/facade/lang'; import {DateFormatter} from 'angular2/src/facade/intl'; +import {Injectable} from 'angular2/di'; import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection'; -import {Pipe, BasePipe, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; // TODO: move to a global configable location along with other i18n components. var defaultLocale: string = 'en-US'; @@ -70,7 +71,8 @@ var defaultLocale: string = 'en-US'; * {{ dateObj | date:'mmss' }} // output is '43:11' */ @CONST() -export class DatePipe extends BasePipe implements PipeFactory { +@Injectable() +export class DatePipe extends BasePipe { static _ALIASES = { 'medium': 'yMMMdjms', 'short': 'yMdjm', @@ -84,6 +86,12 @@ export class DatePipe extends BasePipe implements PipeFactory { transform(value: any, args: List): string { + if (isBlank(value)) return null; + + if (!this.supports(value)) { + throw new InvalidPipeArgumentException(DatePipe, value); + } + var pattern: string = isPresent(args) && args.length > 0 ? args[0] : 'mediumDate'; if (isNumber(value)) { value = DateWrapper.fromMillis(value); @@ -95,6 +103,4 @@ export class DatePipe extends BasePipe implements PipeFactory { } supports(obj: any): boolean { return isDate(obj) || isNumber(obj); } - - create(cdRef: ChangeDetectorRef): Pipe { return this; } } diff --git a/modules/angular2/src/change_detection/pipes/json_pipe.ts b/modules/angular2/src/change_detection/pipes/json_pipe.ts index 2b6512ce8c..f68921efcf 100644 --- a/modules/angular2/src/change_detection/pipes/json_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/json_pipe.ts @@ -1,6 +1,6 @@ import {isBlank, isPresent, Json, CONST} from 'angular2/src/facade/lang'; -import {Pipe, BasePipe, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {Injectable} from 'angular2/di'; +import {Pipe, BasePipe} from './pipe'; /** * Implements json transforms to any object. @@ -26,8 +26,7 @@ import {ChangeDetectorRef} from '../change_detector_ref'; * ``` */ @CONST() -export class JsonPipe extends BasePipe implements PipeFactory { +@Injectable() +export class JsonPipe extends BasePipe { transform(value: any, args: List = null): string { return Json.stringify(value); } - - create(cdRef: ChangeDetectorRef): Pipe { return this; } } diff --git a/modules/angular2/src/change_detection/pipes/limit_to_pipe.ts b/modules/angular2/src/change_detection/pipes/limit_to_pipe.ts index c7e296dc74..1ec2664d1b 100644 --- a/modules/angular2/src/change_detection/pipes/limit_to_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/limit_to_pipe.ts @@ -8,8 +8,8 @@ import { } from 'angular2/src/facade/lang'; import {ListWrapper} from 'angular2/src/facade/collection'; import {Math} from 'angular2/src/facade/math'; -import {WrappedValue, Pipe, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {Injectable} from 'angular2/di'; +import {WrappedValue, Pipe, InvalidPipeArgumentException} from './pipe'; /** * Creates a new List or String containing only a prefix/suffix of the @@ -50,15 +50,18 @@ import {ChangeDetectorRef} from '../change_detector_ref'; * {{ 'abcdefghij' | limitTo: -4 }} // output is 'ghij' * {{ 'abcdefghij' | limitTo: -100 }} // output is 'abcdefghij' */ +@Injectable() export class LimitToPipe implements Pipe { - static supportsObj(obj: any): boolean { return isString(obj) || isArray(obj); } - - supports(obj: any): boolean { return LimitToPipe.supportsObj(obj); } + supports(obj: any): boolean { return isString(obj) || isArray(obj); } transform(value: any, args: List = null): any { if (isBlank(args) || args.length == 0) { throw new BaseException('limitTo pipe requires one argument'); } + if (!this.supports(value)) { + throw new InvalidPipeArgumentException(LimitToPipe, value); + } + if (isBlank(value)) return value; var limit: int = args[0]; var left = 0, right = Math.min(limit, value.length); if (limit < 0) { @@ -73,10 +76,3 @@ export class LimitToPipe implements Pipe { onDestroy(): void {} } - -@CONST() -export class LimitToPipeFactory implements PipeFactory { - supports(obj: any): boolean { return LimitToPipe.supportsObj(obj); } - - create(cdRef: ChangeDetectorRef): Pipe { return new LimitToPipe(); } -} diff --git a/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts b/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts index 84f2ae82d4..10933cc86b 100644 --- a/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts @@ -1,6 +1,6 @@ -import {isString, StringWrapper, CONST} from 'angular2/src/facade/lang'; -import {Pipe, BasePipe, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {isString, StringWrapper, CONST, isBlank} from 'angular2/src/facade/lang'; +import {Injectable} from 'angular2/di'; +import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; /** * Implements lowercase transforms to text. @@ -23,12 +23,13 @@ import {ChangeDetectorRef} from '../change_detector_ref'; * ``` */ @CONST() -export class LowerCasePipe extends BasePipe implements PipeFactory { - supports(str: any): boolean { return isString(str); } - +@Injectable() +export class LowerCasePipe extends BasePipe { transform(value: string, args: List = null): string { + if (isBlank(value)) return value; + if (!isString(value)) { + throw new InvalidPipeArgumentException(LowerCasePipe, value); + } return StringWrapper.toLowerCase(value); } - - create(cdRef: ChangeDetectorRef): Pipe { return this; } } diff --git a/modules/angular2/src/change_detection/pipes/null_pipe.ts b/modules/angular2/src/change_detection/pipes/null_pipe.ts deleted file mode 100644 index 93492424ad..0000000000 --- a/modules/angular2/src/change_detection/pipes/null_pipe.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {isBlank, CONST} from 'angular2/src/facade/lang'; -import {Pipe, BasePipe, WrappedValue, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; - -@CONST() -export class NullPipeFactory implements PipeFactory { - supports(obj: any): boolean { return NullPipe.supportsObj(obj); } - - create(cdRef: ChangeDetectorRef): Pipe { return new NullPipe(); } -} - -export class NullPipe extends BasePipe { - called: boolean = false; - - static supportsObj(obj: any): boolean { return isBlank(obj); } - - supports(obj: any): boolean { return NullPipe.supportsObj(obj); } - - transform(value: any, args: List = null): WrappedValue { - if (!this.called) { - this.called = true; - return WrappedValue.wrap(null); - } else { - return null; - } - } -} diff --git a/modules/angular2/src/change_detection/pipes/number_pipe.ts b/modules/angular2/src/change_detection/pipes/number_pipe.ts index 1663a70e4a..f6a29ca4b5 100644 --- a/modules/angular2/src/change_detection/pipes/number_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/number_pipe.ts @@ -10,17 +10,22 @@ import { FunctionWrapper } from 'angular2/src/facade/lang'; import {NumberFormatter, NumberFormatStyle} from 'angular2/src/facade/intl'; +import {Injectable} from 'angular2/di'; import {ListWrapper} from 'angular2/src/facade/collection'; -import {Pipe, BasePipe, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; var defaultLocale: string = 'en-US'; var _re = RegExpWrapper.create('^(\\d+)?\\.((\\d+)(\\-(\\d+))?)?$'); @CONST() -export class NumberPipe extends BasePipe implements PipeFactory { +@Injectable() +export class NumberPipe extends BasePipe { static _format(value: number, style: NumberFormatStyle, digits: string, currency: string = null, currencyAsSymbol: boolean = false): string { + if (isBlank(value)) return null; + if (!isNumber(value)) { + throw new InvalidPipeArgumentException(NumberPipe, value); + } var minInt = 1, minFraction = 0, maxFraction = 3; if (isPresent(digits)) { var parts = RegExpWrapper.firstMatch(_re, digits); @@ -45,10 +50,6 @@ export class NumberPipe extends BasePipe implements PipeFactory { currencyAsSymbol: currencyAsSymbol }); } - - supports(obj: any): boolean { return isNumber(obj); } - - create(cdRef: ChangeDetectorRef): Pipe { return this; } } /** diff --git a/modules/angular2/src/change_detection/pipes/pipe.ts b/modules/angular2/src/change_detection/pipes/pipe.ts index e4f747343d..de109ca4e3 100644 --- a/modules/angular2/src/change_detection/pipes/pipe.ts +++ b/modules/angular2/src/change_detection/pipes/pipe.ts @@ -1,5 +1,4 @@ -import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {ABSTRACT, BaseException, CONST, Type} from 'angular2/src/facade/lang'; /** * Indicates that the result of a {@link Pipe} transformation has changed even though the reference @@ -50,8 +49,6 @@ export interface Pipe { /** * Query if a pipe supports a particular object instance. */ - supports(obj): boolean; - onDestroy(): void; transform(value: any, args: List): any; @@ -72,19 +69,16 @@ export interface Pipe { */ @CONST() export class BasePipe implements Pipe { - supports(obj: any): boolean { return true; } onDestroy(): void {} transform(value: any, args: List): any { return _abstract(); } } -/** - * - */ -export interface PipeFactory { - supports(obs): boolean; - create(cdRef: ChangeDetectorRef): Pipe; +export class InvalidPipeArgumentException extends BaseException { + constructor(type: Type, value: Object) { + super(`Invalid argument '${value}' for pipe '${type}'`); + } } function _abstract() { throw new BaseException('This method is abstract'); -} +} \ No newline at end of file diff --git a/modules/angular2/src/change_detection/pipes/pipes.ts b/modules/angular2/src/change_detection/pipes/pipes.ts index 7e3a6e51e9..110c2f8089 100644 --- a/modules/angular2/src/change_detection/pipes/pipes.ts +++ b/modules/angular2/src/change_detection/pipes/pipes.ts @@ -1,44 +1,42 @@ import {ListWrapper, isListLikeIterable, StringMapWrapper} from 'angular2/src/facade/collection'; -import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang'; -import {Pipe, PipeFactory} from './pipe'; -import {Injectable, OptionalMetadata, SkipSelfMetadata} from 'angular2/di'; +import {isBlank, isPresent, BaseException, CONST, Type} from 'angular2/src/facade/lang'; +import {Pipe} from './pipe'; +import {Injectable, OptionalMetadata, SkipSelfMetadata, Binding, Injector, bind} from 'angular2/di'; import {ChangeDetectorRef} from '../change_detector_ref'; -import {Binding} from 'angular2/di'; @Injectable() @CONST() export class Pipes { /** - * Map of {@link Pipe} names to {@link PipeFactory} lists used to configure the - * {@link Pipes} registry. + * Map of {@link Pipe} names to {@link Pipe} implementations. * * #Example * * ``` * var pipesConfig = { - * 'json': [jsonPipeFactory] + * 'json': JsonPipe * } * @Component({ * viewBindings: [ - * bind(Pipes).toValue(new Pipes(pipesConfig)) + * bind(Pipes).toFactory(inj => new Pipes(pipesConfig, in), [Injector]) * ] * }) * ``` */ - config: StringMap; + config: StringMap; + constructor(config: StringMap, public injector: Injector) { + this.config = config; + } - constructor(config: StringMap) { this.config = config; } - - get(type: string, obj: any, cdRef?: ChangeDetectorRef, existingPipe?: Pipe): Pipe { - if (isPresent(existingPipe) && existingPipe.supports(obj)) return existingPipe; - - if (isPresent(existingPipe)) existingPipe.onDestroy(); - - var factories = this._getListOfFactories(type, obj); - var factory = this._getMatchingFactory(factories, type, obj); - - return factory.create(cdRef); + get(type: string, cdRef: ChangeDetectorRef): Pipe { + var typeOrBinding = this.config[type]; + if (isBlank(typeOrBinding)) { + throw new BaseException(`Cannot find pipe '${type}'.`); + } + // this is a temporary workaround and will be removed + return this.injector.resolveAndCreateChild([bind(ChangeDetectorRef).toValue(cdRef)]) + .resolveAndInstantiate(typeOrBinding); } /** @@ -46,16 +44,7 @@ export class Pipes { * inherited {@link Pipes} instance with the provided config and return a new * {@link Pipes} instance. * - * If the provided config contains a key that is not yet present in the - * inherited {@link Pipes}' config, a new {@link PipeFactory} list will be created - * for that key. Otherwise, the provided config will be merged with the inherited - * {@link Pipes} instance by prepending pipes to their respective keys, without mutating - * the inherited {@link Pipes}. - * - * The following example shows how to extend an existing list of `async` factories - * with a new {@link PipeFactory}, which will only be applied to the injector - * for this component and its children. This step is all that's required to make a new - * pipe available to this component's template. + * The provided config is merged with the {@link Pipes} instance. * * # Example * @@ -63,55 +52,33 @@ export class Pipes { * @Component({ * viewBindings: [ * Pipes.extend({ - * async: [newAsyncPipe] + * 'bithdayFormat': BirthdayFormat * }) * ] * }) * ``` */ - static extend(config: StringMap): Binding { + static extend(config: StringMap): Binding { return new Binding(Pipes, { - toFactory: (pipes: Pipes) => { + toFactory: (pipes: Pipes, injector: Injector) => { if (isBlank(pipes)) { // Typically would occur when calling Pipe.extend inside of dependencies passed to // bootstrap(), which would override default pipes instead of extending them. throw new BaseException('Cannot extend Pipes without a parent injector'); } - return Pipes.create(config, pipes); + return Pipes.create(config, injector, pipes); }, // Dependency technically isn't optional, but we can provide a better error message this way. - deps: [[Pipes, new SkipSelfMetadata(), new OptionalMetadata()]] + deps: [[Pipes, new SkipSelfMetadata(), new OptionalMetadata()], Injector] }); } - static create(config: StringMap, pipes: Pipes = null): Pipes { + static create(config: StringMap, injector: Injector, + pipes: Pipes = null): Pipes { if (isPresent(pipes)) { - StringMapWrapper.forEach(pipes.config, (v: PipeFactory[], k: string) => { - if (StringMapWrapper.contains(config, k)) { - var configFactories: PipeFactory[] = config[k]; - config[k] = configFactories.concat(v); - } else { - config[k] = ListWrapper.clone(v); - } - }); + return new Pipes(StringMapWrapper.merge(pipes.config, config), injector); + } else { + return new Pipes(config, injector); } - return new Pipes(config); - } - - private _getListOfFactories(type: string, obj: any): PipeFactory[] { - var listOfFactories = this.config[type]; - if (isBlank(listOfFactories)) { - throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`); - } - return listOfFactories; - } - - private _getMatchingFactory(listOfFactories: PipeFactory[], type: string, obj: any): PipeFactory { - var matchingFactory = - ListWrapper.find(listOfFactories, pipeFactory => pipeFactory.supports(obj)); - if (isBlank(matchingFactory)) { - throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`); - } - return matchingFactory; } } diff --git a/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts b/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts index 033ba7c838..ff82c588dd 100644 --- a/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts +++ b/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts @@ -1,6 +1,6 @@ -import {isString, StringWrapper, CONST} from 'angular2/src/facade/lang'; -import {Pipe, BasePipe, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; +import {isString, StringWrapper, CONST, isBlank} from 'angular2/src/facade/lang'; +import {Injectable} from 'angular2/di'; +import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; /** * Implements uppercase transforms to text. @@ -23,12 +23,13 @@ import {ChangeDetectorRef} from '../change_detector_ref'; * ``` */ @CONST() -export class UpperCasePipe extends BasePipe implements PipeFactory { - supports(str: any): boolean { return isString(str); } - +@Injectable() +export class UpperCasePipe extends BasePipe { transform(value: string, args: List = null): string { + if (isBlank(value)) return value; + if (!isString(value)) { + throw new InvalidPipeArgumentException(UpperCasePipe, value); + } return StringWrapper.toUpperCase(value); } - - create(cdRef: ChangeDetectorRef): Pipe { return this; } } diff --git a/modules/angular2/src/core/application_common.ts b/modules/angular2/src/core/application_common.ts index f3a9bcf0d0..163545f64c 100644 --- a/modules/angular2/src/core/application_common.ts +++ b/modules/angular2/src/core/application_common.ts @@ -137,7 +137,7 @@ function _injectorBindings(appComponentType): List> { Compiler, CompilerCache, ViewResolver, - bind(Pipes).toValue(defaultPipes), + defaultPipes, bind(IterableDiffers).toValue(defaultIterableDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(ChangeDetection).toClass(bestChangeDetection), diff --git a/modules/angular2/src/di/injector.ts b/modules/angular2/src/di/injector.ts index b7a93f6113..285b9335a7 100644 --- a/modules/angular2/src/di/injector.ts +++ b/modules/angular2/src/di/injector.ts @@ -558,7 +558,7 @@ export class Injector { * @param `binding`: either a type or a binding. * @returns an object created using binding. */ - resolveAndInstantiate(binding: Type | Binding) { + resolveAndInstantiate(binding: Type | Binding): any { return this.instantiateResolved(Injector.resolve([binding])[0]); } diff --git a/modules/angular2/src/test_lib/spies.dart b/modules/angular2/src/test_lib/spies.dart index 0e0cee62fb..893fac9fc6 100644 --- a/modules/angular2/src/test_lib/spies.dart +++ b/modules/angular2/src/test_lib/spies.dart @@ -19,11 +19,6 @@ class SpyPipe extends SpyObject implements Pipe { noSuchMethod(m) => super.noSuchMethod(m); } -@proxy() -class SpyPipeFactory extends SpyObject implements PipeFactory { - noSuchMethod(m) => super.noSuchMethod(m); -} - @proxy() class SpyDependencyProvider extends SpyObject implements DependencyProvider { noSuchMethod(m) => super.noSuchMethod(m); @@ -39,3 +34,8 @@ class SpyIterableDifferFactory extends SpyObject implements IterableDifferFactory { noSuchMethod(m) => super.noSuchMethod(m); } + +@proxy +class SpyInjector extends SpyObject implements Injector { + noSuchMethod(m) => super.noSuchMethod(m); +} diff --git a/modules/angular2/src/test_lib/spies.ts b/modules/angular2/src/test_lib/spies.ts index 5526edd7c8..b3a2b432db 100644 --- a/modules/angular2/src/test_lib/spies.ts +++ b/modules/angular2/src/test_lib/spies.ts @@ -22,12 +22,12 @@ export class SpyPipe extends SpyObject { constructor() { super(BasePipe); } } -export class SpyPipeFactory extends SpyObject {} - export class SpyDependencyProvider extends SpyObject {} export class SpyIterableDifferFactory extends SpyObject {} +export class SpyInjector extends SpyObject {} + export class SpyChangeDetectorRef extends SpyObject { constructor() { super(ChangeDetectorRef); } } diff --git a/modules/angular2/src/test_lib/test_injector.ts b/modules/angular2/src/test_lib/test_injector.ts index 5f3e94f14e..e91cad942e 100644 --- a/modules/angular2/src/test_lib/test_injector.ts +++ b/modules/angular2/src/test_lib/test_injector.ts @@ -122,7 +122,7 @@ function _getAppBindings() { Compiler, CompilerCache, bind(ViewResolver).toClass(MockViewResolver), - bind(Pipes).toValue(defaultPipes), + defaultPipes, bind(IterableDiffers).toValue(defaultIterableDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(ChangeDetection).toClass(DynamicChangeDetection), 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 105fa555e3..789bd8906c 100644 --- a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart @@ -274,10 +274,7 @@ class _CodegenState { var read = ''' if ($_IDENTICAL_CHECK_FN($pipe, $_UTIL.uninitialized)) { - $pipe = ${_names.getPipesAccessorName()}.get('$pipeType', $context, $cdRef); - } else if (!$pipe.supports($context)) { - $pipe.onDestroy(); - $pipe = ${_names.getPipesAccessorName()}.get('$pipeType', $context, $cdRef); + $pipe = ${_names.getPipesAccessorName()}.get('$pipeType', $cdRef); } $newValue = $pipe.transform($context, [$argString]); '''; diff --git a/modules/angular2/src/web-workers/worker/application_common.ts b/modules/angular2/src/web-workers/worker/application_common.ts index 939cbd3780..59a1c5fbbe 100644 --- a/modules/angular2/src/web-workers/worker/application_common.ts +++ b/modules/angular2/src/web-workers/worker/application_common.ts @@ -118,7 +118,7 @@ function _injectorBindings(appComponentType, bus: WorkerMessageBus, Compiler, CompilerCache, ViewResolver, - bind(Pipes).toValue(defaultPipes), + defaultPipes, bind(ChangeDetection).toClass(bestChangeDetection), DirectiveResolver, Parser, diff --git a/modules/angular2/test/change_detection/change_detector_spec.ts b/modules/angular2/test/change_detection/change_detector_spec.ts index b918e213fa..26a7233864 100644 --- a/modules/angular2/test/change_detection/change_detector_spec.ts +++ b/modules/angular2/test/change_detection/change_detector_spec.ts @@ -768,7 +768,7 @@ export function main() { }); it('should destroy all active pipes during dehyration', () => { - var pipe = new OncePipe(); + var pipe = new PipeWithOnDestroy(); var registry = new FakePipes('pipe', () => pipe); var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector; @@ -810,36 +810,6 @@ export function main() { expect(val.dispatcher.log).toEqual(['propName=Megatron state:1']); }); - it('should lookup pipes in the registry when the context is not supported', () => { - var registry = new FakePipes('pipe', () => new OncePipe()); - var ctx = new Person('Megatron'); - - var cd = _createChangeDetector('name | pipe', ctx, registry).changeDetector; - - cd.detectChanges(); - - expect(registry.numberOfLookups).toEqual(1); - - ctx.name = 'Optimus Prime'; - cd.detectChanges(); - - expect(registry.numberOfLookups).toEqual(2); - }); - - it('should invoke onDestroy on a pipe before switching to another one', () => { - var pipe = new OncePipe(); - var registry = new FakePipes('pipe', () => pipe); - var ctx = new Person('Megatron'); - - var cd = _createChangeDetector('name | pipe', ctx, registry).changeDetector; - - cd.detectChanges(); - ctx.name = 'Optimus Prime'; - cd.detectChanges(); - - expect(pipe.destroyCalled).toEqual(true); - }); - it('should inject the ChangeDetectorRef ' + 'of the encompassing component into a pipe', () => { @@ -886,41 +856,24 @@ export function main() { class CountingPipe implements Pipe { state: number = 0; - onDestroy() {} - - supports(newValue) { return true; } - transform(value, args = null) { return `${value} state:${this.state ++}`; } } -class OncePipe implements Pipe { - called: boolean = false; +class PipeWithOnDestroy implements Pipe { destroyCalled: boolean = false; - - supports(newValue) { return !this.called; } - onDestroy() { this.destroyCalled = true; } - transform(value, args = null) { - this.called = true; - return value; - } + transform(value, args = null) { return null; } } class IdentityPipe implements Pipe { - supports(obj): boolean { return true; } - onDestroy() {} - transform(value, args = null) { return value; } } class WrappedPipe implements Pipe { - supports(obj): boolean { return true; } - onDestroy() {} - transform(value, args = null) { return WrappedValue.wrap(value); } } @@ -931,24 +884,16 @@ class MultiArgPipe implements Pipe { var arg3 = args.length > 2 ? args[2] : 'default'; return `${value} ${arg1} ${arg2} ${arg3}`; } - supports(obj): boolean { return true; } onDestroy(): void {} } class FakePipes extends Pipes { - numberOfLookups: number; - pipeType: string; - factory: Function; + numberOfLookups = 0; cdRef: any; - constructor(pipeType, factory) { - super({}); - this.pipeType = pipeType; - this.factory = factory; - this.numberOfLookups = 0; - } + constructor(public pipeType: string, public factory: Function) { super(null, null); } - get(type: string, obj, cdRef?, existingPipe?) { + get(type: string, cdRef?) { if (type != this.pipeType) return null; this.numberOfLookups++; this.cdRef = cdRef; diff --git a/modules/angular2/test/change_detection/pipes/json_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/json_pipe_spec.ts index 512f876ff9..b4e6bf4502 100644 --- a/modules/angular2/test/change_detection/pipes/json_pipe_spec.ts +++ b/modules/angular2/test/change_detection/pipes/json_pipe_spec.ts @@ -20,10 +20,8 @@ import {JsonPipe} from 'angular2/src/change_detection/pipes/json_pipe'; export function main() { describe("JsonPipe", () => { var regNewLine = '\n'; - var canHasUndefined; // because Dart doesn't like undefined; var inceptionObj; var inceptionObjString; - var catString; var pipe; var collection: number[]; @@ -35,27 +33,10 @@ export function main() { " \"dream\": \"Limbo\"\n" + " }\n" + " }\n" + "}"; - catString = 'Inception Cat'; pipe = new JsonPipe(); collection = []; }); - describe("supports", () => { - it("should support objects", () => { expect(pipe.supports(inceptionObj)).toBe(true); }); - - it("should support strings", () => { expect(pipe.supports(catString)).toBe(true); }); - - it("should support null", () => { expect(pipe.supports(null)).toBe(true); }); - - it("should support NaN", () => { expect(pipe.supports(NumberWrapper.NaN)).toBe(true); }); - - if (!IS_DARTIUM) { - it("should support undefined", - () => { expect(pipe.supports(canHasUndefined)).toBe(true); }); - } - - }); - describe("transform", () => { it("should return JSON-formatted string", () => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); }); diff --git a/modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts index 52637fa383..1680dcc958 100644 --- a/modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts +++ b/modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts @@ -4,29 +4,17 @@ import {LowerCasePipe} from 'angular2/src/change_detection/pipes/lowercase_pipe' export function main() { describe("LowerCasePipe", () => { - var str; var upper; var lower; var pipe; beforeEach(() => { - str = 'something'; lower = 'something'; upper = 'SOMETHING'; pipe = new LowerCasePipe(); }); - describe("supports", () => { - it("should support strings", () => { expect(pipe.supports(str)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports(new Object())).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - describe("transform", () => { - it("should return lowercase", () => { var val = pipe.transform(upper); expect(val).toEqual(lower); @@ -39,6 +27,8 @@ export function main() { expect(val2).toEqual('wat'); }); + it("should not support other objects", + () => { expect(() => pipe.transform(new Object())).toThrowError(); }); }); }); diff --git a/modules/angular2/test/change_detection/pipes/number_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/number_pipe_spec.ts index 08c246099f..92f11c5eab 100644 --- a/modules/angular2/test/change_detection/pipes/number_pipe_spec.ts +++ b/modules/angular2/test/change_detection/pipes/number_pipe_spec.ts @@ -12,18 +12,8 @@ export function main() { beforeEach(() => { pipe = new DecimalPipe(); }); - describe("supports", () => { - it("should support numbers", () => { expect(pipe.supports(123.0)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports(new Object())).toBe(false); - expect(pipe.supports('str')).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - describe("transform", () => { - it('should return correct value', () => { + it('should return correct value for numbers', () => { expect(pipe.transform(12345, [])).toEqual('12,345'); expect(pipe.transform(123, ['.2'])).toEqual('123.00'); expect(pipe.transform(1, ['3.'])).toEqual('001'); @@ -31,6 +21,9 @@ export function main() { expect(pipe.transform(1.123456, ['3.4-5'])).toEqual('001.12346'); expect(pipe.transform(1.1234, [])).toEqual('1.123'); }); + + it("should not support other objects", + () => { expect(() => pipe.transform(new Object(), [])).toThrowError(); }); }); }); @@ -39,21 +32,14 @@ export function main() { beforeEach(() => { pipe = new PercentPipe(); }); - describe("supports", () => { - it("should support numbers", () => { expect(pipe.supports(123.0)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports(new Object())).toBe(false); - expect(pipe.supports('str')).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - describe("transform", () => { - it('should return correct value', () => { + it('should return correct value for numbers', () => { expect(pipe.transform(1.23, [])).toEqual('123%'); expect(pipe.transform(1.2, ['.2'])).toEqual('120.00%'); }); + + it("should not support other objects", + () => { expect(() => pipe.transform(new Object(), [])).toThrowError(); }); }); }); @@ -62,21 +48,14 @@ export function main() { beforeEach(() => { pipe = new CurrencyPipe(); }); - describe("supports", () => { - it("should support numbers", () => { expect(pipe.supports(123.0)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports(new Object())).toBe(false); - expect(pipe.supports('str')).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - describe("transform", () => { - it('should return correct value', () => { + it('should return correct value for numbers', () => { expect(pipe.transform(123, [])).toEqual('USD123'); expect(pipe.transform(12, ['EUR', false, '.2'])).toEqual('EUR12.00'); }); + + it("should not support other objects", + () => { expect(() => pipe.transform(new Object(), [])).toThrowError(); }); }); }); } diff --git a/modules/angular2/test/change_detection/pipes/pipes_spec.ts b/modules/angular2/test/change_detection/pipes/pipes_spec.ts index 828b7d2a9c..9a4ba18e43 100644 --- a/modules/angular2/test/change_detection/pipes/pipes_spec.ts +++ b/modules/angular2/test/change_detection/pipes/pipes_spec.ts @@ -1,158 +1,88 @@ import { ddescribe, + xdescribe, describe, it, iit, xit, expect, beforeEach, - afterEach, - SpyPipe, - SpyPipeFactory + afterEach } from 'angular2/test_lib'; import {Injector, bind} from 'angular2/di'; import {Pipes} from 'angular2/src/change_detection/pipes/pipes'; -import {PipeFactory} from 'angular2/src/change_detection/pipes/pipe'; +import {Pipe} from 'angular2/src/change_detection/pipes/pipe'; + +class APipe implements Pipe { + transform(a, b) {} + onDestroy() {} +} + +class AnotherPipe implements Pipe { + transform(a, b) {} + onDestroy() {} +} export function main() { describe("pipe registry", () => { - var firstPipe; - var secondPipe; + var injector; - var firstPipeFactory; - var secondPipeFactory; + beforeEach(() => { injector = Injector.resolveAndCreate([]); }); - beforeEach(() => { - firstPipe = new SpyPipe(); - secondPipe = new SpyPipe(); - - firstPipeFactory = new SpyPipeFactory(); - secondPipeFactory = new SpyPipeFactory(); + it("should instantiate a pipe", () => { + var r = new Pipes({"type": APipe}, injector); + expect(r.get("type", null)).toBeAnInstanceOf(APipe); }); - it("should return an existing pipe if it can support the passed in object", () => { - var r = new Pipes({"type": []}); - - firstPipe.spy("supports").andReturn(true); - - expect(r.get("type", "some object", null, firstPipe)).toEqual(firstPipe); - }); - - it("should call onDestroy on the provided pipe if it cannot support the provided object", - () => { - firstPipe.spy("supports").andReturn(false); - firstPipeFactory.spy("supports").andReturn(true); - firstPipeFactory.spy("create").andReturn(secondPipe); - - var r = new Pipes({"type": [firstPipeFactory]}); - - expect(r.get("type", "some object", null, firstPipe)).toEqual(secondPipe); - expect(firstPipe.spy("onDestroy")).toHaveBeenCalled(); - }); - - it("should return the first pipe supporting the data type", () => { - firstPipeFactory.spy("supports").andReturn(false); - firstPipeFactory.spy("create").andReturn(firstPipe); - - secondPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("create").andReturn(secondPipe); - - var r = new Pipes({"type": [firstPipeFactory, secondPipeFactory]}); - - expect(r.get("type", "some object")).toBe(secondPipe); + it("should instantiate a new pipe every time", () => { + var r = new Pipes({"type": APipe}, injector); + var p1 = r.get("type", null); + var p2 = r.get("type", null); + expect(p1).not.toBe(p2); }); it("should throw when no matching type", () => { - var r = new Pipes({}); - expect(() => r.get("unknown", "some object")) - .toThrowError(`Cannot find 'unknown' pipe supporting object 'some object'`); - }); - - it("should throw when no matching pipe", () => { - var r = new Pipes({"type": []}); - - expect(() => r.get("type", "some object")) - .toThrowError(`Cannot find 'type' pipe supporting object 'some object'`); + var r = new Pipes({}, null); + expect(() => r.get("unknown", null)).toThrowError(`Cannot find pipe 'unknown'.`); }); describe('.create()', () => { it("should create a new Pipes object", () => { - firstPipeFactory.spy("supports").andReturn(true); - firstPipeFactory.spy("create").andReturn(firstPipe); - - var pipes = Pipes.create({'async': [firstPipeFactory]}); - expect(pipes.get("async", "first")).toBe(firstPipe); + var pipes = Pipes.create({'pipe': APipe}, null); + expect(pipes.config).toEqual({'pipe': APipe}); }); - it("should prepend passed it config in existing registry", () => { - firstPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("create").andReturn(secondPipe); + it("should merge pipes config", () => { + var pipes1 = Pipes.create({'pipe': APipe, 'pipe1': APipe}, null); + var pipes2 = Pipes.create({'pipe': AnotherPipe, 'pipe2': AnotherPipe}, null, pipes1); - var pipes1 = Pipes.create({'async': [firstPipeFactory]}); - var pipes2 = Pipes.create({'async': [secondPipeFactory]}, pipes1); - - expect(pipes2.get("async", "first")).toBe(secondPipe); + expect(pipes2.config).toEqual({'pipe': AnotherPipe, 'pipe1': APipe, 'pipe2': AnotherPipe}); }); - it("should use inherited pipes when no overrides support the provided object", () => { - firstPipeFactory.spy("supports").andReturn(true); - firstPipeFactory.spy("create").andReturn(firstPipe); - secondPipeFactory.spy("supports").andReturn(false); + it("should not change parent's config", () => { + var pipes1 = Pipes.create({'pipe': APipe, 'pipe1': APipe}, null); + Pipes.create({'pipe': AnotherPipe, 'pipe2': AnotherPipe}, null, pipes1); - var pipes1 = Pipes.create({'async': [firstPipeFactory], 'date': [firstPipeFactory]}); - var pipes2 = Pipes.create({'async': [secondPipeFactory]}, pipes1); - - expect(pipes2.get("async", "first")).toBe(firstPipe); - expect(pipes2.get("date", "first")).toBe(firstPipe); + expect(pipes1.config).toEqual({'pipe': APipe, 'pipe1': APipe}); }); }); describe(".extend()", () => { it('should create a factory that prepend new pipes to old', () => { - firstPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("create").andReturn(secondPipe); + var pipes1 = Pipes.create({'pipe': APipe, 'pipe1': APipe}, null); + var binding = Pipes.extend({'pipe': AnotherPipe, 'pipe2': AnotherPipe}); + var pipes: Pipes = binding.toFactory(pipes1, injector); - var originalPipes = new Pipes({'async': [firstPipeFactory]}); - var binding = Pipes.extend({'async':[secondPipeFactory]}); - var pipes: Pipes = binding.toFactory(originalPipes); - - expect(pipes.config['async'].length).toBe(2); - expect(originalPipes.config['async'].length).toBe(1); - expect(pipes.get('async', 'second plz')).toBe(secondPipe); + expect(pipes.config).toEqual({'pipe': AnotherPipe, 'pipe1': APipe, 'pipe2': AnotherPipe}); }); it('should throw if calling extend when creating root injector', () => { - secondPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("create").andReturn(secondPipe); - - var injector: Injector = - Injector.resolveAndCreate([Pipes.extend({'async': [secondPipeFactory]})]); + var injector = Injector.resolveAndCreate([Pipes.extend({'pipe': APipe})]); expect(() => injector.get(Pipes)) .toThrowErrorWith("Cannot extend Pipes without a parent injector"); }); - - it('should extend di-inherited pipes', () => { - firstPipeFactory.spy("supports").andReturn(true); - firstPipeFactory.spy("create").andReturn(firstPipe); - - secondPipeFactory.spy("supports").andReturn(false); - - var originalPipes: Pipes = new Pipes({'async': [firstPipeFactory]}); - var injector: Injector = Injector.resolveAndCreate([bind(Pipes).toValue(originalPipes)]); - var childInjector: Injector = - injector.resolveAndCreateChild([Pipes.extend({'async': [secondPipeFactory]})]); - - var parentPipes: Pipes = injector.get(Pipes); - var childPipes: Pipes = childInjector.get(Pipes); - - expect(childPipes.config['async'].length).toBe(2); - expect(parentPipes.config['async'].length).toBe(1); - expect(childPipes.get('async', 'second plz')).toBe(firstPipe); - }); }); }); } diff --git a/modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts index 813e39eb26..39b6f5710e 100644 --- a/modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts +++ b/modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts @@ -4,27 +4,16 @@ import {UpperCasePipe} from 'angular2/src/change_detection/pipes/uppercase_pipe' export function main() { describe("UpperCasePipe", () => { - var str; var upper; var lower; var pipe; beforeEach(() => { - str = 'something'; lower = 'something'; upper = 'SOMETHING'; pipe = new UpperCasePipe(); }); - describe("supports", () => { - it("should support strings", () => { expect(pipe.supports(str)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports(new Object())).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - describe("transform", () => { it("should return uppercase", () => { @@ -39,6 +28,8 @@ export function main() { expect(val2).toEqual('WAT'); }); + it("should not support other objects", + () => { expect(() => pipe.transform(new Object())).toThrowError(); }); }); }); diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts index 0919312f2a..db7e51f1d8 100644 --- a/modules/angular2/test/core/compiler/integration_spec.ts +++ b/modules/angular2/test/core/compiler/integration_spec.ts @@ -35,7 +35,13 @@ import { CONST, CONST_EXPR } from 'angular2/src/facade/lang'; -import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; +import { + PromiseWrapper, + EventEmitter, + ObservableWrapper, + PromiseCompleter, + Promise +} from 'angular2/src/facade/async'; import { Injector, @@ -50,7 +56,6 @@ import { SkipSelfMetadata } from 'angular2/di'; import { - PipeFactory, Pipes, defaultPipes, Pipe, @@ -673,6 +678,34 @@ export function main() { async.done(); })})); + + if (DOM.supportsDOMEvents()) { + it('should be checked when an async pipe requests a check', + inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => { + tcb = tcb.overrideView(MyComp, new viewAnn.View({ + template: '', + directives: [[[PushCmpWithAsyncPipe]]] + })); + + var rootTC; + tcb.createAsync(MyComp).then(root => { rootTC = root; }); + tick(); + + var cmp = rootTC.componentViewChildren[0].getLocal('cmp'); + rootTC.detectChanges(); + expect(cmp.numberOfChecks).toEqual(1); + + rootTC.detectChanges(); + rootTC.detectChanges(); + expect(cmp.numberOfChecks).toEqual(1); + + cmp.resolve(2); + tick(); + + rootTC.detectChanges(); + expect(cmp.numberOfChecks).toEqual(2); + }))); + } }); it('should create a component that injects an @Host', @@ -1607,9 +1640,30 @@ class PushCmpWithRef { propagate() { this.ref.requestCheck(); } } +@Component({selector: 'push-cmp-with-async', changeDetection: ON_PUSH}) +@View({template: '{{field | async}}'}) +@Injectable() +class PushCmpWithAsyncPipe { + numberOfChecks: number = 0; + promise: Promise; + completer: PromiseCompleter; + + constructor() { + this.completer = PromiseWrapper.completer(); + this.promise = this.completer.promise; + } + + get field() { + this.numberOfChecks++; + return this.promise; + } + + resolve(value) { this.completer.resolve(value); } +} + @Injectable() class PipesWithDouble extends Pipes { - constructor() { super({"double": [new DoublePipeFactory()]}); } + constructor(injector: Injector) { super({"double": DoublePipe}, injector); } } @Component({ @@ -1703,19 +1757,9 @@ class SomeViewport { @Injectable() class DoublePipe implements Pipe { onDestroy() {} - - supports(obj) { return true; } - transform(value, args = null) { return `${value}${value}`; } } -@Injectable() -class DoublePipeFactory implements PipeFactory { - supports(obj) { return true; } - - create(cdRef) { return new DoublePipe(); } -} - @Directive({selector: '[emitter]', events: ['event']}) @Injectable() class DirectiveEmitingEvent {