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
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}]);
|
||||
`;
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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>): 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>): any { return _abstract(); }
|
||||
}
|
||||
|
||||
function _abstract() {
|
||||
throw new BaseException('This method is abstract');
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import {PipeTransform} from './pipe_transform';
|
||||
|
||||
export interface Pipes { get(name: string): PipeTransform; }
|
||||
@@ -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>): 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>): 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');
|
||||
}
|
||||
@@ -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<string, Type | Binding>;
|
||||
|
||||
constructor(config: StringMap<string, Type | Binding>, 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<string, Type | Binding>): 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<string, Type | Binding>, injector: Injector,
|
||||
pipes: Pipes = null): Pipes {
|
||||
if (isPresent(pipes)) {
|
||||
return new Pipes(StringMapWrapper.merge(pipes.config, config), injector);
|
||||
} else {
|
||||
return new Pipes(config, injector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
export {
|
||||
Component as ComponentAnnotation,
|
||||
Directive as DirectiveAnnotation,
|
||||
Pipe as PipeAnnotation,
|
||||
LifecycleEvent
|
||||
} from '../annotations_impl/annotations';
|
||||
|
||||
@@ -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<Type | any | List<any>>,
|
||||
pipes?: List<Type | any | List<any>>,
|
||||
renderer?: string,
|
||||
styles?: List<string>,
|
||||
styleUrls?: List<string>,
|
||||
@@ -44,6 +50,7 @@ export interface ViewDecorator extends TypeDecorator {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: List<Type | any | List<any>>,
|
||||
pipes?: List<Type | any | List<any>>,
|
||||
renderer?: string,
|
||||
styles?: List<string>,
|
||||
styleUrls?: List<string>,
|
||||
@@ -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 = <PipeFactory>makeDecorator(PipeAnnotation);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ export class View {
|
||||
// for an unused import.
|
||||
directives: List<Type | any | List<any>>;
|
||||
|
||||
pipes: List<Type | any | List<any>>;
|
||||
|
||||
/**
|
||||
* 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<Type | any | List<any>>,
|
||||
pipes?: List<Type | any | List<any>>,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: List<string>,
|
||||
styleUrls?: List<string>,
|
||||
@@ -108,6 +111,7 @@ export class View {
|
||||
this.styleUrls = styleUrls;
|
||||
this.styles = styles;
|
||||
this.directives = directives;
|
||||
this.pipes = pipes;
|
||||
this.encapsulation = encapsulation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Type | Binding | List<any>> {
|
||||
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), []),
|
||||
|
||||
@@ -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<Type, Promise<AppProtoView>>;
|
||||
private _viewResolver: ViewResolver;
|
||||
private _componentUrlMapper: ComponentUrlMapper;
|
||||
private _urlResolver: UrlResolver;
|
||||
private _compiling: Map<Type, Promise<AppProtoView>> = 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 <hostcmp></hostcmp>.
|
||||
// Used for bootstrapping.
|
||||
compileInHost(componentTypeOrBinding: Type | Binding): Promise<ProtoViewRef> {
|
||||
@@ -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<Type> {
|
||||
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<Type> {
|
||||
if (isBlank(view.directives)) return [];
|
||||
var directives = [];
|
||||
this._flattenList(template.directives, directives);
|
||||
|
||||
this._flattenList(view.directives, directives);
|
||||
return directives;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ElementInjector> 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<ElementInjector> 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 = <DirectiveDependency>dep;
|
||||
var dirBin = <DirectiveBinding>binding;
|
||||
var staticKeys = StaticKeys.instance();
|
||||
if (binding instanceof DirectiveBinding) {
|
||||
var dirDep = <DirectiveDependency>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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}`);
|
||||
}
|
||||
}
|
||||
@@ -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<DirectiveBinding>): AppProtoView[] {
|
||||
allDirectives: List<DirectiveBinding>, 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<string, string>, allDirectives: List<DirectiveBinding>): AppProtoView {
|
||||
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>,
|
||||
pipes: PipeBinding[]): AppProtoView {
|
||||
var elementBinders = renderProtoView.elementBinders;
|
||||
// Embedded ProtoViews that contain `<ng-content>` 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);
|
||||
|
||||
|
||||
@@ -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<string, string>,
|
||||
public variableLocations: Map<string, number>, public textBindingCount: number) {
|
||||
public variableLocations: Map<string, number>, public textBindingCount: number,
|
||||
public pipes: ProtoPipes) {
|
||||
this.ref = new ProtoViewRef(this);
|
||||
if (isPresent(variableBindings)) {
|
||||
MapWrapper.forEach(variableBindings,
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<string, PipeBinding> = {};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -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;
|
||||
|
||||
+8
-3
@@ -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); }
|
||||
}
|
||||
@@ -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}));
|
||||
@@ -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}'`);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user