From 5b5d31fa9a8871cfdba468fb70bbda0cbb9ce4ff Mon Sep 17 00:00:00 2001 From: vsavkin Date: Fri, 7 Aug 2015 11:41:38 -0700 Subject: [PATCH] feat(pipe): added the Pipe decorator and the pipe property to View BREAKING CHANGE: Instead of configuring pipes via a Pipes object, now you can configure them by providing the pipes property to the View decorator. @Pipe({ name: 'double' }) class DoublePipe { transform(value, args) { return value * 2; } } @View({ template: '{{ 10 | double}}' pipes: [DoublePipe] }) class CustomComponent {} Closes #3572 --- modules/angular2/annotations.ts | 5 +- modules/angular2/change_detection.ts | 6 +- modules/angular2/pipes.ts | 15 ++-- .../abstract_change_detector.ts | 2 +- .../src/change_detection/change_detection.ts | 32 +------ .../change_detection_jit_generator.ts | 4 +- .../change_detection/change_detection_util.ts | 40 ++++++++- .../dynamic_change_detector.ts | 2 +- .../src/change_detection/pipe_transform.ts | 45 ++++++++++ .../angular2/src/change_detection/pipes.ts | 3 + .../src/change_detection/pipes/pipe.ts | 84 ----------------- .../src/change_detection/pipes/pipes.ts | 84 ----------------- .../pregen_proto_change_detector.dart | 2 +- .../src/core/annotations/annotations.ts | 1 + .../src/core/annotations/decorators.ts | 38 +++++++- .../src/core/annotations_impl/annotations.ts | 24 +++++ .../src/core/annotations_impl/view.ts | 6 +- .../angular2/src/core/application_common.ts | 7 +- .../angular2/src/core/compiler/compiler.ts | 65 +++++++------- .../src/core/compiler/element_injector.ts | 89 +++++++++---------- .../src/core/compiler/pipe_resolver.ts | 30 +++++++ .../src/core/compiler/proto_view_factory.ts | 13 ++- modules/angular2/src/core/compiler/view.ts | 4 +- .../src/core/compiler/view_manager_utils.ts | 13 +-- .../angular2/src/core/pipes/pipe_binding.ts | 15 ++++ modules/angular2/src/core/pipes/pipes.ts | 28 ++++++ .../pipes/async_pipe.ts | 11 ++- .../{change_detection => }/pipes/date_pipe.ts | 11 ++- modules/angular2/src/pipes/default_pipes.ts | 27 ++++++ .../pipes/invalid_pipe_argument_exception.ts | 7 ++ .../{change_detection => }/pipes/json_pipe.ts | 8 +- .../pipes/limit_to_pipe.ts | 9 +- .../pipes/lowercase_pipe.ts | 10 ++- .../pipes/number_pipe.ts | 14 ++- .../pipes/uppercase_pipe.ts | 9 +- modules/angular2/src/test_lib/spies.dart | 5 -- modules/angular2/src/test_lib/spies.ts | 5 -- .../angular2/src/test_lib/test_injector.ts | 7 +- .../change_detector_codegen.dart | 3 +- modules/angular2/src/util/decorators.ts | 1 + .../src/web-workers/ui/di_bindings.ts | 7 +- .../web-workers/worker/application_common.ts | 7 +- .../change_detection/change_detector_spec.ts | 38 +++----- .../test/change_detection/pipes/pipes_spec.ts | 88 ------------------ .../test/core/compiler/compiler_spec.ts | 49 +++++++--- .../core/compiler/element_injector_spec.ts | 11 ++- .../test/core/compiler/integration_spec.ts | 34 +++---- .../core/compiler/proto_view_factory_spec.ts | 2 +- .../core/compiler/view_manager_utils_spec.ts | 2 +- .../test/core/compiler/view_pool_spec.ts | 2 +- .../test/core/pipes/pipe_binding_spec.ts | 27 ++++++ .../angular2/test/core/pipes/pipes_spec.ts | 56 ++++++++++++ .../pipes/async_pipe_spec.ts | 4 +- .../pipes/date_pipe_spec.ts | 2 +- .../pipes/json_pipe_spec.ts | 2 +- .../pipes/limit_to_pipe_spec.ts | 2 +- .../pipes/lowercase_pipe_spec.ts | 2 +- .../pipes/number_pipe_spec.ts | 7 +- .../pipes/uppercase_pipe_spec.ts | 2 +- .../angular2/test/router/route_config_spec.ts | 13 ++- .../src/compiler/compiler_benchmark.ts | 8 +- tools/broccoli/trees/browser_tree.ts | 2 +- 62 files changed, 627 insertions(+), 524 deletions(-) create mode 100644 modules/angular2/src/change_detection/pipe_transform.ts create mode 100644 modules/angular2/src/change_detection/pipes.ts delete mode 100644 modules/angular2/src/change_detection/pipes/pipe.ts delete mode 100644 modules/angular2/src/change_detection/pipes/pipes.ts create mode 100644 modules/angular2/src/core/compiler/pipe_resolver.ts create mode 100644 modules/angular2/src/core/pipes/pipe_binding.ts create mode 100644 modules/angular2/src/core/pipes/pipes.ts rename modules/angular2/src/{change_detection => }/pipes/async_pipe.ts (90%) rename modules/angular2/src/{change_detection => }/pipes/date_pipe.ts (91%) create mode 100644 modules/angular2/src/pipes/default_pipes.ts create mode 100644 modules/angular2/src/pipes/invalid_pipe_argument_exception.ts rename modules/angular2/src/{change_detection => }/pipes/json_pipe.ts (72%) rename modules/angular2/src/{change_detection => }/pipes/limit_to_pipe.ts (87%) rename modules/angular2/src/{change_detection => }/pipes/lowercase_pipe.ts (69%) rename modules/angular2/src/{change_detection => }/pipes/number_pipe.ts (91%) rename modules/angular2/src/{change_detection => }/pipes/uppercase_pipe.ts (69%) delete mode 100644 modules/angular2/test/change_detection/pipes/pipes_spec.ts create mode 100644 modules/angular2/test/core/pipes/pipe_binding_spec.ts create mode 100644 modules/angular2/test/core/pipes/pipes_spec.ts rename modules/angular2/test/{change_detection => }/pipes/async_pipe_spec.ts (97%) rename modules/angular2/test/{change_detection => }/pipes/date_pipe_spec.ts (97%) rename modules/angular2/test/{change_detection => }/pipes/json_pipe_spec.ts (97%) rename modules/angular2/test/{change_detection => }/pipes/limit_to_pipe_spec.ts (95%) rename modules/angular2/test/{change_detection => }/pipes/lowercase_pipe_spec.ts (91%) rename modules/angular2/test/{change_detection => }/pipes/number_pipe_spec.ts (94%) rename modules/angular2/test/{change_detection => }/pipes/uppercase_pipe_spec.ts (91%) diff --git a/modules/angular2/annotations.ts b/modules/angular2/annotations.ts index efa3a48e90..bb17a972ad 100644 --- a/modules/angular2/annotations.ts +++ b/modules/angular2/annotations.ts @@ -12,6 +12,7 @@ export { ComponentAnnotation, DirectiveAnnotation, + PipeAnnotation, LifecycleEvent } from './src/core/annotations/annotations'; @@ -43,5 +44,7 @@ export { ViewFactory, Query, QueryFactory, - ViewQuery + ViewQuery, + Pipe, + PipeFactory } from 'angular2/src/core/annotations/decorators'; diff --git a/modules/angular2/change_detection.ts b/modules/angular2/change_detection.ts index 26c6819503..e3c0c349d6 100644 --- a/modules/angular2/change_detection.ts +++ b/modules/angular2/change_detection.ts @@ -20,14 +20,12 @@ export { ChangeDetectorRef, WrappedValue, - defaultPipes, - Pipe, - Pipes, + PipeTransform, IterableDiffers, IterableDiffer, IterableDifferFactory, KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory, - BasePipe + BasePipeTransform } from 'angular2/src/change_detection/change_detection'; diff --git a/modules/angular2/pipes.ts b/modules/angular2/pipes.ts index fbc2758ef7..5bf87b165b 100644 --- a/modules/angular2/pipes.ts +++ b/modules/angular2/pipes.ts @@ -4,10 +4,11 @@ * This module provides advanced support for extending change detection. */ -export {UpperCasePipe} from './src/change_detection/pipes/uppercase_pipe'; -export {LowerCasePipe} from './src/change_detection/pipes/lowercase_pipe'; -export {AsyncPipe} from './src/change_detection/pipes/async_pipe'; -export {JsonPipe} from './src/change_detection/pipes/json_pipe'; -export {DatePipe} from './src/change_detection/pipes/date_pipe'; -export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe'; -export {LimitToPipe} from './src/change_detection/pipes/limit_to_pipe'; +export {UpperCasePipe} from './src/pipes/uppercase_pipe'; +export {LowerCasePipe} from './src/pipes/lowercase_pipe'; +export {AsyncPipe} from './src/pipes/async_pipe'; +export {JsonPipe} from './src/pipes/json_pipe'; +export {DatePipe} from './src/pipes/date_pipe'; +export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/pipes/number_pipe'; +export {LimitToPipe} from './src/pipes/limit_to_pipe'; +export {DEFAULT_PIPES_TOKEN, DEFAULT_PIPES} from './src/pipes/default_pipes'; diff --git a/modules/angular2/src/change_detection/abstract_change_detector.ts b/modules/angular2/src/change_detection/abstract_change_detector.ts index 4fdf665cce..77e27586d0 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.ts +++ b/modules/angular2/src/change_detection/abstract_change_detector.ts @@ -4,6 +4,7 @@ import {ChangeDetectionUtil} from './change_detection_util'; import {ChangeDetectorRef} from './change_detector_ref'; import {DirectiveRecord} from './directive_record'; import {ChangeDetector, ChangeDispatcher} from './interfaces'; +import {Pipes} from './pipes'; import { ChangeDetectionError, ExpressionChangedAfterItHasBeenCheckedException, @@ -12,7 +13,6 @@ import { import {ProtoRecord} from './proto_record'; import {BindingRecord} from './binding_record'; import {Locals} from './parser/locals'; -import {Pipes} from './pipes/pipes'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants'; import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile'; diff --git a/modules/angular2/src/change_detection/change_detection.ts b/modules/angular2/src/change_detection/change_detection.ts index 561b1d582f..b2846ca573 100644 --- a/modules/angular2/src/change_detection/change_detection.ts +++ b/modules/angular2/src/change_detection/change_detection.ts @@ -1,18 +1,10 @@ import {JitProtoChangeDetector} from './jit_proto_change_detector'; import {PregenProtoChangeDetector} from './pregen_proto_change_detector'; import {DynamicProtoChangeDetector} from './proto_change_detector'; -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 {AsyncPipe} from './pipes/async_pipe'; -import {UpperCasePipe} from './pipes/uppercase_pipe'; -import {LowerCasePipe} from './pipes/lowercase_pipe'; -import {JsonPipe} from './pipes/json_pipe'; -import {LimitToPipe} from './pipes/limit_to_pipe'; -import {DatePipe} from './pipes/date_pipe'; -import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe'; import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces'; import {Injector, Inject, Injectable, OpaqueToken, Optional, Binding} from 'angular2/di'; import {List, StringMap, StringMapWrapper} from 'angular2/src/facade/collection'; @@ -50,30 +42,10 @@ export {BindingRecord} from './binding_record'; export {DirectiveIndex, DirectiveRecord} from './directive_record'; export {DynamicChangeDetector} from './dynamic_change_detector'; 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, 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]})); +export {PipeTransform, BasePipeTransform} from './pipe_transform'; +export {WrappedValue} from './change_detection_util'; /** * Structural diffing for `Object`s and `Map`s. 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 7b48951ecd..d2ce2b8d1e 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -179,12 +179,10 @@ export class ChangeDetectorJITGenerator { var newValue = this._names.getLocalName(r.selfIndex); var pipe = this._names.getPipeName(r.selfIndex); - var cdRef = "this.ref"; var pipeType = r.name; - var read = ` if (${pipe} === ${UTIL}.uninitialized) { - ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}', ${cdRef}); + ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}'); } ${newValue} = ${pipe}.transform(${context}, [${argString}]); `; diff --git a/modules/angular2/src/change_detection/change_detection_util.ts b/modules/angular2/src/change_detection/change_detection_util.ts index d924ecea9d..9b38195cf7 100644 --- a/modules/angular2/src/change_detection/change_detection_util.ts +++ b/modules/angular2/src/change_detection/change_detection_util.ts @@ -1,9 +1,47 @@ import {CONST_EXPR, isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ProtoRecord} from './proto_record'; -import {WrappedValue} from './pipes/pipe'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants'; + +/** + * Indicates that the result of a {@link Pipe} transformation has changed even though the reference + * has not changed. + * + * The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored. + * + * Example: + * + * ``` + * if (this._latestValue === this._latestReturnedValue) { + * return this._latestReturnedValue; + * } else { + * this._latestReturnedValue = this._latestValue; + * return WrappedValue.wrap(this._latestValue); // this will force update + * } + * ``` + */ +export class WrappedValue { + constructor(public wrapped: any) {} + + static wrap(value: any): WrappedValue { + var w = _wrappedValues[_wrappedIndex++ % 5]; + w.wrapped = value; + return w; + } +} + +var _wrappedValues = [ + new WrappedValue(null), + new WrappedValue(null), + new WrappedValue(null), + new WrappedValue(null), + new WrappedValue(null) +]; + +var _wrappedIndex = 0; + + export class SimpleChange { constructor(public previousValue: any, public currentValue: any) {} diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index 2d1818f98d..6aa0d14a9f 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -269,7 +269,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { var storedPipe = this._readPipe(proto); if (isPresent(storedPipe)) return storedPipe; - var pipe = this.pipes.get(proto.name, this.ref); + var pipe = this.pipes.get(proto.name); this._writePipe(proto, pipe); return pipe; } diff --git a/modules/angular2/src/change_detection/pipe_transform.ts b/modules/angular2/src/change_detection/pipe_transform.ts new file mode 100644 index 0000000000..6e88a1f1b6 --- /dev/null +++ b/modules/angular2/src/change_detection/pipe_transform.ts @@ -0,0 +1,45 @@ +import {ABSTRACT, BaseException, CONST, Type} from 'angular2/src/facade/lang'; + +/** + * An interface which all pipes must implement. + * + * #Example + * + * ``` + * class DoublePipe implements PipeTransform { + * onDestroy() {} + * + * transform(value, args = []) { + * return `${value}${value}`; + * } + * } + * ``` + */ +export interface PipeTransform { + onDestroy(): void; + + transform(value: any, args: List): any; +} + +/** + * Provides default implementation of the `onDestroy` method. + * + * #Example + * + * ``` + * class DoublePipe extends BasePipe { + * transform(value) { + * return `${value}${value}`; + * } + * } + * ``` + */ +@CONST() +export class BasePipeTransform implements PipeTransform { + onDestroy(): void {} + transform(value: any, args: List): any { return _abstract(); } +} + +function _abstract() { + throw new BaseException('This method is abstract'); +} diff --git a/modules/angular2/src/change_detection/pipes.ts b/modules/angular2/src/change_detection/pipes.ts new file mode 100644 index 0000000000..b9eb15ee0c --- /dev/null +++ b/modules/angular2/src/change_detection/pipes.ts @@ -0,0 +1,3 @@ +import {PipeTransform} from './pipe_transform'; + +export interface Pipes { get(name: string): PipeTransform; } \ No newline at end of file diff --git a/modules/angular2/src/change_detection/pipes/pipe.ts b/modules/angular2/src/change_detection/pipes/pipe.ts deleted file mode 100644 index de109ca4e3..0000000000 --- a/modules/angular2/src/change_detection/pipes/pipe.ts +++ /dev/null @@ -1,84 +0,0 @@ -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 - * has not changed. - * - * The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored. - */ -export class WrappedValue { - constructor(public wrapped: any) {} - - static wrap(value: any): WrappedValue { - var w = _wrappedValues[_wrappedIndex++ % 5]; - w.wrapped = value; - return w; - } -} - -var _wrappedValues = [ - new WrappedValue(null), - new WrappedValue(null), - new WrappedValue(null), - new WrappedValue(null), - new WrappedValue(null) -]; - -var _wrappedIndex = 0; - -/** - * An interface which all pipes must implement. - * - * #Example - * - * ``` - * class DoublePipe implements Pipe { - * supports(obj) { - * return true; - * } - * - * onDestroy() {} - * - * transform(value, args = []) { - * return `${value}${value}`; - * } - * } - * ``` - */ -export interface Pipe { - /** - * Query if a pipe supports a particular object instance. - */ - onDestroy(): void; - - transform(value: any, args: List): any; -} - -/** - * Provides default implementation of `supports` and `onDestroy` method. - * - * #Example - * - * ``` - * class DoublePipe extends BasePipe { - * transform(value) { - * return `${value}${value}`; - * } - * } - * ``` - */ -@CONST() -export class BasePipe implements Pipe { - onDestroy(): void {} - transform(value: any, args: List): any { return _abstract(); } -} - -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 deleted file mode 100644 index 110c2f8089..0000000000 --- a/modules/angular2/src/change_detection/pipes/pipes.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {ListWrapper, isListLikeIterable, StringMapWrapper} from 'angular2/src/facade/collection'; -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'; - -@Injectable() -@CONST() -export class Pipes { - /** - * Map of {@link Pipe} names to {@link Pipe} implementations. - * - * #Example - * - * ``` - * var pipesConfig = { - * 'json': JsonPipe - * } - * @Component({ - * viewBindings: [ - * bind(Pipes).toFactory(inj => new Pipes(pipesConfig, in), [Injector]) - * ] - * }) - * ``` - */ - config: StringMap; - - constructor(config: StringMap, public injector: Injector) { - this.config = config; - } - - 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); - } - - /** - * Takes a {@link Pipes} config object and returns a binding used to extend the - * inherited {@link Pipes} instance with the provided config and return a new - * {@link Pipes} instance. - * - * The provided config is merged with the {@link Pipes} instance. - * - * # Example - * - * ``` - * @Component({ - * viewBindings: [ - * Pipes.extend({ - * 'bithdayFormat': BirthdayFormat - * }) - * ] - * }) - * ``` - */ - static extend(config: StringMap): Binding { - return new Binding(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, injector, pipes); - }, - // Dependency technically isn't optional, but we can provide a better error message this way. - deps: [[Pipes, new SkipSelfMetadata(), new OptionalMetadata()], Injector] - }); - } - - static create(config: StringMap, injector: Injector, - pipes: Pipes = null): Pipes { - if (isPresent(pipes)) { - return new Pipes(StringMapWrapper.merge(pipes.config, config), injector); - } else { - return new Pipes(config, injector); - } - } -} diff --git a/modules/angular2/src/change_detection/pregen_proto_change_detector.dart b/modules/angular2/src/change_detection/pregen_proto_change_detector.dart index 14469b80e8..bdb9a7546d 100644 --- a/modules/angular2/src/change_detection/pregen_proto_change_detector.dart +++ b/modules/angular2/src/change_detection/pregen_proto_change_detector.dart @@ -16,7 +16,7 @@ export 'package:angular2/src/change_detection/directive_record.dart' show DirectiveIndex, DirectiveRecord; export 'package:angular2/src/change_detection/interfaces.dart' show ChangeDetector, ChangeDetectorDefinition, ProtoChangeDetector; -export 'package:angular2/src/change_detection/pipes/pipes.dart' show Pipes; +export 'package:angular2/src/change_detection/pipes.dart' show Pipes; export 'package:angular2/src/change_detection/proto_record.dart' show ProtoRecord; export 'package:angular2/src/change_detection/change_detection_util.dart' diff --git a/modules/angular2/src/core/annotations/annotations.ts b/modules/angular2/src/core/annotations/annotations.ts index 97833ac6d0..8e11e0eb54 100644 --- a/modules/angular2/src/core/annotations/annotations.ts +++ b/modules/angular2/src/core/annotations/annotations.ts @@ -6,5 +6,6 @@ export { Component as ComponentAnnotation, Directive as DirectiveAnnotation, + Pipe as PipeAnnotation, LifecycleEvent } from '../annotations_impl/annotations'; diff --git a/modules/angular2/src/core/annotations/decorators.ts b/modules/angular2/src/core/annotations/decorators.ts index ccc294d613..c1c40a89dc 100644 --- a/modules/angular2/src/core/annotations/decorators.ts +++ b/modules/angular2/src/core/annotations/decorators.ts @@ -1,4 +1,9 @@ -import {ComponentAnnotation, DirectiveAnnotation, LifecycleEvent} from './annotations'; +import { + ComponentAnnotation, + DirectiveAnnotation, + PipeAnnotation, + LifecycleEvent +} from './annotations'; import {ViewAnnotation} from './view'; import {AttributeAnnotation, QueryAnnotation, ViewQueryAnnotation} from './di'; import {makeDecorator, makeParamDecorator, TypeDecorator, Class} from '../../util/decorators'; @@ -25,6 +30,7 @@ export interface ComponentDecorator extends TypeDecorator { templateUrl?: string, template?: string, directives?: List>, + pipes?: List>, renderer?: string, styles?: List, styleUrls?: List, @@ -44,6 +50,7 @@ export interface ViewDecorator extends TypeDecorator { templateUrl?: string, template?: string, directives?: List>, + pipes?: List>, renderer?: string, styles?: List, styleUrls?: List, @@ -337,6 +344,30 @@ export interface QueryFactory { new (selector: Type | string, {descendants}?: {descendants?: boolean}): QueryAnnotation; } +/** + * {@link Pipe} factory for creating decorators. + * + * ## Example as TypeScript Decorator + * + * ``` + * import {Pipe} from "angular2/angular2"; + * + * @Pipe({...}) + * class MyPipe { + * constructor() { + * ... + * } + * + * transform(v, args) {} + * } + * ``` + */ +export interface PipeFactory { + (obj: {name: string}): any; + new (obj: { + name: string, + }): any; +} /** * {@link Component} factory function. @@ -369,3 +400,8 @@ export var Query: QueryFactory = makeParamDecorator(QueryAnnotation); * {@link ViewQuery} factory function. */ export var ViewQuery: QueryFactory = makeParamDecorator(ViewQueryAnnotation); + +/** + * {@link Pipe} factory function. + */ +export var Pipe: PipeFactory = makeDecorator(PipeAnnotation); diff --git a/modules/angular2/src/core/annotations_impl/annotations.ts b/modules/angular2/src/core/annotations_impl/annotations.ts index 2720a0c776..111624f7d3 100644 --- a/modules/angular2/src/core/annotations_impl/annotations.ts +++ b/modules/angular2/src/core/annotations_impl/annotations.ts @@ -1030,3 +1030,27 @@ export enum LifecycleEvent { */ onAllChangesDone } + +/** + * Declare reusable pipe function. + * + * ## Example + * + * ``` + * @Pipe({ + * name: 'lowercase' + * }) + * class Lowercase { + * transform(v, args) { return v.toLowerCase(); } + * } + * ``` + */ +@CONST() +export class Pipe extends InjectableMetadata { + name: string; + + constructor({name}: {name: string}) { + super(); + this.name = name; + } +} diff --git a/modules/angular2/src/core/annotations_impl/view.ts b/modules/angular2/src/core/annotations_impl/view.ts index cbaf1cd9eb..eb83dfd0ea 100644 --- a/modules/angular2/src/core/annotations_impl/view.ts +++ b/modules/angular2/src/core/annotations_impl/view.ts @@ -87,6 +87,8 @@ export class View { // for an unused import. directives: List>; + pipes: List>; + /** * Specify how the template and the styles should be encapsulated. * The default is {@link ViewEncapsulation#EMULATED `ViewEncapsulation.EMULATED`} if the view @@ -95,10 +97,11 @@ export class View { */ encapsulation: ViewEncapsulation; - constructor({templateUrl, template, directives, encapsulation, styles, styleUrls}: { + constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: { templateUrl?: string, template?: string, directives?: List>, + pipes?: List>, encapsulation?: ViewEncapsulation, styles?: List, styleUrls?: List, @@ -108,6 +111,7 @@ export class View { this.styleUrls = styleUrls; this.styles = styles; this.directives = directives; + this.pipes = pipes; this.encapsulation = encapsulation; } } diff --git a/modules/angular2/src/core/application_common.ts b/modules/angular2/src/core/application_common.ts index 163545f64c..13c9b441f5 100644 --- a/modules/angular2/src/core/application_common.ts +++ b/modules/angular2/src/core/application_common.ts @@ -21,19 +21,19 @@ import { DynamicChangeDetection, JitChangeDetection, PreGeneratedChangeDetection, - Pipes, - defaultPipes, IterableDiffers, defaultIterableDiffers, KeyValueDiffers, defaultKeyValueDiffers } from 'angular2/src/change_detection/change_detection'; +import {DEFAULT_PIPES} from 'angular2/pipes'; import {ExceptionHandler} from './exception_handler'; import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader'; import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver'; import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner'; import {ViewResolver} from './compiler/view_resolver'; import {DirectiveResolver} from './compiler/directive_resolver'; +import {PipeResolver} from './compiler/pipe_resolver'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/facade/async'; import {NgZone} from 'angular2/src/core/zone/ng_zone'; @@ -137,12 +137,13 @@ function _injectorBindings(appComponentType): List> { Compiler, CompilerCache, ViewResolver, - defaultPipes, + DEFAULT_PIPES, bind(IterableDiffers).toValue(defaultIterableDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(ChangeDetection).toClass(bestChangeDetection), ViewLoader, DirectiveResolver, + PipeResolver, Parser, Lexer, bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM, isDart ? false : true), []), diff --git a/modules/angular2/src/core/compiler/compiler.ts b/modules/angular2/src/core/compiler/compiler.ts index ef5b0d6074..45e13164e6 100644 --- a/modules/angular2/src/core/compiler/compiler.ts +++ b/modules/angular2/src/core/compiler/compiler.ts @@ -1,4 +1,4 @@ -import {Binding, resolveForwardRef, Injectable} from 'angular2/di'; +import {Binding, resolveForwardRef, Injectable, Inject} from 'angular2/di'; import { Type, isBlank, @@ -19,6 +19,7 @@ import {AppProtoView, AppProtoViewMergeMapping} from './view'; import {ProtoViewRef} from './view_ref'; import {DirectiveBinding} from './element_injector'; import {ViewResolver} from './view_resolver'; +import {PipeResolver} from './pipe_resolver'; import {View} from '../annotations_impl/view'; import {ComponentUrlMapper} from './component_url_mapper'; import {ProtoViewFactory} from './proto_view_factory'; @@ -26,6 +27,8 @@ import {UrlResolver} from 'angular2/src/services/url_resolver'; import {AppRootUrl} from 'angular2/src/services/app_root_url'; import {ElementBinder} from './element_binder'; import {wtfStartTimeRange, wtfEndTimeRange} from '../../profile/profile'; +import {PipeBinding} from '../pipes/pipe_binding'; +import {DEFAULT_PIPES_TOKEN} from 'angular2/pipes'; import * as renderApi from 'angular2/src/render/api'; @@ -83,46 +86,40 @@ export class CompilerCache { */ @Injectable() export class Compiler { - private _reader: DirectiveResolver; - private _compilerCache: CompilerCache; - private _compiling: Map>; - private _viewResolver: ViewResolver; - private _componentUrlMapper: ComponentUrlMapper; - private _urlResolver: UrlResolver; + private _compiling: Map> = new Map(); private _appUrl: string; - private _render: renderApi.RenderCompiler; - private _protoViewFactory: ProtoViewFactory; + private _defaultPipes: Type[]; /** * @private */ - constructor(reader: DirectiveResolver, cache: CompilerCache, viewResolver: ViewResolver, - componentUrlMapper: ComponentUrlMapper, urlResolver: UrlResolver, - render: renderApi.RenderCompiler, protoViewFactory: ProtoViewFactory, - appUrl: AppRootUrl) { - this._reader = reader; - this._compilerCache = cache; - this._compiling = new Map(); - this._viewResolver = viewResolver; - this._componentUrlMapper = componentUrlMapper; - this._urlResolver = urlResolver; + constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver, + @Inject(DEFAULT_PIPES_TOKEN) _defaultPipes: Type[], + private _compilerCache: CompilerCache, private _viewResolver: ViewResolver, + private _componentUrlMapper: ComponentUrlMapper, private _urlResolver: UrlResolver, + private _render: renderApi.RenderCompiler, + private _protoViewFactory: ProtoViewFactory, appUrl: AppRootUrl) { + this._defaultPipes = _defaultPipes; this._appUrl = appUrl.value; - this._render = render; - this._protoViewFactory = protoViewFactory; } private _bindDirective(directiveTypeOrBinding): DirectiveBinding { if (directiveTypeOrBinding instanceof DirectiveBinding) { return directiveTypeOrBinding; } else if (directiveTypeOrBinding instanceof Binding) { - let annotation = this._reader.resolve(directiveTypeOrBinding.token); + let annotation = this._directiveResolver.resolve(directiveTypeOrBinding.token); return DirectiveBinding.createFromBinding(directiveTypeOrBinding, annotation); } else { - let annotation = this._reader.resolve(directiveTypeOrBinding); + let annotation = this._directiveResolver.resolve(directiveTypeOrBinding); return DirectiveBinding.createFromType(directiveTypeOrBinding, annotation); } } + private _bindPipe(typeOrBinding): PipeBinding { + let meta = this._pipeResolver.resolve(typeOrBinding); + return PipeBinding.createFromType(typeOrBinding, meta); + } + // Create a hostView as if the compiler encountered . // Used for bootstrapping. compileInHost(componentTypeOrBinding: Type | Binding): Promise { @@ -143,7 +140,7 @@ export class Compiler { this._render.compileHost(directiveMetadata) .then((hostRenderPv) => { var protoViews = this._protoViewFactory.createAppProtoViews( - componentBinding, hostRenderPv, [componentBinding]); + componentBinding, hostRenderPv, [componentBinding], []); return this._compileNestedProtoViews(protoViews, componentType, new Map()); }) .then((appProtoView) => { @@ -186,14 +183,17 @@ export class Compiler { } var boundDirectives = this._removeDuplicatedDirectives( - ListWrapper.map(directives, (directive) => this._bindDirective(directive))); + directives.map(directive => this._bindDirective(directive))); + + var pipes = this._flattenPipes(view); + var boundPipes = pipes.map(pipe => this._bindPipe(pipe)); var renderTemplate = this._buildRenderTemplate(component, view, boundDirectives); resultPromise = this._render.compile(renderTemplate) .then((renderPv) => { var protoViews = this._protoViewFactory.createAppProtoViews( - componentBinding, renderPv, boundDirectives); + componentBinding, renderPv, boundDirectives, boundPipes); return this._compileNestedProtoViews(protoViews, component, componentPath); }) .then((appProtoView) => { @@ -317,12 +317,17 @@ export class Compiler { }); } - private _flattenDirectives(template: View): List { - if (isBlank(template.directives)) return []; + private _flattenPipes(view: View): any[] { + if (isBlank(view.pipes)) return this._defaultPipes; + var pipes = ListWrapper.clone(this._defaultPipes); + this._flattenList(view.pipes, pipes); + return pipes; + } + private _flattenDirectives(view: View): List { + if (isBlank(view.directives)) return []; var directives = []; - this._flattenList(template.directives, directives); - + this._flattenList(view.directives, directives); return directives; } diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts index adde9c88b1..b01a3db79f 100644 --- a/modules/angular2/src/core/compiler/element_injector.ts +++ b/modules/angular2/src/core/compiler/element_injector.ts @@ -42,14 +42,11 @@ import {ElementRef} from './element_ref'; import {TemplateRef} from './template_ref'; import {Directive, Component, LifecycleEvent} from 'angular2/src/core/annotations_impl/annotations'; import {hasLifecycleHook} from './directive_lifecycle_reflector'; -import { - ChangeDetector, - ChangeDetectorRef, - Pipes -} from 'angular2/src/change_detection/change_detection'; +import {ChangeDetector, ChangeDetectorRef} from 'angular2/src/change_detection/change_detection'; import {QueryList} from './query_list'; import {reflector} from 'angular2/src/reflection/reflection'; import {DirectiveMetadata} from 'angular2/src/render/api'; +import {PipeBinding} from '../pipes/pipe_binding'; var _staticKeys; @@ -59,7 +56,6 @@ export class StaticKeys { viewContainerId: number; changeDetectorRefId: number; elementRefId: number; - pipesKey: Key; constructor() { this.viewManagerId = Key.get(avmModule.AppViewManager).id; @@ -67,8 +63,6 @@ export class StaticKeys { this.viewContainerId = Key.get(ViewContainerRef).id; this.changeDetectorRefId = Key.get(ChangeDetectorRef).id; this.elementRefId = Key.get(ElementRef).id; - // not an id because the public API of injector works only with keys and tokens - this.pipesKey = Key.get(Pipes); } static instance(): StaticKeys { @@ -541,11 +535,6 @@ export class ElementInjector extends TreeNode implements Depend injector.internalStrategy.attach(parentInjector, isBoundary); } - getPipes(): Pipes { - var pipesKey = StaticKeys.instance().pipesKey; - return this._injector.getOptional(pipesKey); - } - hasVariableBinding(name: string): boolean { var vb = this._proto.directiveVariableBindings; return isPresent(vb) && vb.has(name); @@ -589,51 +578,57 @@ export class ElementInjector extends TreeNode implements Depend getDependency(injector: Injector, binding: ResolvedBinding, dep: Dependency): any { var key: Key = dep.key; - if (!(dep instanceof DirectiveDependency)) return undefinedValue; - if (!(binding instanceof DirectiveBinding)) return undefinedValue; - - var dirDep = dep; - var dirBin = binding; - var staticKeys = StaticKeys.instance(); + if (binding instanceof DirectiveBinding) { + var dirDep = dep; + var dirBin = binding; + var staticKeys = StaticKeys.instance(); - if (key.id === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager; + if (key.id === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager; - if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep); + if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep); - if (isPresent(dirDep.queryDecorator)) return this._findQuery(dirDep.queryDecorator).list; + if (isPresent(dirDep.queryDecorator)) return this._findQuery(dirDep.queryDecorator).list; - if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) { - // We provide the component's view change detector to components and - // the surrounding component's change detector to directives. - if (dirBin.metadata.type === DirectiveMetadata.COMPONENT_TYPE) { + if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) { + // We provide the component's view change detector to components and + // the surrounding component's change detector to directives. + if (dirBin.metadata.type === DirectiveMetadata.COMPONENT_TYPE) { + var componentView = this._preBuiltObjects.view.getNestedView( + this._preBuiltObjects.elementRef.boundElementIndex); + return componentView.changeDetector.ref; + } else { + return this._preBuiltObjects.view.changeDetector.ref; + } + } + + if (dirDep.key.id === StaticKeys.instance().elementRefId) { + return this.getElementRef(); + } + + if (dirDep.key.id === StaticKeys.instance().viewContainerId) { + return this.getViewContainerRef(); + } + + if (dirDep.key.id === StaticKeys.instance().templateRefId) { + if (isBlank(this._preBuiltObjects.templateRef)) { + if (dirDep.optional) { + return null; + } + + throw new NoBindingError(null, dirDep.key); + } + return this._preBuiltObjects.templateRef; + } + + } else if (binding instanceof PipeBinding) { + if (dep.key.id === StaticKeys.instance().changeDetectorRefId) { var componentView = this._preBuiltObjects.view.getNestedView( this._preBuiltObjects.elementRef.boundElementIndex); return componentView.changeDetector.ref; - } else { - return this._preBuiltObjects.view.changeDetector.ref; } } - if (dirDep.key.id === StaticKeys.instance().elementRefId) { - return this.getElementRef(); - } - - if (dirDep.key.id === StaticKeys.instance().viewContainerId) { - return this.getViewContainerRef(); - } - - if (dirDep.key.id === StaticKeys.instance().templateRefId) { - if (isBlank(this._preBuiltObjects.templateRef)) { - if (dirDep.optional) { - return null; - } - - throw new NoBindingError(null, dirDep.key); - } - return this._preBuiltObjects.templateRef; - } - return undefinedValue; } diff --git a/modules/angular2/src/core/compiler/pipe_resolver.ts b/modules/angular2/src/core/compiler/pipe_resolver.ts new file mode 100644 index 0000000000..a18923bb64 --- /dev/null +++ b/modules/angular2/src/core/compiler/pipe_resolver.ts @@ -0,0 +1,30 @@ +import {resolveForwardRef, Injectable} from 'angular2/di'; +import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang'; +import {Pipe} from '../annotations_impl/annotations'; +import {reflector} from 'angular2/src/reflection/reflection'; + +/** + * Resolve a `Type` for {@link Pipe}. + * + * This interface can be overridden by the application developer to create custom behavior. + * + * See {@link Compiler} + */ +@Injectable() +export class PipeResolver { + /** + * Return {@link Pipe} for a given `Type`. + */ + resolve(type: Type): Pipe { + var metas = reflector.annotations(resolveForwardRef(type)); + if (isPresent(metas)) { + for (var i = 0; i < metas.length; i++) { + var annotation = metas[i]; + if (annotation instanceof Pipe) { + return annotation; + } + } + } + throw new BaseException(`No Pipe decorator found on ${stringify(type)}`); + } +} diff --git a/modules/angular2/src/core/compiler/proto_view_factory.ts b/modules/angular2/src/core/compiler/proto_view_factory.ts index 2f1bb11f9b..56b912ac06 100644 --- a/modules/angular2/src/core/compiler/proto_view_factory.ts +++ b/modules/angular2/src/core/compiler/proto_view_factory.ts @@ -15,6 +15,9 @@ import { ASTWithSource } from 'angular2/src/change_detection/change_detection'; +import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding'; +import {ProtoPipes} from 'angular2/src/core/pipes/pipes'; + import * as renderApi from 'angular2/src/render/api'; import {AppProtoView} from './view'; import {ElementBinder} from './element_binder'; @@ -160,7 +163,7 @@ export class ProtoViewFactory { createAppProtoViews(hostComponentBinding: DirectiveBinding, rootRenderProtoView: renderApi.ProtoViewDto, - allDirectives: List): AppProtoView[] { + allDirectives: List, pipes: PipeBinding[]): AppProtoView[] { var allRenderDirectiveMetadata = ListWrapper.map(allDirectives, directiveBinding => directiveBinding.metadata); var nestedPvsWithIndex = _collectNestedProtoViews(rootRenderProtoView); @@ -177,7 +180,7 @@ export class ProtoViewFactory { ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex: RenderProtoViewWithIndex) => { var appProtoView = _createAppProtoView(pvWithIndex.renderProtoView, protoChangeDetectors[pvWithIndex.index], - nestedPvVariableBindings[pvWithIndex.index], allDirectives); + nestedPvVariableBindings[pvWithIndex.index], allDirectives, pipes); if (isPresent(pvWithIndex.parentIndex)) { var parentView = appProtoViews[pvWithIndex.parentIndex]; parentView.elementBinders[pvWithIndex.boundElementIndex].nestedProtoView = appProtoView; @@ -252,14 +255,16 @@ function _getChangeDetectorDefinitions( function _createAppProtoView( renderProtoView: renderApi.ProtoViewDto, protoChangeDetector: ProtoChangeDetector, - variableBindings: Map, allDirectives: List): AppProtoView { + variableBindings: Map, allDirectives: List, + pipes: PipeBinding[]): AppProtoView { var elementBinders = renderProtoView.elementBinders; // Embedded ProtoViews that contain `` will be merged into their parents and use // a RenderFragmentRef. I.e. renderProtoView.transitiveNgContentCount > 0. + var protoPipes = new ProtoPipes(pipes); var protoView = new AppProtoView( renderProtoView.type, renderProtoView.transitiveNgContentCount > 0, renderProtoView.render, protoChangeDetector, variableBindings, createVariableLocations(elementBinders), - renderProtoView.textBindings.length); + renderProtoView.textBindings.length, protoPipes); _createElementBinders(protoView, elementBinders, allDirectives); _bindDirectiveEvents(protoView, elementBinders); diff --git a/modules/angular2/src/core/compiler/view.ts b/modules/angular2/src/core/compiler/view.ts index 7cfadfcd6e..b988029b08 100644 --- a/modules/angular2/src/core/compiler/view.ts +++ b/modules/angular2/src/core/compiler/view.ts @@ -31,6 +31,7 @@ import * as renderApi from 'angular2/src/render/api'; import {RenderEventDispatcher} from 'angular2/src/render/api'; import {ViewRef, ProtoViewRef, internalView} from './view_ref'; import {ElementRef} from './element_ref'; +import {ProtoPipes} from 'angular2/src/core/pipes/pipes'; export {DebugContext} from 'angular2/src/change_detection/interfaces'; @@ -335,7 +336,8 @@ export class AppProtoView { public render: renderApi.RenderProtoViewRef, public protoChangeDetector: ProtoChangeDetector, public variableBindings: Map, - public variableLocations: Map, public textBindingCount: number) { + public variableLocations: Map, public textBindingCount: number, + public pipes: ProtoPipes) { this.ref = new ProtoViewRef(this); if (isPresent(variableBindings)) { MapWrapper.forEach(variableBindings, diff --git a/modules/angular2/src/core/compiler/view_manager_utils.ts b/modules/angular2/src/core/compiler/view_manager_utils.ts index a88c3f0331..a4ae21fe63 100644 --- a/modules/angular2/src/core/compiler/view_manager_utils.ts +++ b/modules/angular2/src/core/compiler/view_manager_utils.ts @@ -9,6 +9,7 @@ import {ElementRef} from './element_ref'; import {TemplateRef} from './template_ref'; import {Renderer, RenderViewWithFragments} from 'angular2/src/render/api'; import {Locals} from 'angular2/src/change_detection/change_detection'; +import {Pipes} from 'angular2/src/core/pipes/pipes'; import {RenderViewRef, RenderFragmentRef, ViewType} from 'angular2/src/render/api'; @Injectable() @@ -206,21 +207,15 @@ export class AppViewManagerUtils { this._setUpHostActions(currView, elementInjector, boundElementIndex); } } - var pipes = this._getPipes(imperativelyCreatedInjector, hostElementInjector); + var pipes = isPresent(hostElementInjector) ? + new Pipes(currView.proto.pipes, hostElementInjector.getInjector()) : + null; currView.changeDetector.hydrate(currView.context, currView.locals, currView, pipes); viewIdx++; } } } - _getPipes(imperativelyCreatedInjector: Injector, hostElementInjector: eli.ElementInjector) { - var pipesKey = eli.StaticKeys.instance().pipesKey; - if (isPresent(imperativelyCreatedInjector)) - return imperativelyCreatedInjector.getOptional(pipesKey); - if (isPresent(hostElementInjector)) return hostElementInjector.getPipes(); - return null; - } - _populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector, boundElementIdx: number): void { if (isPresent(elementInjector.getDirectiveVariableBindings())) { diff --git a/modules/angular2/src/core/pipes/pipe_binding.ts b/modules/angular2/src/core/pipes/pipe_binding.ts new file mode 100644 index 0000000000..bd6a1643fb --- /dev/null +++ b/modules/angular2/src/core/pipes/pipe_binding.ts @@ -0,0 +1,15 @@ +import {Type} from 'angular2/src/facade/lang'; +import {Key, Dependency, ResolvedBinding, Binding} from 'angular2/di'; +import {Pipe} from 'angular2/src/core/annotations_impl/annotations'; + +export class PipeBinding extends ResolvedBinding { + constructor(public name: string, key: Key, factory: Function, dependencies: Dependency[]) { + super(key, factory, dependencies); + } + + static createFromType(type: Type, metadata: Pipe): PipeBinding { + var binding = new Binding(type, {toClass: type}); + var rb = binding.resolve(); + return new PipeBinding(metadata.name, rb.key, rb.factory, rb.dependencies); + } +} \ No newline at end of file diff --git a/modules/angular2/src/core/pipes/pipes.ts b/modules/angular2/src/core/pipes/pipes.ts new file mode 100644 index 0000000000..31a6395209 --- /dev/null +++ b/modules/angular2/src/core/pipes/pipes.ts @@ -0,0 +1,28 @@ +import {isBlank, isPresent, BaseException, CONST, Type} from 'angular2/src/facade/lang'; +import {Injectable, OptionalMetadata, SkipSelfMetadata, Binding, Injector, bind} from 'angular2/di'; +import {PipeBinding} from './pipe_binding'; +import * as cd from 'angular2/src/change_detection/pipes'; + +export class ProtoPipes { + /** + * Map of {@link Pipe} names to {@link Pipe} implementations. + */ + config: StringMap = {}; + + constructor(bindings: PipeBinding[]) { bindings.forEach(b => this.config[b.name] = b); } + + get(name: string): PipeBinding { + var binding = this.config[name]; + if (isBlank(binding)) throw new BaseException(`Cannot find pipe '${name}'.`); + return binding; + } +} + +export class Pipes implements cd.Pipes { + constructor(public proto: ProtoPipes, public injector: Injector) {} + + get(name: string): any { + var b = this.proto.get(name); + return this.injector.instantiateResolved(b); + } +} diff --git a/modules/angular2/src/change_detection/pipes/async_pipe.ts b/modules/angular2/src/pipes/async_pipe.ts similarity index 90% rename from modules/angular2/src/change_detection/pipes/async_pipe.ts rename to modules/angular2/src/pipes/async_pipe.ts index ce8e483728..58d43a2d12 100644 --- a/modules/angular2/src/change_detection/pipes/async_pipe.ts +++ b/modules/angular2/src/pipes/async_pipe.ts @@ -1,8 +1,12 @@ import {isBlank, isPresent, isPromise, CONST, BaseException} from 'angular2/src/facade/lang'; import {Observable, Promise, ObservableWrapper} from 'angular2/src/facade/async'; import {Injectable} from 'angular2/di'; -import {Pipe, WrappedValue, InvalidPipeArgumentException} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; + +import {PipeTransform, WrappedValue} from 'angular2/change_detection'; +import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception'; +import {ChangeDetectorRef} from 'angular2/change_detection'; + +import {Pipe} from 'angular2/src/core/annotations/decorators'; class ObservableStrategy { @@ -53,8 +57,9 @@ var _observableStrategy = new ObservableStrategy(); * * ``` */ +@Pipe({name: 'async'}) @Injectable() -export class AsyncPipe implements Pipe { +export class AsyncPipe implements PipeTransform { _latestValue: Object = null; _latestReturnedValue: Object = null; diff --git a/modules/angular2/src/change_detection/pipes/date_pipe.ts b/modules/angular2/src/pipes/date_pipe.ts similarity index 91% rename from modules/angular2/src/change_detection/pipes/date_pipe.ts rename to modules/angular2/src/pipes/date_pipe.ts index b04f88c13b..78867a0d05 100644 --- a/modules/angular2/src/change_detection/pipes/date_pipe.ts +++ b/modules/angular2/src/pipes/date_pipe.ts @@ -11,7 +11,11 @@ import { import {DateFormatter} from 'angular2/src/facade/intl'; import {Injectable} from 'angular2/di'; import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection'; -import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; + +import {PipeTransform, WrappedValue, BasePipeTransform} from 'angular2/change_detection'; +import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception'; + +import {Pipe} from 'angular2/src/core/annotations/decorators'; // TODO: move to a global configable location along with other i18n components. var defaultLocale: string = 'en-US'; @@ -71,8 +75,9 @@ var defaultLocale: string = 'en-US'; * {{ dateObj | date:'mmss' }} // output is '43:11' */ @CONST() +@Pipe({name: 'date'}) @Injectable() -export class DatePipe extends BasePipe { +export class DatePipe extends BasePipeTransform { static _ALIASES = { 'medium': 'yMMMdjms', 'short': 'yMdjm', @@ -102,5 +107,5 @@ export class DatePipe extends BasePipe { return DateFormatter.format(value, defaultLocale, pattern); } - supports(obj: any): boolean { return isDate(obj) || isNumber(obj); } + private supports(obj: any): boolean { return isDate(obj) || isNumber(obj); } } diff --git a/modules/angular2/src/pipes/default_pipes.ts b/modules/angular2/src/pipes/default_pipes.ts new file mode 100644 index 0000000000..1bc679f4f3 --- /dev/null +++ b/modules/angular2/src/pipes/default_pipes.ts @@ -0,0 +1,27 @@ +import {AsyncPipe} from './async_pipe'; +import {UpperCasePipe} from './uppercase_pipe'; +import {LowerCasePipe} from './lowercase_pipe'; +import {JsonPipe} from './json_pipe'; +import {LimitToPipe} from './limit_to_pipe'; +import {DatePipe} from './date_pipe'; +import {DecimalPipe, PercentPipe, CurrencyPipe} from './number_pipe'; + +import {CONST_EXPR} from 'angular2/src/facade/lang'; +import {Binding, OpaqueToken} from 'angular2/di'; + +const DEFAULT_PIPES_LIST = CONST_EXPR([ + AsyncPipe, + UpperCasePipe, + LowerCasePipe, + JsonPipe, + LimitToPipe, + DecimalPipe, + PercentPipe, + CurrencyPipe, + DatePipe +]); + +export const DEFAULT_PIPES_TOKEN = CONST_EXPR(new OpaqueToken("Default Pipes")); + +export const DEFAULT_PIPES = + CONST_EXPR(new Binding(DEFAULT_PIPES_TOKEN, {toValue: DEFAULT_PIPES_LIST})); diff --git a/modules/angular2/src/pipes/invalid_pipe_argument_exception.ts b/modules/angular2/src/pipes/invalid_pipe_argument_exception.ts new file mode 100644 index 0000000000..44601f1373 --- /dev/null +++ b/modules/angular2/src/pipes/invalid_pipe_argument_exception.ts @@ -0,0 +1,7 @@ +import {ABSTRACT, BaseException, CONST, Type} from 'angular2/src/facade/lang'; + +export class InvalidPipeArgumentException extends BaseException { + constructor(type: Type, value: Object) { + super(`Invalid argument '${value}' for pipe '${type}'`); + } +} diff --git a/modules/angular2/src/change_detection/pipes/json_pipe.ts b/modules/angular2/src/pipes/json_pipe.ts similarity index 72% rename from modules/angular2/src/change_detection/pipes/json_pipe.ts rename to modules/angular2/src/pipes/json_pipe.ts index f68921efcf..a088d95c69 100644 --- a/modules/angular2/src/change_detection/pipes/json_pipe.ts +++ b/modules/angular2/src/pipes/json_pipe.ts @@ -1,6 +1,9 @@ import {isBlank, isPresent, Json, CONST} from 'angular2/src/facade/lang'; import {Injectable} from 'angular2/di'; -import {Pipe, BasePipe} from './pipe'; + +import {PipeTransform, WrappedValue, BasePipeTransform} from 'angular2/change_detection'; + +import {Pipe} from 'angular2/src/core/annotations/decorators'; /** * Implements json transforms to any object. @@ -26,7 +29,8 @@ import {Pipe, BasePipe} from './pipe'; * ``` */ @CONST() +@Pipe({name: 'json'}) @Injectable() -export class JsonPipe extends BasePipe { +export class JsonPipe extends BasePipeTransform { transform(value: any, args: List = null): string { return Json.stringify(value); } } diff --git a/modules/angular2/src/change_detection/pipes/limit_to_pipe.ts b/modules/angular2/src/pipes/limit_to_pipe.ts similarity index 87% rename from modules/angular2/src/change_detection/pipes/limit_to_pipe.ts rename to modules/angular2/src/pipes/limit_to_pipe.ts index 1ec2664d1b..95ee2e4a16 100644 --- a/modules/angular2/src/change_detection/pipes/limit_to_pipe.ts +++ b/modules/angular2/src/pipes/limit_to_pipe.ts @@ -9,7 +9,11 @@ import { import {ListWrapper} from 'angular2/src/facade/collection'; import {Math} from 'angular2/src/facade/math'; import {Injectable} from 'angular2/di'; -import {WrappedValue, Pipe, InvalidPipeArgumentException} from './pipe'; + +import {PipeTransform, WrappedValue, BasePipeTransform} from 'angular2/change_detection'; +import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception'; + +import {Pipe} from 'angular2/src/core/annotations/decorators'; /** * Creates a new List or String containing only a prefix/suffix of the @@ -50,8 +54,9 @@ import {WrappedValue, Pipe, InvalidPipeArgumentException} from './pipe'; * {{ 'abcdefghij' | limitTo: -4 }} // output is 'ghij' * {{ 'abcdefghij' | limitTo: -100 }} // output is 'abcdefghij' */ +@Pipe({name: 'limitTo'}) @Injectable() -export class LimitToPipe implements Pipe { +export class LimitToPipe implements PipeTransform { supports(obj: any): boolean { return isString(obj) || isArray(obj); } transform(value: any, args: List = null): any { diff --git a/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts b/modules/angular2/src/pipes/lowercase_pipe.ts similarity index 69% rename from modules/angular2/src/change_detection/pipes/lowercase_pipe.ts rename to modules/angular2/src/pipes/lowercase_pipe.ts index 10933cc86b..146d71f03c 100644 --- a/modules/angular2/src/change_detection/pipes/lowercase_pipe.ts +++ b/modules/angular2/src/pipes/lowercase_pipe.ts @@ -1,6 +1,11 @@ import {isString, StringWrapper, CONST, isBlank} from 'angular2/src/facade/lang'; import {Injectable} from 'angular2/di'; -import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; + +import {PipeTransform, WrappedValue, BasePipeTransform} from 'angular2/change_detection'; +import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception'; + + +import {Pipe} from 'angular2/src/core/annotations/decorators'; /** * Implements lowercase transforms to text. @@ -23,8 +28,9 @@ import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; * ``` */ @CONST() +@Pipe({name: 'lowercase'}) @Injectable() -export class LowerCasePipe extends BasePipe { +export class LowerCasePipe extends BasePipeTransform { transform(value: string, args: List = null): string { if (isBlank(value)) return value; if (!isString(value)) { diff --git a/modules/angular2/src/change_detection/pipes/number_pipe.ts b/modules/angular2/src/pipes/number_pipe.ts similarity index 91% rename from modules/angular2/src/change_detection/pipes/number_pipe.ts rename to modules/angular2/src/pipes/number_pipe.ts index f6a29ca4b5..d7048fe8f9 100644 --- a/modules/angular2/src/change_detection/pipes/number_pipe.ts +++ b/modules/angular2/src/pipes/number_pipe.ts @@ -12,14 +12,18 @@ import { import {NumberFormatter, NumberFormatStyle} from 'angular2/src/facade/intl'; import {Injectable} from 'angular2/di'; import {ListWrapper} from 'angular2/src/facade/collection'; -import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; + +import {PipeTransform, WrappedValue, BasePipeTransform} from 'angular2/change_detection'; +import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception'; + +import {Pipe} from 'angular2/src/core/annotations/decorators'; var defaultLocale: string = 'en-US'; var _re = RegExpWrapper.create('^(\\d+)?\\.((\\d+)(\\-(\\d+))?)?$'); @CONST() @Injectable() -export class NumberPipe extends BasePipe { +export class NumberPipe extends BasePipeTransform { static _format(value: number, style: NumberFormatStyle, digits: string, currency: string = null, currencyAsSymbol: boolean = false): string { if (isBlank(value)) return null; @@ -78,6 +82,8 @@ export class NumberPipe extends BasePipe { * {{ 1 | number: '2.2' }} // output is 01.00 */ @CONST() +@Pipe({name: 'number'}) +@Injectable() export class DecimalPipe extends NumberPipe { transform(value: any, args: any[]): string { var digits: string = ListWrapper.first(args); @@ -95,6 +101,8 @@ export class DecimalPipe extends NumberPipe { * For more information about `digitInfo` see {@link DecimalPipe} */ @CONST() +@Pipe({name: 'percent'}) +@Injectable() export class PercentPipe extends NumberPipe { transform(value: any, args: any[]): string { var digits: string = ListWrapper.first(args); @@ -116,6 +124,8 @@ export class PercentPipe extends NumberPipe { * For more information about `digitInfo` see {@link DecimalPipe} */ @CONST() +@Pipe({name: 'currency'}) +@Injectable() export class CurrencyPipe extends NumberPipe { transform(value: any, args: any[]): string { var currencyCode: string = isPresent(args) && args.length > 0 ? args[0] : 'USD'; diff --git a/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts b/modules/angular2/src/pipes/uppercase_pipe.ts similarity index 69% rename from modules/angular2/src/change_detection/pipes/uppercase_pipe.ts rename to modules/angular2/src/pipes/uppercase_pipe.ts index ff82c588dd..a76768a650 100644 --- a/modules/angular2/src/change_detection/pipes/uppercase_pipe.ts +++ b/modules/angular2/src/pipes/uppercase_pipe.ts @@ -1,6 +1,10 @@ import {isString, StringWrapper, CONST, isBlank} from 'angular2/src/facade/lang'; import {Injectable} from 'angular2/di'; -import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; + +import {PipeTransform, WrappedValue, BasePipeTransform} from 'angular2/change_detection'; +import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception'; + +import {Pipe} from 'angular2/src/core/annotations/decorators'; /** * Implements uppercase transforms to text. @@ -23,8 +27,9 @@ import {Pipe, BasePipe, InvalidPipeArgumentException} from './pipe'; * ``` */ @CONST() +@Pipe({name: 'uppercase'}) @Injectable() -export class UpperCasePipe extends BasePipe { +export class UpperCasePipe extends BasePipeTransform { transform(value: string, args: List = null): string { if (isBlank(value)) return value; if (!isString(value)) { diff --git a/modules/angular2/src/test_lib/spies.dart b/modules/angular2/src/test_lib/spies.dart index d0836acd8f..e906e1aed3 100644 --- a/modules/angular2/src/test_lib/spies.dart +++ b/modules/angular2/src/test_lib/spies.dart @@ -14,11 +14,6 @@ class SpyProtoChangeDetector extends SpyObject implements ProtoChangeDetector { noSuchMethod(m) => super.noSuchMethod(m); } -@proxy -class SpyPipe extends SpyObject implements Pipe { - noSuchMethod(m) => super.noSuchMethod(m); -} - @proxy class SpyDependencyProvider extends SpyObject implements DependencyProvider { noSuchMethod(m) => super.noSuchMethod(m); diff --git a/modules/angular2/src/test_lib/spies.ts b/modules/angular2/src/test_lib/spies.ts index b3a2b432db..7343ac2c0c 100644 --- a/modules/angular2/src/test_lib/spies.ts +++ b/modules/angular2/src/test_lib/spies.ts @@ -7,7 +7,6 @@ import { import {DependencyProvider} from 'angular2/di'; -import {BasePipe} from 'angular2/src/change_detection/pipes/pipe'; import {SpyObject, proxy} from './test_lib'; export class SpyChangeDetector extends SpyObject { @@ -18,10 +17,6 @@ export class SpyProtoChangeDetector extends SpyObject { constructor() { super(DynamicChangeDetector); } } -export class SpyPipe extends SpyObject { - constructor() { super(BasePipe); } -} - export class SpyDependencyProvider extends SpyObject {} export class SpyIterableDifferFactory extends SpyObject {} diff --git a/modules/angular2/src/test_lib/test_injector.ts b/modules/angular2/src/test_lib/test_injector.ts index e91cad942e..4c7dacfa58 100644 --- a/modules/angular2/src/test_lib/test_injector.ts +++ b/modules/angular2/src/test_lib/test_injector.ts @@ -7,17 +7,17 @@ import { Lexer, ChangeDetection, DynamicChangeDetection, - Pipes, - defaultPipes, IterableDiffers, defaultIterableDiffers, KeyValueDiffers, defaultKeyValueDiffers } from 'angular2/src/change_detection/change_detection'; +import {DEFAULT_PIPES} from 'angular2/pipes'; import {ExceptionHandler} from 'angular2/src/core/exception_handler'; import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader'; import {ViewResolver} from 'angular2/src/core/compiler/view_resolver'; import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; +import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver'; import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader'; import {XHR} from 'angular2/src/render/xhr'; import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; @@ -122,7 +122,7 @@ function _getAppBindings() { Compiler, CompilerCache, bind(ViewResolver).toClass(MockViewResolver), - defaultPipes, + DEFAULT_PIPES, bind(IterableDiffers).toValue(defaultIterableDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(ChangeDetection).toClass(DynamicChangeDetection), @@ -130,6 +130,7 @@ function _getAppBindings() { ViewLoader, DynamicComponentLoader, DirectiveResolver, + PipeResolver, Parser, Lexer, bind(ExceptionHandler).toValue(new ExceptionHandler(DOM)), 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 789bd8906c..60a4fba90f 100644 --- a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart @@ -269,12 +269,11 @@ class _CodegenState { var newValue = _names.getLocalName(r.selfIndex); var pipe = _names.getPipeName(r.selfIndex); - var cdRef = 'this.ref'; var pipeType = r.name; var read = ''' if ($_IDENTICAL_CHECK_FN($pipe, $_UTIL.uninitialized)) { - $pipe = ${_names.getPipesAccessorName()}.get('$pipeType', $cdRef); + $pipe = ${_names.getPipesAccessorName()}.get('$pipeType'); } $newValue = $pipe.transform($context, [$argString]); '''; diff --git a/modules/angular2/src/util/decorators.ts b/modules/angular2/src/util/decorators.ts index b9da7c39d1..582c9e96e9 100644 --- a/modules/angular2/src/util/decorators.ts +++ b/modules/angular2/src/util/decorators.ts @@ -268,6 +268,7 @@ export function makeParamDecorator(annotationCls): any { return ParamDecorator; } + function ParamDecorator(cls, unusedKey, index): any { var parameters: Array> = Reflect.getMetadata('parameters', cls); parameters = parameters || []; diff --git a/modules/angular2/src/web-workers/ui/di_bindings.ts b/modules/angular2/src/web-workers/ui/di_bindings.ts index 17f559d25f..cf9ac9060d 100644 --- a/modules/angular2/src/web-workers/ui/di_bindings.ts +++ b/modules/angular2/src/web-workers/ui/di_bindings.ts @@ -10,10 +10,9 @@ import { ChangeDetection, DynamicChangeDetection, JitChangeDetection, - PreGeneratedChangeDetection, - Pipes, - defaultPipes + PreGeneratedChangeDetection } from 'angular2/src/change_detection/change_detection'; +import {DEFAULT_PIPES} from 'angular2/pipes'; import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager'; import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; @@ -114,7 +113,7 @@ function _injectorBindings(): List> { Compiler, CompilerCache, ViewResolver, - bind(Pipes).toValue(defaultPipes), + DEFAULT_PIPES, bind(ChangeDetection).toClass(bestChangeDetection), ViewLoader, DirectiveResolver, diff --git a/modules/angular2/src/web-workers/worker/application_common.ts b/modules/angular2/src/web-workers/worker/application_common.ts index 4b7f661578..731030f133 100644 --- a/modules/angular2/src/web-workers/worker/application_common.ts +++ b/modules/angular2/src/web-workers/worker/application_common.ts @@ -18,16 +18,16 @@ import { DynamicChangeDetection, JitChangeDetection, PreGeneratedChangeDetection, - Pipes, - defaultPipes, IterableDiffers, defaultIterableDiffers, KeyValueDiffers, defaultKeyValueDiffers } from 'angular2/src/change_detection/change_detection'; +import {DEFAULT_PIPES} from 'angular2/pipes'; import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver'; import {ExceptionHandler} from 'angular2/src/core/exception_handler'; import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; +import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver'; import {ViewResolver} from 'angular2/src/core/compiler/view_resolver'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/facade/async'; @@ -123,11 +123,12 @@ function _injectorBindings(appComponentType, bus: WebWorkerMessageBus, Compiler, CompilerCache, ViewResolver, - defaultPipes, + DEFAULT_PIPES, bind(IterableDiffers).toValue(defaultIterableDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(ChangeDetection).toClass(bestChangeDetection), DirectiveResolver, + PipeResolver, Parser, Lexer, bind(ExceptionHandler).toFactory(() => new ExceptionHandler(new PrintLogger()), []), diff --git a/modules/angular2/test/change_detection/change_detector_spec.ts b/modules/angular2/test/change_detection/change_detector_spec.ts index 26a7233864..ee946898dd 100644 --- a/modules/angular2/test/change_detection/change_detector_spec.ts +++ b/modules/angular2/test/change_detection/change_detector_spec.ts @@ -1,4 +1,4 @@ -/// +/// import { ddescribe, describe, @@ -29,8 +29,7 @@ import { BindingRecord, DirectiveRecord, DirectiveIndex, - Pipes, - Pipe, + PipeTransform, CHECK_ALWAYS, CHECK_ONCE, CHECKED, @@ -45,6 +44,8 @@ import { Locals, ProtoChangeDetector } from 'angular2/src/change_detection/change_detection'; + +import {Pipes} from 'angular2/src/change_detection/pipes'; import {JitProtoChangeDetector} from 'angular2/src/change_detection/jit_proto_change_detector'; import {getDefinition} from './change_detector_config'; @@ -809,19 +810,6 @@ export function main() { expect(val.dispatcher.log).toEqual(['propName=Megatron state:1']); }); - - it('should inject the ChangeDetectorRef ' + - 'of the encompassing component into a pipe', - () => { - - var registry = new FakePipes('pipe', () => new IdentityPipe()); - var cd = - _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector; - - cd.detectChanges(); - - expect(registry.cdRef).toBe(cd.ref); - }); }); it('should do nothing when no change', () => { @@ -854,30 +842,30 @@ export function main() { }); } -class CountingPipe implements Pipe { +class CountingPipe implements PipeTransform { state: number = 0; onDestroy() {} transform(value, args = null) { return `${value} state:${this.state ++}`; } } -class PipeWithOnDestroy implements Pipe { +class PipeWithOnDestroy implements PipeTransform { destroyCalled: boolean = false; onDestroy() { this.destroyCalled = true; } transform(value, args = null) { return null; } } -class IdentityPipe implements Pipe { +class IdentityPipe implements PipeTransform { onDestroy() {} transform(value, args = null) { return value; } } -class WrappedPipe implements Pipe { +class WrappedPipe implements PipeTransform { onDestroy() {} transform(value, args = null) { return WrappedValue.wrap(value); } } -class MultiArgPipe implements Pipe { +class MultiArgPipe implements PipeTransform { transform(value, args = null) { var arg1 = args[0]; var arg2 = args[1]; @@ -887,16 +875,14 @@ class MultiArgPipe implements Pipe { onDestroy(): void {} } -class FakePipes extends Pipes { +class FakePipes implements Pipes { numberOfLookups = 0; - cdRef: any; - constructor(public pipeType: string, public factory: Function) { super(null, null); } + constructor(public pipeType: string, public factory: Function) {} - get(type: string, cdRef?) { + get(type: string) { if (type != this.pipeType) return null; this.numberOfLookups++; - this.cdRef = cdRef; return this.factory(); } } diff --git a/modules/angular2/test/change_detection/pipes/pipes_spec.ts b/modules/angular2/test/change_detection/pipes/pipes_spec.ts deleted file mode 100644 index 9a4ba18e43..0000000000 --- a/modules/angular2/test/change_detection/pipes/pipes_spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { - ddescribe, - xdescribe, - describe, - it, - iit, - xit, - expect, - beforeEach, - afterEach -} from 'angular2/test_lib'; - -import {Injector, bind} from 'angular2/di'; -import {Pipes} from 'angular2/src/change_detection/pipes/pipes'; -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 injector; - - beforeEach(() => { injector = Injector.resolveAndCreate([]); }); - - it("should instantiate a pipe", () => { - var r = new Pipes({"type": APipe}, injector); - expect(r.get("type", null)).toBeAnInstanceOf(APipe); - }); - - 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({}, null); - expect(() => r.get("unknown", null)).toThrowError(`Cannot find pipe 'unknown'.`); - }); - - describe('.create()', () => { - it("should create a new Pipes object", () => { - var pipes = Pipes.create({'pipe': APipe}, null); - expect(pipes.config).toEqual({'pipe': APipe}); - }); - - it("should merge pipes config", () => { - var pipes1 = Pipes.create({'pipe': APipe, 'pipe1': APipe}, null); - var pipes2 = Pipes.create({'pipe': AnotherPipe, 'pipe2': AnotherPipe}, null, pipes1); - - expect(pipes2.config).toEqual({'pipe': AnotherPipe, 'pipe1': APipe, 'pipe2': AnotherPipe}); - }); - - it("should not change parent's config", () => { - var pipes1 = Pipes.create({'pipe': APipe, 'pipe1': APipe}, null); - Pipes.create({'pipe': AnotherPipe, 'pipe2': AnotherPipe}, null, pipes1); - - expect(pipes1.config).toEqual({'pipe': APipe, 'pipe1': APipe}); - }); - }); - - describe(".extend()", () => { - it('should create a factory that prepend new pipes to old', () => { - var pipes1 = Pipes.create({'pipe': APipe, 'pipe1': APipe}, null); - var binding = Pipes.extend({'pipe': AnotherPipe, 'pipe2': AnotherPipe}); - var pipes: Pipes = binding.toFactory(pipes1, injector); - - expect(pipes.config).toEqual({'pipe': AnotherPipe, 'pipe1': APipe, 'pipe2': AnotherPipe}); - }); - - it('should throw if calling extend when creating root injector', () => { - var injector = Injector.resolveAndCreate([Pipes.extend({'pipe': APipe})]); - - expect(() => injector.get(Pipes)) - .toThrowErrorWith("Cannot extend Pipes without a parent injector"); - }); - }); - }); -} diff --git a/modules/angular2/test/core/compiler/compiler_spec.ts b/modules/angular2/test/core/compiler/compiler_spec.ts index 445309b493..2a8b24ff0d 100644 --- a/modules/angular2/test/core/compiler/compiler_spec.ts +++ b/modules/angular2/test/core/compiler/compiler_spec.ts @@ -22,7 +22,8 @@ import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {AppProtoView} from 'angular2/src/core/compiler/view'; import {ElementBinder} from 'angular2/src/core/compiler/element_binder'; import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; -import {Attribute, View, Component, Directive} from 'angular2/annotations'; +import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver'; +import {Attribute, View, Component, Directive, Pipe} from 'angular2/annotations'; import * as viewAnn from 'angular2/src/core/annotations_impl/view'; import {internalProtoView} from 'angular2/src/core/compiler/view_ref'; import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector'; @@ -38,11 +39,14 @@ import {AppRootUrl} from 'angular2/src/services/app_root_url'; import * as renderApi from 'angular2/src/render/api'; // TODO(tbosch): Spys don't support named modules... import {RenderCompiler} from 'angular2/src/render/api'; +import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding'; + + export function main() { describe('compiler', function() { - var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper, - rootProtoView; + var directiveResolver, pipeResolver, tplResolver, renderCompiler, protoViewFactory, + cmpUrlMapper, rootProtoView; var renderCompileRequests: any[]; function createCompiler(renderCompileResults: @@ -57,13 +61,14 @@ export function main() { }); protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults); - return new Compiler(directiveResolver, new CompilerCache(), tplResolver, cmpUrlMapper, - urlResolver, renderCompiler, protoViewFactory, + return new Compiler(directiveResolver, pipeResolver, [SomeDefaultPipe], new CompilerCache(), + tplResolver, cmpUrlMapper, urlResolver, renderCompiler, protoViewFactory, new AppRootUrl("http://www.app.com")); } beforeEach(() => { directiveResolver = new DirectiveResolver(); + pipeResolver = new PipeResolver(); tplResolver = new FakeViewResolver(); cmpUrlMapper = new RuntimeComponentUrlMapper(); renderCompiler = new SpyRenderCompiler(); @@ -304,6 +309,20 @@ export function main() { }); })); + it('should pass the pipe bindings', inject([AsyncTestCompleter], (async) => { + tplResolver.setView(MainComponent, + new viewAnn.View({template: '
', pipes: [SomePipe]})); + var compiler = + createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]); + compiler.compileInHost(MainComponent) + .then((_) => { + var request = protoViewFactory.requests[1]; + expect(request[3][0].key.token).toBe(SomeDefaultPipe); + expect(request[3][1].key.token).toBe(SomePipe); + async.done(); + }); + })); + it('should use the protoView of the ProtoViewFactory', inject([AsyncTestCompleter], (async) => { tplResolver.setView(MainComponent, new viewAnn.View({template: '
'})); @@ -399,9 +418,9 @@ export function main() { var reader: any = new SpyDirectiveResolver(); // create the compiler - var compiler = - new Compiler(reader, cache, tplResolver, cmpUrlMapper, new UrlResolver(), - renderCompiler, protoViewFactory, new AppRootUrl("http://www.app.com")); + var compiler = new Compiler(reader, pipeResolver, [], cache, tplResolver, cmpUrlMapper, + new UrlResolver(), renderCompiler, protoViewFactory, + new AppRootUrl("http://www.app.com")); compiler.compileInHost(MainComponent) .then((protoViewRef) => { // the test should have failed if the resolver was called, so we're good @@ -570,7 +589,7 @@ function createProtoView(elementBinders = null, type: renderApi.ViewType = null, type = renderApi.ViewType.COMPONENT; } var pv = new AppProtoView(type, isEmbeddedFragment, new renderApi.RenderProtoViewRef(), null, - null, new Map(), null); + null, new Map(), null, null); if (isBlank(elementBinders)) { elementBinders = []; } @@ -653,6 +672,14 @@ class DirectiveWithProperties { class DirectiveWithBind { } +@Pipe({name: 'some-default-pipe'}) +class SomeDefaultPipe { +} + +@Pipe({name: 'some-pipe'}) +class SomePipe { +} + @Directive({selector: 'directive-with-accts'}) class DirectiveWithAttributes { constructor(@Attribute('someAttr') someAttr: String) {} @@ -694,8 +721,8 @@ class FakeProtoViewFactory extends ProtoViewFactory { } createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, - directives: List): AppProtoView[] { - this.requests.push([componentBinding, renderProtoView, directives]); + directives: List, pipes: PipeBinding[]): AppProtoView[] { + this.requests.push([componentBinding, renderProtoView, directives, pipes]); return collectEmbeddedPvs(ListWrapper.removeAt(this.results, 0)); } } diff --git a/modules/angular2/test/core/compiler/element_injector_spec.ts b/modules/angular2/test/core/compiler/element_injector_spec.ts index f1c1e0ef13..1e582629e2 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.ts +++ b/modules/angular2/test/core/compiler/element_injector_spec.ts @@ -192,14 +192,17 @@ class OptionallyInjectsTemplateRef { @Injectable() class DirectiveNeedsChangeDetectorRef { - changeDetectorRef; - constructor(cdr: ChangeDetectorRef) { this.changeDetectorRef = cdr; } + constructor(public changeDetectorRef: ChangeDetectorRef) {} } @Injectable() class ComponentNeedsChangeDetectorRef { - changeDetectorRef; - constructor(cdr: ChangeDetectorRef) { this.changeDetectorRef = cdr; } + constructor(public changeDetectorRef: ChangeDetectorRef) {} +} + +@Injectable() +class PipeNeedsChangeDetectorRef { + constructor(public changeDetectorRef: ChangeDetectorRef) {} } class A_Needs_B { diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts index db7e51f1d8..b041c810aa 100644 --- a/modules/angular2/test/core/compiler/integration_spec.ts +++ b/modules/angular2/test/core/compiler/integration_spec.ts @@ -55,15 +55,14 @@ import { SkipSelf, SkipSelfMetadata } from 'angular2/di'; + import { - Pipes, - defaultPipes, - Pipe, + PipeTransform, ChangeDetectorRef, ON_PUSH } from 'angular2/src/change_detection/change_detection'; -import {Directive, Component, View, Attribute, Query} from 'angular2/annotations'; +import {Directive, Component, View, Attribute, Query, Pipe} from 'angular2/annotations'; import * as viewAnn from 'angular2/src/core/annotations_impl/view'; import {QueryList} from 'angular2/src/core/compiler/query_list'; @@ -98,6 +97,7 @@ export function main() { rootTC.detectChanges(); expect(rootTC.nativeElement).toHaveText('Hello World!'); async.done(); + }); })); @@ -242,12 +242,13 @@ export function main() { it("should support pipes in bindings", inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { - tcb.overrideView(MyCompWithPipes, new viewAnn.View({ + tcb.overrideView(MyComp, new viewAnn.View({ template: '
', - directives: [MyDir] + directives: [MyDir], + pipes: [DoublePipe] })) - .createAsync(MyCompWithPipes) + .createAsync(MyComp) .then((rootTC) => { rootTC.componentInstance.ctxProp = 'a'; rootTC.detectChanges(); @@ -1661,21 +1662,6 @@ class PushCmpWithAsyncPipe { resolve(value) { this.completer.resolve(value); } } -@Injectable() -class PipesWithDouble extends Pipes { - constructor(injector: Injector) { super({"double": DoublePipe}, injector); } -} - -@Component({ - selector: 'my-comp-with-pipes', - viewBindings: [new Binding(Pipes, {toClass: PipesWithDouble})] -}) -@View({directives: []}) -@Injectable() -class MyCompWithPipes { - ctxProp: string = "initial value"; -} - @Component({selector: 'my-comp'}) @View({directives: []}) @Injectable() @@ -1754,8 +1740,8 @@ class SomeViewport { } } -@Injectable() -class DoublePipe implements Pipe { +@Pipe({name: 'double'}) +class DoublePipe implements PipeTransform { onDestroy() {} transform(value, args = null) { return `${value}${value}`; } } diff --git a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts index 157523a224..3d7934eb1c 100644 --- a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts +++ b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts @@ -70,7 +70,7 @@ export function main() { varBindings.set('a', 'b'); var renderPv = createRenderProtoView([], null, varBindings); var appPvs = - protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, []); + protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, [], []); expect(appPvs[0].variableBindings.get('a')).toEqual('b'); expect(appPvs.length).toBe(1); }); diff --git a/modules/angular2/test/core/compiler/view_manager_utils_spec.ts b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts index 4e9530e989..c6b353797d 100644 --- a/modules/angular2/test/core/compiler/view_manager_utils_spec.ts +++ b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts @@ -318,7 +318,7 @@ function _createProtoView(type: ViewType, binders: ElementBinder[] = null) { } var protoChangeDetector = new SpyProtoChangeDetector(); protoChangeDetector.spy('instantiate').andReturn(new SpyChangeDetector()); - var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0); + var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0, null); res.elementBinders = binders; var mappedElementIndices = ListWrapper.createFixedSize(countNestedElementBinders(res)); for (var i = 0; i < binders.length; i++) { diff --git a/modules/angular2/test/core/compiler/view_pool_spec.ts b/modules/angular2/test/core/compiler/view_pool_spec.ts index 0a933ba5be..15698b5683 100644 --- a/modules/angular2/test/core/compiler/view_pool_spec.ts +++ b/modules/angular2/test/core/compiler/view_pool_spec.ts @@ -25,7 +25,7 @@ export function main() { function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); } function createProtoView() { - return new AppProtoView(null, null, null, null, null, null, null); + return new AppProtoView(null, null, null, null, null, null, null, null); } function createView(pv) { diff --git a/modules/angular2/test/core/pipes/pipe_binding_spec.ts b/modules/angular2/test/core/pipes/pipe_binding_spec.ts new file mode 100644 index 0000000000..612cbdae68 --- /dev/null +++ b/modules/angular2/test/core/pipes/pipe_binding_spec.ts @@ -0,0 +1,27 @@ +import { + ddescribe, + xdescribe, + describe, + it, + iit, + xit, + expect, + beforeEach, + afterEach +} from 'angular2/test_lib'; + +import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding'; +import {Pipe} from 'angular2/src/core/annotations_impl/annotations'; + +class MyPipe {} + +export function main() { + describe("PipeBinding", () => { + it('should create a binding out of a type', () => { + var binding = PipeBinding.createFromType(MyPipe, new Pipe({name: 'my-pipe'})); + expect(binding.name).toEqual('my-pipe'); + expect(binding.factory()).toBeAnInstanceOf(MyPipe); + expect(binding.dependencies.length).toEqual(0); + }); + }); +} diff --git a/modules/angular2/test/core/pipes/pipes_spec.ts b/modules/angular2/test/core/pipes/pipes_spec.ts new file mode 100644 index 0000000000..dfb3b1b983 --- /dev/null +++ b/modules/angular2/test/core/pipes/pipes_spec.ts @@ -0,0 +1,56 @@ +import { + ddescribe, + xdescribe, + describe, + it, + iit, + xit, + expect, + beforeEach, + afterEach +} from 'angular2/test_lib'; + +import {PipeTransform} from 'angular2/change_detection'; +import {Injector, Inject, bind} from 'angular2/di'; +import {ProtoPipes, Pipes} from 'angular2/src/core/pipes/pipes'; +import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding'; +import {Pipe} from 'angular2/src/core/annotations_impl/annotations'; + +class PipeA implements PipeTransform { + transform(a, b) {} + onDestroy() {} +} + +class PipeB implements PipeTransform { + dep; + constructor(@Inject("dep") dep: any) { this.dep = dep; } + transform(a, b) {} + onDestroy() {} +} + +export function main() { + describe("Pipes", () => { + var injector; + + beforeEach( + () => { injector = Injector.resolveAndCreate([bind('dep').toValue('dependency')]); }); + + it('should instantiate a pipe', () => { + var proto = new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a'}))]); + var pipes = new Pipes(proto, injector); + expect(pipes.get("a")).toBeAnInstanceOf(PipeA); + }); + + it('should throw when no pipe found', () => { + var proto = new ProtoPipes([]); + var pipes = new Pipes(proto, injector); + expect(() => pipes.get("invalid")).toThrowErrorWith("Cannot find pipe 'invalid'"); + }); + + it('should inject dependencies from the provided injector', () => { + var proto = new ProtoPipes([PipeBinding.createFromType(PipeB, new Pipe({name: 'b'}))]); + var pipes = new Pipes(proto, injector); + expect(pipes.get("b").dep).toEqual("dependency"); + }); + }); +} diff --git a/modules/angular2/test/change_detection/pipes/async_pipe_spec.ts b/modules/angular2/test/pipes/async_pipe_spec.ts similarity index 97% rename from modules/angular2/test/change_detection/pipes/async_pipe_spec.ts rename to modules/angular2/test/pipes/async_pipe_spec.ts index 04e4dc6b1a..4e75716ce1 100644 --- a/modules/angular2/test/change_detection/pipes/async_pipe_spec.ts +++ b/modules/angular2/test/pipes/async_pipe_spec.ts @@ -14,8 +14,8 @@ import { } from 'angular2/test_lib'; import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang'; -import {WrappedValue} from 'angular2/src/change_detection/pipes/pipe'; -import {AsyncPipe} from 'angular2/src/change_detection/pipes/async_pipe'; +import {WrappedValue} from 'angular2/change_detection'; +import {AsyncPipe} from 'angular2/pipes'; import { EventEmitter, ObservableWrapper, diff --git a/modules/angular2/test/change_detection/pipes/date_pipe_spec.ts b/modules/angular2/test/pipes/date_pipe_spec.ts similarity index 97% rename from modules/angular2/test/change_detection/pipes/date_pipe_spec.ts rename to modules/angular2/test/pipes/date_pipe_spec.ts index a8618f266f..0155ff6a07 100644 --- a/modules/angular2/test/change_detection/pipes/date_pipe_spec.ts +++ b/modules/angular2/test/pipes/date_pipe_spec.ts @@ -1,6 +1,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import {DatePipe} from 'angular2/src/change_detection/pipes/date_pipe'; +import {DatePipe} from 'angular2/pipes'; import {DateWrapper} from 'angular2/src/facade/lang'; export function main() { diff --git a/modules/angular2/test/change_detection/pipes/json_pipe_spec.ts b/modules/angular2/test/pipes/json_pipe_spec.ts similarity index 97% rename from modules/angular2/test/change_detection/pipes/json_pipe_spec.ts rename to modules/angular2/test/pipes/json_pipe_spec.ts index b4e6bf4502..59bb82fa16 100644 --- a/modules/angular2/test/change_detection/pipes/json_pipe_spec.ts +++ b/modules/angular2/test/pipes/json_pipe_spec.ts @@ -15,7 +15,7 @@ import { } from 'angular2/test_lib'; import {Json, RegExp, NumberWrapper, StringWrapper} from 'angular2/src/facade/lang'; -import {JsonPipe} from 'angular2/src/change_detection/pipes/json_pipe'; +import {JsonPipe} from 'angular2/pipes'; export function main() { describe("JsonPipe", () => { diff --git a/modules/angular2/test/change_detection/pipes/limit_to_pipe_spec.ts b/modules/angular2/test/pipes/limit_to_pipe_spec.ts similarity index 95% rename from modules/angular2/test/change_detection/pipes/limit_to_pipe_spec.ts rename to modules/angular2/test/pipes/limit_to_pipe_spec.ts index c4a7870cd8..3e50d3b43d 100644 --- a/modules/angular2/test/change_detection/pipes/limit_to_pipe_spec.ts +++ b/modules/angular2/test/pipes/limit_to_pipe_spec.ts @@ -1,6 +1,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import {LimitToPipe} from 'angular2/src/change_detection/pipes/limit_to_pipe'; +import {LimitToPipe} from 'angular2/pipes'; export function main() { describe("LimitToPipe", () => { diff --git a/modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts b/modules/angular2/test/pipes/lowercase_pipe_spec.ts similarity index 91% rename from modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts rename to modules/angular2/test/pipes/lowercase_pipe_spec.ts index 1680dcc958..3f5ba4dec0 100644 --- a/modules/angular2/test/change_detection/pipes/lowercase_pipe_spec.ts +++ b/modules/angular2/test/pipes/lowercase_pipe_spec.ts @@ -1,6 +1,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import {LowerCasePipe} from 'angular2/src/change_detection/pipes/lowercase_pipe'; +import {LowerCasePipe} from 'angular2/pipes'; export function main() { describe("LowerCasePipe", () => { diff --git a/modules/angular2/test/change_detection/pipes/number_pipe_spec.ts b/modules/angular2/test/pipes/number_pipe_spec.ts similarity index 94% rename from modules/angular2/test/change_detection/pipes/number_pipe_spec.ts rename to modules/angular2/test/pipes/number_pipe_spec.ts index 92f11c5eab..29dd9ff98e 100644 --- a/modules/angular2/test/change_detection/pipes/number_pipe_spec.ts +++ b/modules/angular2/test/pipes/number_pipe_spec.ts @@ -1,10 +1,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import { - DecimalPipe, - PercentPipe, - CurrencyPipe -} from 'angular2/src/change_detection/pipes/number_pipe'; +import {DecimalPipe, PercentPipe, CurrencyPipe} from 'angular2/pipes'; export function main() { describe("DecimalPipe", () => { @@ -18,6 +14,7 @@ export function main() { expect(pipe.transform(123, ['.2'])).toEqual('123.00'); expect(pipe.transform(1, ['3.'])).toEqual('001'); expect(pipe.transform(1.1, ['3.4-5'])).toEqual('001.1000'); + expect(pipe.transform(1.123456, ['3.4-5'])).toEqual('001.12346'); expect(pipe.transform(1.1234, [])).toEqual('1.123'); }); diff --git a/modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts b/modules/angular2/test/pipes/uppercase_pipe_spec.ts similarity index 91% rename from modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts rename to modules/angular2/test/pipes/uppercase_pipe_spec.ts index 39b6f5710e..1955a34879 100644 --- a/modules/angular2/test/change_detection/pipes/uppercase_pipe_spec.ts +++ b/modules/angular2/test/pipes/uppercase_pipe_spec.ts @@ -1,6 +1,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import {UpperCasePipe} from 'angular2/src/change_detection/pipes/uppercase_pipe'; +import {UpperCasePipe} from 'angular2/pipes'; export function main() { describe("UpperCasePipe", () => { diff --git a/modules/angular2/test/router/route_config_spec.ts b/modules/angular2/test/router/route_config_spec.ts index 7d53624d2e..adbd9c088d 100644 --- a/modules/angular2/test/router/route_config_spec.ts +++ b/modules/angular2/test/router/route_config_spec.ts @@ -26,9 +26,17 @@ import { routerDirectives } from 'angular2/router'; +import {ExceptionHandler} from 'angular2/src/core/exception_handler'; import {LocationStrategy} from 'angular2/src/router/location_strategy'; import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy'; +class _ArrayLogger { + res: any[] = []; + log(s: any): void { this.res.push(s); } + logGroup(s: any): void { this.res.push(s); } + logGroupEnd(){}; +} + export function main() { describe('RouteConfig with POJO arguments', () => { var fakeDoc, el, testBindings; @@ -36,10 +44,13 @@ export function main() { fakeDoc = DOM.createHtmlDocument(); el = DOM.createElement('app-cmp', fakeDoc); DOM.appendChild(fakeDoc.body, el); + var logger = new _ArrayLogger(); + var exceptionHandler = new ExceptionHandler(logger, true); testBindings = [ routerInjectables, bind(LocationStrategy).toClass(MockLocationStrategy), - bind(DOCUMENT_TOKEN).toValue(fakeDoc) + bind(DOCUMENT_TOKEN).toValue(fakeDoc), + bind(ExceptionHandler).toValue(exceptionHandler) ]; }); diff --git a/modules/benchmarks/src/compiler/compiler_benchmark.ts b/modules/benchmarks/src/compiler/compiler_benchmark.ts index 90b3f079d5..bda36b8961 100644 --- a/modules/benchmarks/src/compiler/compiler_benchmark.ts +++ b/modules/benchmarks/src/compiler/compiler_benchmark.ts @@ -11,6 +11,7 @@ import { import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; +import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver'; import * as viewModule from 'angular2/src/core/annotations_impl/view'; import {Component, Directive, View} from 'angular2/angular2'; @@ -38,6 +39,7 @@ export function main() { reflector.reflectionCapabilities = new ReflectionCapabilities(); var reader = new DirectiveResolver(); + var pipeResolver = new PipeResolver(); var cache = new CompilerCache(); var viewResolver = new MultipleViewResolver( count, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]); @@ -46,9 +48,9 @@ export function main() { var renderCompiler = new DefaultDomCompiler( new DomElementSchemaRegistry(), new TemplateCloner(-1), new Parser(new Lexer()), new ViewLoader(null, null, null), new SharedStylesHost(), 'a'); - var compiler = - new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver, - renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()), appRootUrl); + var compiler = new Compiler(reader, pipeResolver, [], cache, viewResolver, + new ComponentUrlMapper(), urlResolver, renderCompiler, + new ProtoViewFactory(new DynamicChangeDetection()), appRootUrl); function measureWrapper(func, desc) { return function() { diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index aee455c5ee..639c26bb19 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -101,7 +101,7 @@ module.exports = function makeBrowserTree(options, destinationPath) { declaration: true, emitDecoratorMetadata: true, mapRoot: '', // force sourcemaps to use relative path - noEmitOnError: true, + noEmitOnError: false, rootDir: '.', sourceMap: true, sourceRoot: '.',