refactor(core): simplify decorators

Every decorator now is made of the following:
- a function that can be used
as a decorator or as a constructor. This function
also can be used for `instanceof` checks.
- a type for this function (callable and newable)
- a type that describes the shape of the data
  that the user needs to pass to the decorator
  as well as the instance of the metadata

The docs for decorators live at the followig places
so that IDEs can discover them correctly:
- General description of the decorator is placed on the
  `...Decorator` interface on the callable function
  definition
- Property descriptions are placed on the interface
  that describes the metadata produces by the decorator
This commit is contained in:
Tobias Bosch 2016-09-12 09:44:20 -07:00 committed by Igor Minar
parent 26d1423ae9
commit 1b15170c89
25 changed files with 1573 additions and 2466 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, NgModuleMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, NgModuleMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from './private_import_core'; import {ReflectorReader} from './private_import_core';
@ -169,16 +169,11 @@ export class StaticReflector implements ReflectorReader {
} }
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void { private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => { this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
var metadata = Object.create(ctor.prototype);
ctor.apply(metadata, args);
return metadata;
});
} }
private registerFunction(type: StaticSymbol, fn: any): void { private registerFunction(type: StaticSymbol, fn: any): void {
this.conversionMap.set( this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
type, (context: StaticSymbol, args: any[]) => { return fn.apply(undefined, args); });
} }
private initializeConversionMap(): void { private initializeConversionMap(): void {

View File

@ -431,7 +431,7 @@ describe('StaticReflector', () => {
const annotations = reflector.annotations( const annotations = reflector.annotations(
host.getStaticSymbol('/tmp/src/static-method-ref.ts', 'MethodReference')); host.getStaticSymbol('/tmp/src/static-method-ref.ts', 'MethodReference'));
expect(annotations.length).toBe(1); expect(annotations.length).toBe(1);
expect(annotations[0]._providers[0].useValue.members[0]).toEqual('staticMethod'); expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
}); });
}); });

View File

@ -63,20 +63,23 @@ export class DirectiveResolver {
inputs.push(propName); inputs.push(propName);
} }
} else if (a instanceof OutputMetadata) { } else if (a instanceof OutputMetadata) {
if (isPresent(a.bindingPropertyName)) { const output: OutputMetadata = a;
outputs.push(`${propName}: ${a.bindingPropertyName}`); if (isPresent(output.bindingPropertyName)) {
outputs.push(`${propName}: ${output.bindingPropertyName}`);
} else { } else {
outputs.push(propName); outputs.push(propName);
} }
} else if (a instanceof HostBindingMetadata) { } else if (a instanceof HostBindingMetadata) {
if (isPresent(a.hostPropertyName)) { const hostBinding: HostBindingMetadata = a;
host[`[${a.hostPropertyName}]`] = propName; if (isPresent(hostBinding.hostPropertyName)) {
host[`[${hostBinding.hostPropertyName}]`] = propName;
} else { } else {
host[`[${propName}]`] = propName; host[`[${propName}]`] = propName;
} }
} else if (a instanceof HostListenerMetadata) { } else if (a instanceof HostListenerMetadata) {
var args = isPresent(a.args) ? (<any[]>a.args).join(', ') : ''; const hostListener: HostListenerMetadata = a;
host[`(${a.eventName})`] = `${propName}(${args})`; var args = isPresent(hostListener.args) ? (<any[]>hostListener.args).join(', ') : '';
host[`(${hostListener.eventName})`] = `${propName}(${args})`;
} else if (a instanceof QueryMetadata) { } else if (a instanceof QueryMetadata) {
queries[propName] = a; queries[propName] = a;
} }

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AttributeMetadata, ChangeDetectionStrategy, ComponentMetadata, HostMetadata, InjectMetadata, Injectable, ModuleWithProviders, OptionalMetadata, Provider, QueryMetadata, SchemaMetadata, SelfMetadata, SkipSelfMetadata, Type, ViewQueryMetadata, resolveForwardRef} from '@angular/core'; import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AttributeMetadata, ChangeDetectionStrategy, ComponentMetadata, HostMetadata, InjectMetadata, Injectable, ModuleWithProviders, OptionalMetadata, Provider, QueryMetadata, SchemaMetadata, SelfMetadata, SkipSelfMetadata, Type, resolveForwardRef} from '@angular/core';
import {StringMapWrapper} from '../src/facade/collection'; import {StringMapWrapper} from '../src/facade/collection';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata'; import * as cpl from './compile_metadata';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {isArray, isBlank, isPresent, isString, stringify} from './facade/lang'; import {StringWrapper, isArray, isBlank, isPresent, isString, stringify} from './facade/lang';
import {Identifiers, resolveIdentifierToken} from './identifiers'; import {Identifiers, resolveIdentifierToken} from './identifiers';
import {hasLifecycleHook} from './lifecycle_reflector'; import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
@ -484,7 +484,7 @@ export class CompileMetadataResolver {
let isSkipSelf = false; let isSkipSelf = false;
let isOptional = false; let isOptional = false;
let query: QueryMetadata = null; let query: QueryMetadata = null;
let viewQuery: ViewQueryMetadata = null; let viewQuery: QueryMetadata = null;
var token: any = null; var token: any = null;
if (isArray(param)) { if (isArray(param)) {
(<any[]>param).forEach((paramEntry) => { (<any[]>param).forEach((paramEntry) => {
@ -664,11 +664,16 @@ export class CompileMetadataResolver {
return res; return res;
} }
private _queryVarBindings(selector: any): string[] {
return StringWrapper.split(selector, /\s*,\s*/g);
}
getQueryMetadata(q: QueryMetadata, propertyName: string, typeOrFunc: Type<any>|Function): getQueryMetadata(q: QueryMetadata, propertyName: string, typeOrFunc: Type<any>|Function):
cpl.CompileQueryMetadata { cpl.CompileQueryMetadata {
var selectors: cpl.CompileTokenMetadata[]; var selectors: cpl.CompileTokenMetadata[];
if (q.isVarBindingQuery) { if (isString(q.selector)) {
selectors = q.varBindings.map(varName => this.getTokenMetadata(varName)); selectors = this._queryVarBindings(q.selector).map(varName => this.getTokenMetadata(varName));
} else { } else {
if (!isPresent(q.selector)) { if (!isPresent(q.selector)) {
throw new Error( throw new Error(

View File

@ -62,7 +62,7 @@ export class MockDirectiveResolver extends DirectiveResolver {
let view = this._views.get(type); let view = this._views.get(type);
if (!view) { if (!view) {
view = metadata; view = <any>metadata;
} }
let animations = view.animations; let animations = view.animations;

View File

@ -26,7 +26,7 @@ export * from './directive_resolver_mock';
export * from './ng_module_resolver_mock'; export * from './ng_module_resolver_mock';
export * from './pipe_resolver_mock'; export * from './pipe_resolver_mock';
import {createPlatformFactory, ModuleWithComponentFactories, Injectable, CompilerOptions, COMPILER_OPTIONS, CompilerFactory, ComponentFactory, NgModuleFactory, Injector, NgModuleMetadata, NgModuleMetadataType, ComponentMetadata, ComponentMetadataType, DirectiveMetadata, DirectiveMetadataType, PipeMetadata, PipeMetadataType, Type, PlatformRef} from '@angular/core'; import {createPlatformFactory, ModuleWithComponentFactories, Injectable, CompilerOptions, COMPILER_OPTIONS, CompilerFactory, NgModuleFactory, Injector, NgModuleMetadata, NgModule, ComponentMetadata, Component, DirectiveMetadata, Directive, Pipe, Type, PlatformRef} from '@angular/core';
import {MetadataOverride} from '@angular/core/testing'; import {MetadataOverride} from '@angular/core/testing';
import {TestingCompilerFactory, TestingCompiler} from './private_import_core'; import {TestingCompilerFactory, TestingCompiler} from './private_import_core';
import {platformCoreDynamic, RuntimeCompiler, DirectiveResolver, NgModuleResolver, PipeResolver} from '@angular/compiler'; import {platformCoreDynamic, RuntimeCompiler, DirectiveResolver, NgModuleResolver, PipeResolver} from '@angular/compiler';
@ -70,25 +70,24 @@ export class TestingCompilerImpl implements TestingCompiler {
return this._compiler.compileModuleAndAllComponentsAsync(moduleType); return this._compiler.compileModuleAndAllComponentsAsync(moduleType);
} }
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModuleMetadataType>): void { overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
const oldMetadata = this._moduleResolver.resolve(ngModule, false); const oldMetadata = this._moduleResolver.resolve(ngModule, false);
this._moduleResolver.setNgModule( this._moduleResolver.setNgModule(
ngModule, this._overrider.overrideMetadata(NgModuleMetadata, oldMetadata, override)); ngModule, this._overrider.overrideMetadata(NgModuleMetadata, oldMetadata, override));
} }
overrideDirective(directive: Type<any>, override: MetadataOverride<DirectiveMetadataType>): void { overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
const oldMetadata = this._directiveResolver.resolve(directive, false); const oldMetadata = this._directiveResolver.resolve(directive, false);
this._directiveResolver.setDirective( this._directiveResolver.setDirective(
directive, this._overrider.overrideMetadata(DirectiveMetadata, oldMetadata, override)); directive, this._overrider.overrideMetadata(DirectiveMetadata, oldMetadata, override));
} }
overrideComponent(component: Type<any>, override: MetadataOverride<ComponentMetadataType>): void { overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
const oldMetadata = this._directiveResolver.resolve(component, false); const oldMetadata = this._directiveResolver.resolve(component, false);
this._directiveResolver.setDirective( this._directiveResolver.setDirective(
component, this._overrider.overrideMetadata(ComponentMetadata, oldMetadata, override)); component, this._overrider.overrideMetadata(ComponentMetadata, oldMetadata, override));
} }
overridePipe(pipe: Type<any>, override: MetadataOverride<PipeMetadataType>): void { overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
const oldMetadata = this._pipeResolver.resolve(pipe, false); const oldMetadata = this._pipeResolver.resolve(pipe, false);
this._pipeResolver.setPipe( this._pipeResolver.setPipe(pipe, this._overrider.overrideMetadata(Pipe, oldMetadata, override));
pipe, this._overrider.overrideMetadata(PipeMetadata, oldMetadata, override));
} }
clearCache(): void { this._compiler.clearCache(); } clearCache(): void { this._compiler.clearCache(); }
clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); } clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from './di/decorators'; import {Injectable} from './di';
import {print, warn} from './facade/lang'; import {print, warn} from './facade/lang';
@Injectable() @Injectable()

View File

@ -12,11 +12,7 @@
* The `di` module provides dependency injection container services. * The `di` module provides dependency injection container services.
*/ */
export {HostMetadata, InjectMetadata, InjectableMetadata, OptionalMetadata, SelfMetadata, SkipSelfMetadata} from './di/metadata'; export * from './di/metadata';
// we have to reexport * because Dart and TS export two different sets of types
export * from './di/decorators';
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref'; export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';

View File

@ -1,109 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {makeDecorator, makeParamDecorator} from '../util/decorators';
import {HostMetadata, InjectMetadata, InjectableMetadata, OptionalMetadata, SelfMetadata, SkipSelfMetadata} from './metadata';
/**
* Factory for creating {@link InjectMetadata}.
* @stable
*/
export interface InjectMetadataFactory {
(token: any): any;
new (token: any): InjectMetadata;
}
/**
* Factory for creating {@link OptionalMetadata}.
* @stable
*/
export interface OptionalMetadataFactory {
(): any;
new (): OptionalMetadata;
}
/**
* Factory for creating {@link InjectableMetadata}.
* @stable
*/
export interface InjectableMetadataFactory {
(): any;
new (): InjectableMetadata;
}
/**
* Factory for creating {@link SelfMetadata}.
* @stable
*/
export interface SelfMetadataFactory {
(): any;
new (): SelfMetadata;
}
/**
* Factory for creating {@link HostMetadata}.
* @stable
*/
export interface HostMetadataFactory {
(): any;
new (): HostMetadata;
}
/**
* Factory for creating {@link SkipSelfMetadata}.
* @stable
*/
export interface SkipSelfMetadataFactory {
(): any;
new (): SkipSelfMetadata;
}
/**
* Factory for creating {@link InjectMetadata}.
* @stable
* @Annotation
*/
export var Inject: InjectMetadataFactory = makeParamDecorator(InjectMetadata);
/**
* Factory for creating {@link OptionalMetadata}.
* @stable
* @Annotation
*/
export var Optional: OptionalMetadataFactory = makeParamDecorator(OptionalMetadata);
/**
* Factory for creating {@link InjectableMetadata}.
* @stable
* @Annotation
*/
export var Injectable: InjectableMetadataFactory =
<InjectableMetadataFactory>makeDecorator(InjectableMetadata);
/**
* Factory for creating {@link SelfMetadata}.
* @stable
* @Annotation
*/
export var Self: SelfMetadataFactory = makeParamDecorator(SelfMetadata);
/**
* Factory for creating {@link HostMetadata}.
* @stable
* @Annotation
*/
export var Host: HostMetadataFactory = makeParamDecorator(HostMetadata);
/**
* Factory for creating {@link SkipSelfMetadata}.
* @stable
* @Annotation
*/
export var SkipSelf: SkipSelfMetadataFactory = makeParamDecorator(SkipSelfMetadata);

View File

@ -7,8 +7,15 @@
*/ */
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {makeParamDecorator} from '../util/decorators';
/** /**
* Type of the Inject decorator / constructor function.
*
* @stable
*/
export interface InjectMetadataFactory {
/**
* A parameter metadata that specifies a dependency. * A parameter metadata that specifies a dependency.
* *
* ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview)) * ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview))
@ -32,7 +39,8 @@ import {stringify} from '../facade/lang';
* expect(injector.get(Car).engine instanceof Engine).toBe(true); * expect(injector.get(Car).engine instanceof Engine).toBe(true);
* ``` * ```
* *
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the parameter. * When `@Inject()` is not present, {@link Injector} will use the type annotation of the
* parameter.
* *
* ### Example * ### Example
* *
@ -49,12 +57,33 @@ import {stringify} from '../facade/lang';
* ``` * ```
* @stable * @stable
*/ */
export class InjectMetadata { (token: any): any;
constructor(public token: any) {} new (token: any): Inject;
toString(): string { return `@Inject(${stringify(this.token)})`; }
} }
/** /**
* Type of the Inject metadata.
*
* @stable
*/
export interface Inject { token: any; }
/**
* Inject decorator and metadata.
*
* @stable
* @Annotation
*/
export const Inject: InjectMetadataFactory = makeParamDecorator([['token', undefined]]);
/**
* Type of the Optional decorator / constructor function.
*
* @stable
*/
export interface OptionalMetadataFactory {
/**
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if * A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if
* the dependency is not found. * the dependency is not found.
* *
@ -76,20 +105,32 @@ export class InjectMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class OptionalMetadata { (): any;
toString(): string { return `@Optional()`; } new (): Optional;
} }
/** /**
* `DependencyMetadata` is used by the framework to extend DI. * Type of the Optional metadata.
* This is internal to Angular and should not be used directly. *
* @stable * @stable
*/ */
export class DependencyMetadata { export interface Optional {}
get token(): any { return null; }
}
/** /**
* Optional decorator and metadata.
*
* @stable
* @Annotation
*/
export const Optional: OptionalMetadataFactory = makeParamDecorator([]);
/**
* Type of the Injectable decorator / constructor function.
*
* @stable
*/
export interface InjectableMetadataFactory {
/**
* A marker metadata that marks a class as available to {@link Injector} for creation. * A marker metadata that marks a class as available to {@link Injector} for creation.
* *
* ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview)) * ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview))
@ -121,11 +162,32 @@ export class DependencyMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class InjectableMetadata { (): any;
constructor() {} new (): Injectable;
} }
/** /**
* Type of the Injectable metadata.
*
* @stable
*/
export interface Injectable {}
/**
* Injectable decorator and metadata.
*
* @stable
* @Annotation
*/
export const Injectable: InjectableMetadataFactory = makeParamDecorator([]);
/**
* Type of the Self decorator / constructor function.
*
* @stable
*/
export interface SelfMetadataFactory {
/**
* Specifies that an {@link Injector} should retrieve a dependency only from itself. * Specifies that an {@link Injector} should retrieve a dependency only from itself.
* *
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview)) * ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
@ -153,11 +215,33 @@ export class InjectableMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class SelfMetadata { (): any;
toString(): string { return `@Self()`; } new (): Self;
} }
/** /**
* Type of the Self metadata.
*
* @stable
*/
export interface Self {}
/**
* Self decorator and metadata.
*
* @stable
* @Annotation
*/
export const Self: SelfMetadataFactory = makeParamDecorator([]);
/**
* Type of the SkipSelf decorator / constructor function.
*
* @stable
*/
export interface SkipSelfMetadataFactory {
/**
* Specifies that the dependency resolution should start from the parent injector. * Specifies that the dependency resolution should start from the parent injector.
* *
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview)) * ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
@ -183,11 +267,32 @@ export class SelfMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class SkipSelfMetadata { (): any;
toString(): string { return `@SkipSelf()`; } new (): SkipSelf;
} }
/** /**
* Type of the SkipSelf metadata.
*
* @stable
*/
export interface SkipSelf {}
/**
* SkipSelf decorator and metadata.
*
* @stable
* @Annotation
*/
export const SkipSelf: SkipSelfMetadataFactory = makeParamDecorator([]);
/**
* Type of the Host decorator / constructor function.
*
* @stable
*/
export interface HostMetadataFactory {
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the * Specifies that an injector should retrieve a dependency from any injector until reaching the
* closest host. * closest host.
* *
@ -240,6 +345,31 @@ export class SkipSelfMetadata {
*``` *```
* @stable * @stable
*/ */
export class HostMetadata { (): any;
toString(): string { return `@Host()`; } new (): Host;
} }
/**
* Type of the Host metadata.
*
* @stable
*/
export interface Host {}
/**
* Host decorator and metadata.
*
* @stable
* @Annotation
*/
export const Host: HostMetadataFactory = makeParamDecorator([]);
// TODO(tbosch): remove this
export {
Host as HostMetadata,
Inject as InjectMetadata,
Injectable as InjectableMetadata,
Optional as OptionalMetadata,
Self as SelfMetadata,
SkipSelf as SkipSelfMetadata
};

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from './decorators'; import {Injectable} from './metadata';
/** /**
* Creates a token that can be used in a DI Provider. * Creates a token that can be used in a DI Provider.

View File

@ -12,7 +12,7 @@ import {reflector} from '../reflection/reflection';
import {Type} from '../type'; import {Type} from '../type';
import {resolveForwardRef} from './forward_ref'; import {resolveForwardRef} from './forward_ref';
import {DependencyMetadata, HostMetadata, InjectMetadata, OptionalMetadata, SelfMetadata, SkipSelfMetadata} from './metadata'; import {HostMetadata, InjectMetadata, OptionalMetadata, SelfMetadata, SkipSelfMetadata} from './metadata';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider'; import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider';
import {InvalidProviderError, MixingMultiProvidersWithRegularProvidersError, NoAnnotationError} from './reflective_errors'; import {InvalidProviderError, MixingMultiProvidersWithRegularProvidersError, NoAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key'; import {ReflectiveKey} from './reflective_key';
@ -256,12 +256,6 @@ function _extractToken(
} else if (paramMetadata instanceof SkipSelfMetadata) { } else if (paramMetadata instanceof SkipSelfMetadata) {
lowerBoundVisibility = paramMetadata; lowerBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof DependencyMetadata) {
if (isPresent(paramMetadata.token)) {
token = paramMetadata.token;
}
depProps.push(paramMetadata);
} }
} }

View File

@ -9,7 +9,7 @@
import {APP_ID} from '../application_tokens'; import {APP_ID} from '../application_tokens';
import {devModeEqual} from '../change_detection/change_detection'; import {devModeEqual} from '../change_detection/change_detection';
import {UNINITIALIZED} from '../change_detection/change_detection_util'; import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {Inject, Injectable} from '../di/decorators'; import {Inject, Injectable} from '../di';
import {ListWrapper} from '../facade/collection'; import {ListWrapper} from '../facade/collection';
import {isBlank, isPresent, looseIdentical} from '../facade/lang'; import {isBlank, isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view'; import {ViewEncapsulation} from '../metadata/view';

File diff suppressed because it is too large Load Diff

View File

@ -7,10 +7,10 @@
*/ */
import {resolveForwardRef} from '../di/forward_ref'; import {resolveForwardRef} from '../di/forward_ref';
import {DependencyMetadata} from '../di/metadata';
import {OpaqueToken} from '../di/opaque_token'; import {OpaqueToken} from '../di/opaque_token';
import {StringWrapper, isString, stringify} from '../facade/lang'; import {StringWrapper, isString, stringify} from '../facade/lang';
import {Type} from '../type'; import {Type} from '../type';
import {makeParamDecorator, makePropDecorator} from '../util/decorators';
/** /**
* This token can be used to create a virtual provider that will populate the * This token can be used to create a virtual provider that will populate the
@ -48,7 +48,14 @@ import {Type} from '../type';
*/ */
export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComponents'); export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComponents');
/** /**
* Type of the Attribute decorator / constructor function.
*
* @stable
*/
export interface AttributeMetadataFactory {
/**
* Specifies that a constant attribute value should be injected. * Specifies that a constant attribute value should be injected.
* *
* The directive can inject constant string literals of host element attributes. * The directive can inject constant string literals of host element attributes.
@ -64,175 +71,82 @@ export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComp
* A decorator can inject string literal `text` like so: * A decorator can inject string literal `text` like so:
* *
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'} * {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
*
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
*
* ### Example as ES5 DSL
*
* ```
* var MyComponent = ng
* .Component({...})
* .Class({
* constructor: [new ng.Attribute('title'), function(title) {
* ...
* }]
* })
* ```
*
* ### Example as ES5 annotation
*
* ```
* var MyComponent = function(title) {
* ...
* };
*
* MyComponent.annotations = [
* new ng.Component({...})
* ]
* MyComponent.parameters = [
* [new ng.Attribute('title')]
* ]
* ```
*
* @stable * @stable
*/ */ (name: string): any;
export class AttributeMetadata extends DependencyMetadata { new (name: string): Attribute;
constructor(public attributeName: string) { super(); }
get token(): AttributeMetadata {
// Normally one would default a token to a type of an injected value but here
// the type of a variable is "string" and we can't use primitive type as a return value
// so we use instance of Attribute instead. This doesn't matter much in practice as arguments
// with @Attribute annotation are injected by ElementInjector that doesn't take tokens into
// account.
return this;
}
toString(): string { return `@Attribute(${stringify(this.attributeName)})`; }
} }
/** /**
* Declares an injectable parameter to be a live list of directives or variable * Type of the Attribute metadata.
* bindings from the content children of a directive.
* *
* ### Example ([live demo](http://plnkr.co/edit/lY9m8HLy7z06vDoUaSN2?p=preview))
*
* Assume that `<tabs>` component would like to get a list its children `<pane>`
* components as shown in this example:
*
* ```html
* <tabs>
* <pane title="Overview">...</pane>
* <pane *ngFor="let o of objects" [title]="o.title">{{o.text}}</pane>
* </tabs>
* ```
*
* The preferred solution is to query for `Pane` directives using this decorator.
*
* ```javascript
* @Component({
* selector: 'pane',
* inputs: ['title']
* })
* class Pane {
* title:string;
* }
*
* @Component({
* selector: 'tabs',
* template: `
* <ul>
* <li *ngFor="let pane of panes">{{pane.title}}</li>
* </ul>
* <ng-content></ng-content>
* `
* })
* class Tabs {
* @ContentChildren(Pane) panes: QueryList<Pane>;
* }
* ```
*
* A query can look for variable bindings by passing in a string with desired binding symbol.
*
* ### Example ([live demo](http://plnkr.co/edit/sT2j25cH1dURAyBRCKx1?p=preview))
* ```html
* <seeker>
* <div #findme>...</div>
* </seeker>
*
* @Component({ selector: 'seeker' })
* class Seeker {
* @ContentChildren('findme') elList;
* }
* ```
*
* In this case the object that is injected depend on the type of the variable
* binding. It can be an ElementRef, a directive or a component.
*
* Passing in a comma separated list of variable bindings will query for all of them.
*
* ```html
* <seeker>
* <div #find-me>...</div>
* <div #find-me-too>...</div>
* </seeker>
*
* @Component({
* selector: 'seeker'
* })
* class Seeker {
* @ContentChildren('findMe, findMeToo') elList: QueryList<ElementRef>;
* }
* ```
*
* Configure whether query looks for direct children or all descendants
* of the querying element, by using the `descendants` parameter.
* It is set to `false` by default.
*
* ### Example ([live demo](http://plnkr.co/edit/wtGeB977bv7qvA5FTYl9?p=preview))
* ```html
* <container #first>
* <item>a</item>
* <item>b</item>
* <container #second>
* <item>c</item>
* </container>
* </container>
* ```
*
* When querying for items, the first container will see only `a` and `b` by default,
* but with `ContentChildren(TextDirective, {descendants: true})` it will see `c` too.
*
* The queried directives are kept in a depth-first pre-order with respect to their
* positions in the DOM.
*
* ContentChildren does not look deep into any subcomponent views.
*
* ContentChildren is updated as part of the change-detection cycle. Since change detection
* happens after construction of a directive, QueryList will always be empty when observed in the
* constructor.
*
* The injected object is an unmodifiable live list.
* See {@link QueryList} for more details.
* @stable * @stable
*/ */
export class QueryMetadata extends DependencyMetadata { export interface Attribute { attributeName?: string; }
/**
* whether we want to query only direct children (false) or all /**
* children (true). * Attribute decorator and metadata.
*
* @stable
* @Annotation
*/ */
export const Attribute: AttributeMetadataFactory =
makeParamDecorator([['attributeName', undefined]]);
/**
* Type of the Query metadata.
*
* @stable
*/
export interface Query {
descendants: boolean; descendants: boolean;
first: boolean; first: boolean;
/**
* The DI token to read from an element that matches the selector.
*/
read: any; read: any;
isViewQuery: boolean;
constructor( selector: any;
private _selector: Type<any>|string,
{descendants = false, first = false,
read = null}: {descendants?: boolean, first?: boolean, read?: any} = {}) {
super();
this.descendants = descendants;
this.first = first;
this.read = read;
}
/**
* always `false` to differentiate it with {@link ViewQueryMetadata}.
*/
get isViewQuery(): boolean { return false; }
/**
* what this is querying for.
*/
get selector() { return resolveForwardRef(this._selector); }
/**
* whether this is querying for a variable binding or a directive.
*/
get isVarBindingQuery(): boolean { return isString(this.selector); }
/**
* returns a list of variable bindings this is querying for.
* Only applicable if this is a variable bindings query.
*/
get varBindings(): string[] { return StringWrapper.split(this.selector, /\s*,\s*/g); }
toString(): string { return `@Query(${stringify(this.selector)})`; }
} }
// TODO: add an example after ContentChildren and ViewChildren are in master export abstract class Query {}
/** /**
* Type of the ContentChildren decorator / constructor function.
*
* @stable
*/
export interface ContentChildrenMetadataFactory {
/**
* Configures a content query. * Configures a content query.
* *
* Content queries are set before the `ngAfterContentInit` callback is called. * Content queries are set before the `ngAfterContentInit` callback is called.
@ -253,16 +167,40 @@ export class QueryMetadata extends DependencyMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class ContentChildrenMetadata extends QueryMetadata { (selector: Type<any>|Function|string,
constructor( {descendants, read}?: {descendants?: boolean, read?: any}): any;
_selector: Type<any>|string, new (
{descendants = false, read = null}: {descendants?: boolean, read?: any} = {}) { selector: Type<any>|Function|string,
super(_selector, {descendants: descendants, read: read}); {descendants, read}?: {descendants?: boolean, read?: any}): Query;
}
} }
// TODO: add an example after ContentChild and ViewChild are in master
/** /**
* Type of the ContentChildren metadata.
*
* @stable
*/
export type ContentChildren = Query;
/**
* ContentChildren decorator and metadata.
*
* @stable
* @Annotation
*/
export const ContentChildren: ContentChildrenMetadataFactory = makePropDecorator(
[
['selector', undefined],
{first: false, isViewQuery: false, descendants: false, read: undefined}
],
Query);
/**
* Type of the ContentChild decorator / constructor function.
*
* @stable
*/
export interface ContentChildMetadataFactory {
/**
* Configures a content query. * Configures a content query.
* *
* Content queries are set before the `ngAfterContentInit` callback is called. * Content queries are set before the `ngAfterContentInit` callback is called.
@ -275,67 +213,57 @@ export class ContentChildrenMetadata extends QueryMetadata {
* }) * })
* class SomeDir { * class SomeDir {
* @ContentChild(ChildDirective) contentChild; * @ContentChild(ChildDirective) contentChild;
* @ContentChild('container_ref') containerChild
* *
* ngAfterContentInit() { * ngAfterContentInit() {
* // contentChild is set * // contentChild is set
* // containerChild is set
* } * }
* } * }
* ``` * ```
* @stable *
* ```html
* <container #container_ref>
* <item>a</item>
* <item>b</item>
* </container>
* ```
*/ */
export class ContentChildMetadata extends QueryMetadata { (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
constructor(_selector: Type<any>|string, {read = null}: {read?: any} = {}) { new (selector: Type<any>|Function|string, {read}?: {read?: any}): ContentChild;
super(_selector, {descendants: true, first: true, read: read});
}
} }
/** /**
* Similar to {@link ContentChildMetadata}, but querying the component view, instead * Type of the ContentChild metadata.
* of the content children.
* *
* ### Example ([live demo](http://plnkr.co/edit/eNsFHDf7YjyM6IzKxM1j?p=preview))
*
* ```javascript
* @Component({
* ...,
* template: `
* <item> a </item>
* <item> b </item>
* <item> c </item>
* `
* })
* class MyComponent {
* shown: boolean;
*
* constructor(private @ViewChildren(Item) items:QueryList<Item>) {
* items.changes.subscribe(() => console.log(items.length));
* }
* }
* ```
*
* As `shown` is flipped between true and false, items will contain zero of one
* items.
*
* Specifies that a {@link QueryList} should be injected.
*
* The injected object is an iterable and observable live list.
* See {@link QueryList} for more details.
* @stable * @stable
*/ */
export class ViewQueryMetadata extends QueryMetadata { export type ContentChild = Query;
constructor(
_selector: Type<any>|string, {descendants = false, first = false, read = null}:
{descendants?: boolean, first?: boolean, read?: any} = {}) {
super(_selector, {descendants: descendants, first: first, read: read});
}
/**
* ContentChild decorator and metadata.
*
* @stable
* @Annotation
*/
export const ContentChild: ContentChildMetadataFactory = makePropDecorator(
[
['selector', undefined], {
first: true,
isViewQuery: false,
descendants: false,
read: undefined,
}
],
Query);
/**
* Type of the ViewChildrenMetadataFactory decorator / constructor function.
*
* @stable
*/
export interface ViewChildrenMetadataFactory {
/** /**
* always `true` to differentiate it with {@link QueryMetadata}.
*/
get isViewQuery() { return true; }
}
/**
* Declares a list of child element references. * Declares a list of child element references.
* *
* Angular automatically updates the list when the DOM is updated. * Angular automatically updates the list when the DOM is updated.
@ -412,15 +340,42 @@ export class ViewQueryMetadata extends QueryMetadata {
* } * }
* ``` * ```
* @stable * @stable
*/ */ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
export class ViewChildrenMetadata extends ViewQueryMetadata { new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChildren;
constructor(_selector: Type<any>|string, {read = null}: {read?: any} = {}) {
super(_selector, {descendants: true, read: read});
}
toString(): string { return `@ViewChildren(${stringify(this.selector)})`; }
} }
/** /**
* Type of the ViewChildren metadata.
*
* @stable
*/
export type ViewChildren = Query;
/**
* ViewChildren decorator and metadata.
*
* @stable
* @Annotation
*/
export const ViewChildren: ViewChildrenMetadataFactory = makePropDecorator(
[
['selector', undefined], {
first: false,
isViewQuery: true,
descendants: true,
read: undefined,
}
],
Query);
/**
* Type of the ViewChildMetadataFactory decorator / constructor function.
*
* @stable
*/
export interface ViewChildMetadataFactory {
/**
* *
* Declares a reference of child element. * Declares a reference of child element.
* *
@ -490,9 +445,30 @@ export class ViewChildrenMetadata extends ViewQueryMetadata {
* } * }
* ``` * ```
* @stable * @stable
*/ */ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
export class ViewChildMetadata extends ViewQueryMetadata { new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChild;
constructor(_selector: Type<any>|string, {read = null}: {read?: any} = {}) {
super(_selector, {descendants: true, first: true, read: read});
}
} }
/**
* Type of the ViewChild metadata.
*
* @stable
*/
export type ViewChild = Query;
/**
* ViewChild decorator and metadata.
*
* @stable
* @Annotation
*/
export const ViewChild: ViewChildMetadataFactory = makePropDecorator(
[
['selector', undefined], {
first: true,
isViewQuery: true,
descendants: true,
read: undefined,
}
],
Query);

View File

@ -11,23 +11,18 @@ import {ChangeDetectionStrategy} from '../change_detection/constants';
import {InjectableMetadata, Provider} from '../di'; import {InjectableMetadata, Provider} from '../di';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Type} from '../type'; import {Type} from '../type';
import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators';
import {ViewEncapsulation} from './view'; import {ViewEncapsulation} from './view';
/**
* Interface for creating {@link DirectiveMetadata}
* @stable
*/
export interface DirectiveMetadataType {
selector?: string;
inputs?: string[];
outputs?: string[];
host?: {[key: string]: string};
providers?: any[];
exportAs?: string;
queries?: {[key: string]: any};
}
/** /**
* Type of the Directive decorator / constructor function.
*
* @stable
*/
export interface DirectiveMetadataFactory {
/**
* Directives allow you to attach behavior to elements in the DOM. * Directives allow you to attach behavior to elements in the DOM.
* *
* {@link DirectiveMetadata}s with an embedded view are called {@link ComponentMetadata}s. * {@link DirectiveMetadata}s with an embedded view are called {@link ComponentMetadata}s.
@ -62,11 +57,13 @@ export interface DirectiveMetadataType {
* depth-first order. The * depth-first order. The
* current `ElementInjector` resolves the constructor dependencies for each directive. * current `ElementInjector` resolves the constructor dependencies for each directive.
* *
* Angular then resolves dependencies as follows, according to the order in which they appear in the * Angular then resolves dependencies as follows, according to the order in which they appear in
* the
* {@link ComponentMetadata}: * {@link ComponentMetadata}:
* *
* 1. Dependencies on the current element * 1. Dependencies on the current element
* 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary * 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM
* boundary
* 3. Dependencies on component injectors and their parents until it encounters the root component * 3. Dependencies on component injectors and their parents until it encounters the root component
* 4. Dependencies on pre-existing injectors * 4. Dependencies on pre-existing injectors
* *
@ -180,7 +177,8 @@ export interface DirectiveMetadataType {
* } * }
* } * }
* ``` * ```
* This directive would be instantiated with `Dependency` declared at the same element, in this case * This directive would be instantiated with `Dependency` declared at the same element, in this
* case
* `dependency="3"`. * `dependency="3"`.
* *
* ### Injecting a directive from any ancestor elements * ### Injecting a directive from any ancestor elements
@ -256,7 +254,8 @@ export interface DirectiveMetadataType {
* } * }
* ``` * ```
* *
* This directive would be instantiated with a `Dependency` directive found on the current element. * This directive would be instantiated with a `Dependency` directive found on the current
* element.
* If none can be * If none can be
* found, the injector supplies `null` instead of throwing an error. * found, the injector supplies `null` instead of throwing an error.
* *
@ -312,7 +311,8 @@ export interface DirectiveMetadataType {
* location in the current view * location in the current view
* where these actions are performed. * where these actions are performed.
* *
* Views are always created as children of the current {@link ComponentMetadata}, and as siblings of * Views are always created as children of the current {@link ComponentMetadata}, and as siblings
* of
* the * the
* `<template>` element. Thus a * `<template>` element. Thus a
* directive in a child view cannot inject the directive that created it. * directive in a child view cannot inject the directive that created it.
@ -340,9 +340,11 @@ export interface DirectiveMetadataType {
* </ul> * </ul>
* ``` * ```
* *
* Notice that although the shorthand places `*foo="bar"` within the `<li>` element, the binding for * Notice that although the shorthand places `*foo="bar"` within the `<li>` element, the binding
* for
* the directive * the directive
* controller is correctly instantiated on the `<template>` element rather than the `<li>` element. * controller is correctly instantiated on the `<template>` element rather than the `<li>`
* element.
* *
* ## Lifecycle hooks * ## Lifecycle hooks
* *
@ -403,12 +405,48 @@ export interface DirectiveMetadataType {
* </ul> * </ul>
* ``` * ```
* *
* Note also that although the `<li></li>` template still exists inside the `<template></template>`, * Note also that although the `<li></li>` template still exists inside the
* `<template></template>`,
* the instantiated * the instantiated
* view occurs on the second `<li></li>` which is a sibling to the `<template>` element. * view occurs on the second `<li></li>` which is a sibling to the `<template>` element.
* @stable *
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='directive'}
*
* ### Example as ES5 DSL
*
* ```
* var MyDirective = ng
* .Directive({...})
* .Class({
* constructor: function() {
* ...
* }
* })
* ```
*
* ### Example as ES5 annotation
*
* ```
* var MyDirective = function() {
* ...
* };
*
* MyDirective.annotations = [
* new ng.Directive({...})
* ]
* ```
*/ */
export class DirectiveMetadata extends InjectableMetadata implements DirectiveMetadataType { (obj: Directive): TypeDecorator;
/**
* See the {@link Directive} decorator.
*/
new (obj: Directive): Directive;
}
export interface Directive {
/** /**
* The CSS selector that triggers the instantiation of a directive. * The CSS selector that triggers the instantiation of a directive.
* *
@ -441,7 +479,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* The directive would only be instantiated on the `<input type="text">` element. * The directive would only be instantiated on the `<input type="text">` element.
* *
*/ */
selector: string; selector?: string;
/** /**
* Enumerates the set of data-bound input properties for a directive * Enumerates the set of data-bound input properties for a directive
@ -487,8 +525,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* ``` * ```
* *
*/ */
get inputs(): string[] { return this._inputs; } inputs?: string[];
private _inputs: string[];
/** /**
* Enumerates the set of event-bound output properties. * Enumerates the set of event-bound output properties.
@ -533,8 +570,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* ``` * ```
* *
*/ */
get outputs(): string[] { return this._outputs; } outputs?: string[];
private _outputs: string[];
/** /**
* Specify the events, actions, properties and attributes related to the host element. * Specify the events, actions, properties and attributes related to the host element.
@ -636,7 +672,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* } * }
* ``` * ```
*/ */
host: {[key: string]: string}; host?: {[key: string]: string};
/** /**
* Defines the set of injectable objects that are visible to a Directive and its light DOM * Defines the set of injectable objects that are visible to a Directive and its light DOM
@ -668,8 +704,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* } * }
* ``` * ```
*/ */
get providers(): Provider[] { return this._providers; } providers?: Provider[];
private _providers: Provider[];
/** /**
* Defines the name that can be used in the template to assign this directive to a variable. * Defines the name that can be used in the template to assign this directive to a variable.
@ -693,9 +728,8 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* *
* ``` * ```
*/ */
exportAs: string; exportAs?: string;
// TODO: add an example after ContentChildren and ViewChildren are in master
/** /**
* Configures the queries that will be injected into the directive. * Configures the queries that will be injected into the directive.
* *
@ -727,45 +761,38 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* } * }
* ``` * ```
*/ */
queries: {[key: string]: any}; queries?: {[key: string]: any};
constructor(
{selector, inputs, outputs, host, providers, exportAs, queries}: DirectiveMetadataType = {}) {
super();
this.selector = selector;
this._inputs = inputs;
this._outputs = outputs;
this.host = host;
this.exportAs = exportAs;
this.queries = queries;
this._providers = providers;
}
} }
/** /**
* Interface for creating {@link ComponentMetadataType} * Directive decorator and metadata.
*
* @stable
* @Annotation
*/
export const Directive: DirectiveMetadataFactory = <DirectiveMetadataFactory>makeDecorator({
selector: undefined,
inputs: undefined,
outputs: undefined,
host: undefined,
providers: undefined,
exportAs: undefined,
queries: undefined
});
/**
* Type of the Component decorator / constructor function.
*
* @stable * @stable
*/ */
export interface ComponentMetadataType extends DirectiveMetadataType { export interface ComponentMetadataFactory {
changeDetection?: ChangeDetectionStrategy; /**
viewProviders?: any[];
moduleId?: string;
templateUrl?: string;
template?: string;
styleUrls?: string[];
styles?: string[];
animations?: AnimationEntryMetadata[];
encapsulation?: ViewEncapsulation;
interpolation?: [string, string];
entryComponents?: Array<Type<any>|any[]>;
}
/**
* Declare reusable UI building blocks for an application. * Declare reusable UI building blocks for an application.
* *
* Each Angular component requires a single `@Component` annotation. The * Each Angular component requires a single `@Component` annotation. The
* `@Component` * `@Component`
* annotation specifies when a component is instantiated, and which properties and hostListeners it * annotation specifies when a component is instantiated, and which properties and hostListeners
* it
* binds to. * binds to.
* *
* When a component is instantiated, Angular * When a component is instantiated, Angular
@ -784,9 +811,48 @@ export interface ComponentMetadataType extends DirectiveMetadataType {
* ### Example * ### Example
* *
* {@example core/ts/metadata/metadata.ts region='component'} * {@example core/ts/metadata/metadata.ts region='component'}
*
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='component'}
*
* ### Example as ES5 DSL
*
* ```
* var MyComponent = ng
* .Component({...})
* .Class({
* constructor: function() {
* ...
* }
* })
* ```
*
* ### Example as ES5 annotation
*
* ```
* var MyComponent = function() {
* ...
* };
*
* MyComponent.annotations = [
* new ng.Component({...})
* ]
* ```
*/
(obj: Component): TypeDecorator;
/**
* See the {@link Component} decorator.
*/
new (obj: Component): Component;
}
/**
* Type of the Component metadata.
*
* @stable * @stable
*/ */
export class ComponentMetadata extends DirectiveMetadata implements ComponentMetadataType { export interface Component extends Directive {
/** /**
* Defines the used change detection strategy. * Defines the used change detection strategy.
* *
@ -796,7 +862,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* The `changeDetection` property defines, whether the change detection will be checked every time * The `changeDetection` property defines, whether the change detection will be checked every time
* or only when the component tells it to do so. * or only when the component tells it to do so.
*/ */
changeDetection: ChangeDetectionStrategy; changeDetection?: ChangeDetectionStrategy;
/** /**
* Defines the set of injectable objects that are visible to its view DOM children. * Defines the set of injectable objects that are visible to its view DOM children.
@ -835,8 +901,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* *
* ``` * ```
*/ */
get viewProviders(): Provider[] { return this._viewProviders; } viewProviders?: Provider[];
private _viewProviders: Provider[];
/** /**
* The module id of the module that contains the component. * The module id of the module that contains the component.
@ -857,7 +922,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* *
* ``` * ```
*/ */
moduleId: string; moduleId?: string;
/** /**
* Specifies a template URL for an Angular component. * Specifies a template URL for an Angular component.
@ -866,26 +931,26 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* *
* <!-- TODO: what's the url relative to? --> * <!-- TODO: what's the url relative to? -->
*/ */
templateUrl: string; templateUrl?: string;
/** /**
* Specifies an inline template for an Angular component. * Specifies an inline template for an Angular component.
* *
* NOTE: Only one of `templateUrl` or `template` can be defined per View. * NOTE: Only one of `templateUrl` or `template` can be defined per View.
*/ */
template: string; template?: string;
/** /**
* Specifies stylesheet URLs for an Angular component. * Specifies stylesheet URLs for an Angular component.
* *
* <!-- TODO: what's the url relative to? --> * <!-- TODO: what's the url relative to? -->
*/ */
styleUrls: string[]; styleUrls?: string[];
/** /**
* Specifies inline stylesheets for an Angular component. * Specifies inline stylesheets for an Angular component.
*/ */
styles: string[]; styles?: string[];
/** /**
* Animations are defined on components via an animation-like DSL. This DSL approach to describing * Animations are defined on components via an animation-like DSL. This DSL approach to describing
@ -958,7 +1023,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* - {@link animate animate()} * - {@link animate animate()}
* - {@link keyframes keyframes()} * - {@link keyframes keyframes()}
*/ */
animations: AnimationEntryMetadata[]; animations?: AnimationEntryMetadata[];
/** /**
* Specify how the template and the styles should be encapsulated. * Specify how the template and the styles should be encapsulated.
@ -966,9 +1031,9 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* has styles, * has styles,
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}. * otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
*/ */
encapsulation: ViewEncapsulation; encapsulation?: ViewEncapsulation;
interpolation: [string, string]; interpolation?: [string, string];
/** /**
* Defines the components that should be compiled as well when * Defines the components that should be compiled as well when
@ -976,73 +1041,88 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the * Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}. * {@link ComponentFactoryResolver ComponentFactoryResolver}.
*/ */
entryComponents: Array<Type<any>|any[]>; entryComponents?: Array<Type<any>|any[]>;
constructor(
{selector, inputs, outputs, host, exportAs, moduleId, providers, viewProviders,
changeDetection = ChangeDetectionStrategy.Default, queries, templateUrl, template, styleUrls,
styles, animations, encapsulation, interpolation,
entryComponents}: ComponentMetadataType = {}) {
super({
selector: selector,
inputs: inputs,
outputs: outputs,
host: host,
exportAs: exportAs,
providers: providers,
queries: queries
});
this.changeDetection = changeDetection;
this._viewProviders = viewProviders;
this.templateUrl = templateUrl;
this.template = template;
this.styleUrls = styleUrls;
this.styles = styles;
this.encapsulation = encapsulation;
this.moduleId = moduleId;
this.animations = animations;
this.interpolation = interpolation;
this.entryComponents = entryComponents;
}
} }
/** /**
* Interface for creating {@link PipeMetadata} * Component decorator and metadata.
*
* @stable
* @Annotation
*/
export const Component: ComponentMetadataFactory = <ComponentMetadataFactory>makeDecorator(
{
selector: undefined,
inputs: undefined,
outputs: undefined,
host: undefined,
exportAs: undefined,
moduleId: undefined,
providers: undefined,
viewProviders: undefined,
changeDetection: ChangeDetectionStrategy.Default,
queries: undefined,
templateUrl: undefined,
template: undefined,
styleUrls: undefined,
styles: undefined,
animations: undefined,
encapsulation: undefined,
interpolation: undefined,
entryComponents: undefined
},
Directive);
/**
* Type of the Pipe decorator / constructor function.
*
* @stable * @stable
*/ */
export interface PipeMetadataType { export interface PipeMetadataFactory {
name: string; /**
pure?: boolean;
}
/**
* Declare reusable pipe function. * Declare reusable pipe function.
* *
* A "pure" pipe is only re-evaluated when either the input or any of the arguments change. * A "pure" pipe is only re-evaluated when either the input or any of the arguments change.
* *
* When not specified, pipes default to being pure. * When not specified, pipes default to being pure.
*
* ### Example
*
* {@example core/ts/metadata/metadata.ts region='pipe'}
* @stable
*/ */
export class PipeMetadata extends InjectableMetadata implements PipeMetadataType { (obj: Pipe): TypeDecorator;
name: string;
/** @internal */
_pure: boolean;
constructor({name, pure}: PipeMetadataType) { /**
super(); * See the {@link Pipe} decorator.
this.name = name; */
this._pure = pure; new (obj: Pipe): Pipe;
}
get pure(): boolean { return isPresent(this._pure) ? this._pure : true; }
} }
/** /**
* Type of the Pipe metadata.
*
* @stable
*/
export interface Pipe {
name?: string;
pure?: boolean;
}
/**
* Pipe decorator and metadata.
*
* @stable
* @Annotation
*/
export const Pipe: PipeMetadataFactory = <PipeMetadataFactory>makeDecorator({
name: undefined,
pure: true,
});
/**
* Type of the Input decorator / constructor function.
*
* @stable
*/
export interface InputMetadataFactory {
/**
* Declares a data-bound input property. * Declares a data-bound input property.
* *
* Angular automatically updates data-bound properties during change detection. * Angular automatically updates data-bound properties during change detection.
@ -1082,15 +1162,37 @@ export class PipeMetadata extends InjectableMetadata implements PipeMetadataType
* ``` * ```
* @stable * @stable
*/ */
export class InputMetadata { (bindingPropertyName?: string): any;
constructor( new (bindingPropertyName?: string): any;
/**
* Name used when instantiating a component in the template.
*/
public bindingPropertyName?: string) {}
} }
/** /**
* Type of the Input metadata.
*
* @stable
*/
export interface Input {
/**
* Name used when instantiating a component in the template.
*/
bindingPropertyName?: string;
}
/**
* Input decorator and metadata.
*
* @stable
* @Annotation
*/
export const Input: InputMetadataFactory = makePropDecorator([['bindingPropertyName', undefined]]);
/**
* Type of the Output decorator / constructor function.
*
* @stable
*/
export interface OutputMetadataFactory {
/**
* Declares an event-bound output property. * Declares an event-bound output property.
* *
* When an output property emits an event, an event handler attached to that event * When an output property emits an event, an event handler attached to that event
@ -1130,11 +1232,34 @@ export class InputMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class OutputMetadata { (bindingPropertyName?: string): any;
constructor(public bindingPropertyName?: string) {} new (bindingPropertyName?: string): any;
} }
/** /**
* Type of the Output metadata.
*
* @stable
*/
export interface Output { bindingPropertyName?: string; }
/**
* Output decorator and metadata.
*
* @stable
* @Annotation
*/
export const Output: OutputMetadataFactory =
makePropDecorator([['bindingPropertyName', undefined]]);
/**
* Type of the HostBinding decorator / constructor function.
*
* @stable
*/
export interface HostBindingMetadataFactory {
/**
* Declares a host property binding. * Declares a host property binding.
* *
* Angular automatically checks host property bindings during change detection. * Angular automatically checks host property bindings during change detection.
@ -1153,13 +1278,14 @@ export class OutputMetadata {
* @Directive({selector: '[ngModel]'}) * @Directive({selector: '[ngModel]'})
* class NgModelStatus { * class NgModelStatus {
* constructor(public control:NgModel) {} * constructor(public control:NgModel) {}
* @HostBinding('class.valid') get valid { return this.control.valid; } * @HostBinding('class.valid') get valid() { return this.control.valid; }
* @HostBinding('class.invalid') get invalid { return this.control.invalid; } * @HostBinding('class.invalid') get invalid() { return this.control.invalid; }
* } * }
* *
* @Component({ * @Component({
* selector: 'app', * selector: 'app',
* template: `<input [(ngModel)]="prop">` * template: `<input [(ngModel)]="prop">`,
* directives: [FORM_DIRECTIVES, NgModelStatus]
* }) * })
* class App { * class App {
* prop; * prop;
@ -1167,11 +1293,34 @@ export class OutputMetadata {
* ``` * ```
* @stable * @stable
*/ */
export class HostBindingMetadata { (hostPropertyName?: string): any;
constructor(public hostPropertyName?: string) {} new (hostPropertyName?: string): any;
} }
/** /**
* Type of the HostBinding metadata.
*
* @stable
*/
export interface HostBinding { hostPropertyName?: string; }
/**
* HostBinding decorator and metadata.
*
* @stable
* @Annotation
*/
export const HostBinding: HostBindingMetadataFactory =
makePropDecorator([['hostPropertyName', undefined]]);
/**
* Type of the HostListener decorator / constructor function.
*
* @stable
*/
export interface HostListenerMetadataFactory {
/**
* Declares a host listener. * Declares a host listener.
* *
* Angular will invoke the decorated method when the host element emits the specified event. * Angular will invoke the decorated method when the host element emits the specified event.
@ -1197,12 +1346,33 @@ export class HostBindingMetadata {
* *
* @Component({ * @Component({
* selector: 'app', * selector: 'app',
* template: `<button counting>Increment</button>` * template: `<button counting>Increment</button>`,
* directives: [CountClicks]
* }) * })
* class App {} * class App {}
* ``` * ```
* @stable * @stable
* @Annotation
*/ */
export class HostListenerMetadata { (eventName: string, args?: string[]): any;
constructor(public eventName: string, public args?: string[]) {} new (eventName: string, args?: string[]): any;
} }
/**
* Type of the HostListener metadata.
*
* @stable
*/
export interface HostListener {
eventName?: string;
args?: string[];
}
/**
* HostBinding decorator and metadata.
*
* @stable
* @Annotation
*/
export const HostListener: HostListenerMetadataFactory =
makePropDecorator([['eventName', undefined], ['args', []]]);

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {InjectableMetadata, Provider} from '../di'; import {Provider} from '../di';
import {Type} from '../type'; import {Type} from '../type';
import {TypeDecorator, makeDecorator} from '../util/decorators';
/** /**
* A wrapper around a module that also includes the providers. * A wrapper around a module that also includes the providers.
@ -47,26 +48,26 @@ export const NO_ERRORS_SCHEMA: SchemaMetadata = {
name: 'no-errors-schema' name: 'no-errors-schema'
}; };
/** /**
* Interface for creating {@link NgModuleMetadata} * Type of the NgModule decorator / constructor function.
*
* @stable * @stable
*/ */
export interface NgModuleMetadataType { export interface NgModuleMetadataFactory {
providers?: Provider[]; /**
declarations?: Array<Type<any>|any[]>; * Defines an NgModule.
imports?: Array<Type<any>|ModuleWithProviders|any[]>; */
exports?: Array<Type<any>|any[]>; (obj?: NgModule): TypeDecorator;
entryComponents?: Array<Type<any>|any[]>; new (obj?: NgModule): NgModule;
bootstrap?: Array<Type<any>|any[]>;
schemas?: Array<SchemaMetadata|any[]>;
id?: string;
} }
/** /**
* Declares an Angular Module. * Type of the NgModule metadata.
*
* @stable * @stable
*/ */
export class NgModuleMetadata extends InjectableMetadata implements NgModuleMetadataType { export interface NgModule {
/** /**
* Defines the set of injectable objects that are available in the injector * Defines the set of injectable objects that are available in the injector
* of this module. * of this module.
@ -96,9 +97,7 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* } * }
* ``` * ```
*/ */
get providers(): Provider[] { return this._providers; } providers?: Provider[];
private _providers: Provider[];
/** /**
* Specifies a list of directives/pipes that belong to this module. * Specifies a list of directives/pipes that belong to this module.
@ -113,7 +112,7 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* } * }
* ``` * ```
*/ */
declarations: Array<Type<any>|any[]>; declarations?: Array<Type<any>|any[]>;
/** /**
* Specifies a list of modules whose exported directives/pipes * Specifies a list of modules whose exported directives/pipes
@ -130,7 +129,7 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* } * }
* ``` * ```
*/ */
imports: Array<Type<any>|ModuleWithProviders|any[]>; imports?: Array<Type<any>|ModuleWithProviders|any[]>;
/** /**
* Specifies a list of directives/pipes/module that can be used within the template * Specifies a list of directives/pipes/module that can be used within the template
@ -147,7 +146,7 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* } * }
* ``` * ```
*/ */
exports: Array<Type<any>|any[]>; exports?: Array<Type<any>|any[]>;
/** /**
* Defines the components that should be compiled as well when * Defines the components that should be compiled as well when
@ -155,14 +154,14 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the * Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}. * {@link ComponentFactoryResolver ComponentFactoryResolver}.
*/ */
entryComponents: Array<Type<any>|any[]>; entryComponents?: Array<Type<any>|any[]>;
/** /**
* Defines the components that should be bootstrapped when * Defines the components that should be bootstrapped when
* this module is bootstrapped. The components listed here * this module is bootstrapped. The components listed here
* will automatically be added to `entryComponents`. * will automatically be added to `entryComponents`.
*/ */
bootstrap: Array<Type<any>|any[]>; bootstrap?: Array<Type<any>|any[]>;
/** /**
* Elements and properties that are not angular Components nor Directives have to be declared in * Elements and properties that are not angular Components nor Directives have to be declared in
@ -176,26 +175,29 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* @security When using one of `NO_ERRORS_SCHEMA` or `CUSTOM_ELEMENTS_SCHEMA` we're trusting that * @security When using one of `NO_ERRORS_SCHEMA` or `CUSTOM_ELEMENTS_SCHEMA` we're trusting that
* allowed elements (and its properties) securely escape inputs. * allowed elements (and its properties) securely escape inputs.
*/ */
schemas: Array<SchemaMetadata|any[]>; schemas?: Array<SchemaMetadata|any[]>;
/** /**
* An opaque ID for this module, e.g. a name or a path. Used to identify modules in * An opaque ID for this module, e.g. a name or a path. Used to identify modules in
* `getModuleFactory`. If left `undefined`, the `NgModule` will not be registered with * `getModuleFactory`. If left `undefined`, the `NgModule` will not be registered with
* `getModuleFactory`. * `getModuleFactory`.
*/ */
id: string; id?: string;
constructor(options: NgModuleMetadataType = {}) {
// We cannot use destructuring of the constructor argument because `exports` is a
// protected symbol in CommonJS and closure tries to aggressively optimize it away.
super();
this._providers = options.providers;
this.declarations = options.declarations;
this.imports = options.imports;
this.exports = options.exports;
this.entryComponents = options.entryComponents;
this.bootstrap = options.bootstrap;
this.schemas = options.schemas;
this.id = options.id;
}
} }
/**
* NgModule decorator and metadata
*
* @stable
* @Annotation
*/
export const NgModule: NgModuleMetadataFactory = <NgModuleMetadataFactory>makeDecorator({
providers: undefined,
declarations: undefined,
imports: undefined,
exports: undefined,
entryComponents: undefined,
bootstrap: undefined,
schemas: undefined,
id: undefined,
});

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '../di/decorators'; import {Injectable} from '../di';
import {Map, MapWrapper} from '../facade/collection'; import {Map, MapWrapper} from '../facade/collection';
import {scheduleMicroTask} from '../facade/lang'; import {scheduleMicroTask} from '../facade/lang';
import {NgZone} from '../zone/ng_zone'; import {NgZone} from '../zone/ng_zone';

View File

@ -251,8 +251,11 @@ export function Class(clsDef: ClassDefinition): Type<any> {
var Reflect = global.Reflect; var Reflect = global.Reflect;
export function makeDecorator(annotationCls: any, chainFn: (fn: Function) => void = null): export function makeDecorator(
(...args: any[]) => (cls: any) => any { props: {[key: string]: any}, parentClass?: any,
chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {
const annotationCls = makeMetadataClass([props], parentClass);
function DecoratorFactory(objOrType: any): (cls: any) => any { function DecoratorFactory(objOrType: any): (cls: any) => any {
if (!(Reflect && Reflect.getMetadata)) { if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators'; throw 'reflect-metadata shim is required when using class decorators';
@ -277,22 +280,49 @@ export function makeDecorator(annotationCls: any, chainFn: (fn: Function) => voi
return TypeDecorator; return TypeDecorator;
} }
} }
DecoratorFactory.prototype = Object.create(annotationCls.prototype); DecoratorFactory.prototype = annotationCls.prototype;
(<any>DecoratorFactory).annotationCls = annotationCls; (<any>DecoratorFactory).annotationCls = annotationCls;
return DecoratorFactory; return DecoratorFactory;
} }
export function makeParamDecorator(annotationCls: any): any { function makeMetadataClass(
function ParamDecoratorFactory(...args: any[]): any { props: ([string, any] | {[key: string]: any})[], parentClass?: any): any {
var annotationInstance = Object.create(annotationCls.prototype); function ctor(...args: any[]) {
annotationCls.apply(annotationInstance, args); props.forEach((prop, i) => {
if (this instanceof annotationCls) { const argVal = args[i];
return annotationInstance; if (Array.isArray(prop)) {
// plain parameter
const val = !argVal || argVal === undefined ? prop[1] : argVal;
this[prop[0]] = val;
} else { } else {
(<any>ParamDecorator).annotation = annotationInstance; for (let propName in prop) {
return ParamDecorator; const val = !argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName];
this[propName] = val;
}
}
});
} }
if (parentClass) {
ctor.prototype = Object.create(parentClass.prototype);
}
return ctor;
}
export function makeParamDecorator(
props: ([string, any] | {[key: string]: any})[], parentClass?: any): any {
const annotationCls = makeMetadataClass(props, parentClass);
function ParamDecoratorFactory(...args: any[]): any {
let annotationInstance = Object.create(annotationCls.prototype);
annotationCls.apply(annotationInstance, args);
if (this instanceof annotationCls) {
return annotationInstance;
}
(<any>ParamDecorator).annotation = annotationInstance;
return ParamDecorator;
function ParamDecorator(cls: any, unusedKey: any, index: number): any { function ParamDecorator(cls: any, unusedKey: any, index: number): any {
const parameters: any[][] = Reflect.getMetadata('parameters', cls) || []; const parameters: any[][] = Reflect.getMetadata('parameters', cls) || [];
@ -311,12 +341,14 @@ export function makeParamDecorator(annotationCls: any): any {
return cls; return cls;
} }
} }
ParamDecoratorFactory.prototype = Object.create(annotationCls.prototype); ParamDecoratorFactory.prototype = annotationCls.prototype;
(<any>ParamDecoratorFactory).annotationCls = annotationCls; (<any>ParamDecoratorFactory).annotationCls = annotationCls;
return ParamDecoratorFactory; return ParamDecoratorFactory;
} }
export function makePropDecorator(annotationCls: any): any { export function makePropDecorator(
props: ([string, any] | {[key: string]: any})[], parentClass?: any): any {
const annotationCls = makeMetadataClass(props, parentClass);
function PropDecoratorFactory(...args: any[]): any { function PropDecoratorFactory(...args: any[]): any {
var decoratorInstance = Object.create(annotationCls.prototype); var decoratorInstance = Object.create(annotationCls.prototype);
annotationCls.apply(decoratorInstance, args); annotationCls.apply(decoratorInstance, args);
@ -332,7 +364,7 @@ export function makePropDecorator(annotationCls: any): any {
}; };
} }
} }
PropDecoratorFactory.prototype = Object.create(annotationCls.prototype); PropDecoratorFactory.prototype = annotationCls.prototype;
(<any>PropDecoratorFactory).annotationCls = annotationCls; (<any>PropDecoratorFactory).annotationCls = annotationCls;
return PropDecoratorFactory; return PropDecoratorFactory;
} }

View File

@ -7,15 +7,12 @@
*/ */
import {Inject, InjectMetadata, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, SelfMetadata, forwardRef} from '@angular/core'; import {Inject, InjectMetadata, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, SelfMetadata, forwardRef} from '@angular/core';
import {DependencyMetadata} from '@angular/core/src/di/metadata';
import {ReflectiveInjectorDynamicStrategy, ReflectiveInjectorInlineStrategy, ReflectiveInjector_, ReflectiveProtoInjector} from '@angular/core/src/di/reflective_injector'; import {ReflectiveInjectorDynamicStrategy, ReflectiveInjectorInlineStrategy, ReflectiveInjector_, ReflectiveProtoInjector} from '@angular/core/src/di/reflective_injector';
import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider'; import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {expect} from '@angular/platform-browser/testing/matchers'; import {expect} from '@angular/platform-browser/testing/matchers';
import {isBlank, isPresent, stringify} from '../../src/facade/lang'; import {isBlank, isPresent, stringify} from '../../src/facade/lang';
class CustomDependencyMetadata extends DependencyMetadata {}
class Engine {} class Engine {}
class BrokenEngine { class BrokenEngine {
@ -539,15 +536,12 @@ export function main() {
var providers = ReflectiveInjector.resolve([{ var providers = ReflectiveInjector.resolve([{
provide: 'token', provide: 'token',
useFactory: (e: any /** TODO #9100 */) => 'result', useFactory: (e: any /** TODO #9100 */) => 'result',
deps: [[new InjectMetadata('dep'), new CustomDependencyMetadata()]] deps: [[new InjectMetadata('dep')]]
}]); }]);
var provider = providers[0]; var provider = providers[0];
expect(provider.resolvedFactories[0].dependencies[0].key.token).toEqual('dep'); expect(provider.resolvedFactories[0].dependencies[0].key.token).toEqual('dep');
expect(provider.resolvedFactories[0].dependencies[0].properties).toEqual([
new CustomDependencyMetadata()
]);
}); });
it('should allow declaring dependencies with flat arrays', () => { it('should allow declaring dependencies with flat arrays', () => {

View File

@ -8,33 +8,33 @@
import {makeDecorator, makeParamDecorator, makePropDecorator} from '@angular/core/src/util/decorators'; import {makeDecorator, makeParamDecorator, makePropDecorator} from '@angular/core/src/util/decorators';
export class ClassDecoratorMeta { export interface ClassDecoratorFactory {
constructor(public value: any /** TODO #9100 */) {} (data: ClassDecorator): any;
new (data: ClassDecorator): ClassDecorator;
} }
export class ParamDecoratorMeta { export interface ClassDecorator { value: any; }
constructor(public value: any /** TODO #9100 */) {}
}
export class PropDecoratorMeta { export interface ParamDecorator { value: any; }
constructor(public value: any /** TODO #9100 */) {}
} export interface PropDecorator { value: any; }
export function classDecorator(value: any /** TODO #9100 */) { export function classDecorator(value: any /** TODO #9100 */) {
return new ClassDecoratorMeta(value); return new ClassDecorator({value: value});
} }
export function paramDecorator(value: any /** TODO #9100 */) { export function paramDecorator(value: any /** TODO #9100 */) {
return new ParamDecoratorMeta(value); return new ParamDecorator(value);
} }
export function propDecorator(value: any /** TODO #9100 */) { export function propDecorator(value: any /** TODO #9100 */) {
return new PropDecoratorMeta(value); return new PropDecorator(value);
} }
/** @Annotation */ export var ClassDecorator = makeDecorator(ClassDecoratorMeta); /** @Annotation */ export const ClassDecorator =
/** @Annotation */ export var ParamDecorator = makeParamDecorator(ParamDecoratorMeta); <ClassDecoratorFactory>makeDecorator({value: undefined});
/** @Annotation */ export var PropDecorator = makePropDecorator(PropDecoratorMeta); /** @Annotation */ export const ParamDecorator = makeParamDecorator([['value', undefined]]);
/** @Annotation */ export const PropDecorator = makePropDecorator([['value', undefined]]);
// used only in Dart // used only in Dart
export class HasGetterAndSetterDecorators {} export class HasGetterAndSetterDecorators {}

View File

@ -20,7 +20,7 @@ class AType {
constructor(value: any /** TODO #9100 */) { this.value = value; } constructor(value: any /** TODO #9100 */) { this.value = value; }
} }
@ClassDecorator('class') @ClassDecorator({value: 'class'})
class ClassWithDecorators { class ClassWithDecorators {
@PropDecorator('p1') @PropDecorator('p2') a: any /** TODO #9100 */; @PropDecorator('p1') @PropDecorator('p2') a: any /** TODO #9100 */;
b: any /** TODO #9100 */; b: any /** TODO #9100 */;

View File

@ -13,44 +13,37 @@ import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@an
import {global} from '../../src/facade/lang'; import {global} from '../../src/facade/lang';
class TestAnnotation {
constructor(public arg: any) {}
}
class TerminalAnnotation {
terminal = true;
}
class DecoratedParent {} class DecoratedParent {}
class DecoratedChild extends DecoratedParent {} class DecoratedChild extends DecoratedParent {}
export function main() { export function main() {
var Reflect = global.Reflect; var Reflect = global.Reflect;
var TerminalDecorator = makeDecorator(TerminalAnnotation); var TerminalDecorator = makeDecorator({terminal: true});
var TestDecorator = makeDecorator(TestAnnotation, (fn: any) => fn.Terminal = TerminalDecorator); var TestDecorator =
makeDecorator({marker: undefined}, Object, (fn: any) => fn.Terminal = TerminalDecorator);
describe('decorators', () => { describe('decorators', () => {
it('should invoke as decorator', () => { it('should invoke as decorator', () => {
function Type() {} function Type() {}
TestDecorator({marker: 'WORKS'})(Type); TestDecorator({marker: 'WORKS'})(Type);
var annotations = Reflect.getMetadata('annotations', Type); var annotations = Reflect.getMetadata('annotations', Type);
expect(annotations[0].arg.marker).toEqual('WORKS'); expect(annotations[0].marker).toEqual('WORKS');
}); });
it('should invoke as new', () => { it('should invoke as new', () => {
var annotation = new (<any>TestDecorator)({marker: 'WORKS'}); var annotation = new (<any>TestDecorator)({marker: 'WORKS'});
expect(annotation instanceof TestAnnotation).toEqual(true); expect(annotation instanceof TestDecorator).toEqual(true);
expect(annotation.arg.marker).toEqual('WORKS'); expect(annotation.marker).toEqual('WORKS');
}); });
it('should invoke as chain', () => { it('should invoke as chain', () => {
var chain: any = TestDecorator({marker: 'WORKS'}); var chain: any = TestDecorator({marker: 'WORKS'});
expect(typeof chain.Terminal).toEqual('function'); expect(typeof chain.Terminal).toEqual('function');
chain = chain.Terminal(); chain = chain.Terminal();
expect(chain.annotations[0] instanceof TestAnnotation).toEqual(true); expect(chain.annotations[0] instanceof TestDecorator).toEqual(true);
expect(chain.annotations[0].arg.marker).toEqual('WORKS'); expect(chain.annotations[0].marker).toEqual('WORKS');
expect(chain.annotations[1] instanceof TerminalAnnotation).toEqual(true); expect(chain.annotations[1] instanceof TerminalDecorator).toEqual(true);
}); });
it('should not apply decorators from the prototype chain', function() { it('should not apply decorators from the prototype chain', function() {
@ -59,13 +52,13 @@ export function main() {
var annotations = Reflect.getOwnMetadata('annotations', DecoratedChild); var annotations = Reflect.getOwnMetadata('annotations', DecoratedChild);
expect(annotations.length).toBe(1); expect(annotations.length).toBe(1);
expect(annotations[0].arg.marker).toEqual('child'); expect(annotations[0].marker).toEqual('child');
}); });
describe('Class', () => { describe('Class', () => {
it('should create a class', () => { it('should create a class', () => {
var i0: any /** TODO #9100 */, i1: any /** TODO #9100 */; var i0: any /** TODO #9100 */, i1: any /** TODO #9100 */;
var MyClass = (<any>TestDecorator('test-works')).Class(<any>{ var MyClass = (<any>TestDecorator({marker: 'test-works'})).Class(<any>{
extends: Class(<any>{ extends: Class(<any>{
constructor: function() {}, constructor: function() {},
extendWorks: function() { return 'extend ' + this.arg; } extendWorks: function() { return 'extend ' + this.arg; }
@ -89,7 +82,7 @@ export function main() {
expect(proto.extends).toEqual(undefined); expect(proto.extends).toEqual(undefined);
expect(proto.prototype).toEqual(undefined); expect(proto.prototype).toEqual(undefined);
expect(reflector.annotations(MyClass)[0].arg).toEqual('test-works'); expect(reflector.annotations(MyClass)[0].marker).toEqual('test-works');
}); });
describe('errors', () => { describe('errors', () => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompilerOptions, ComponentMetadataType, DirectiveMetadataType, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleMetadataType, NgModuleRef, NgZone, OpaqueToken, PipeMetadataType, PlatformRef, Provider, SchemaMetadata, Type} from '@angular/core'; import {CompilerOptions, Component, Directive, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, OpaqueToken, Pipe, PlatformRef, Provider, SchemaMetadata, Type} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer'; import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture'; import {ComponentFixture} from './component_fixture';
import {ListWrapper} from './facade/collection'; import {ListWrapper} from './facade/collection';
@ -104,26 +104,24 @@ export class TestBed implements Injector {
*/ */
static compileComponents(): Promise<any> { return getTestBed().compileComponents(); } static compileComponents(): Promise<any> { return getTestBed().compileComponents(); }
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModuleMetadataType>): static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed {
typeof TestBed {
getTestBed().overrideModule(ngModule, override); getTestBed().overrideModule(ngModule, override);
return TestBed; return TestBed;
} }
static overrideComponent(component: Type<any>, override: MetadataOverride<ComponentMetadataType>): static overrideComponent(component: Type<any>, override: MetadataOverride<Component>):
typeof TestBed { typeof TestBed {
getTestBed().overrideComponent(component, override); getTestBed().overrideComponent(component, override);
return TestBed; return TestBed;
} }
static overrideDirective(directive: Type<any>, override: MetadataOverride<DirectiveMetadataType>): static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>):
typeof TestBed { typeof TestBed {
getTestBed().overrideDirective(directive, override); getTestBed().overrideDirective(directive, override);
return TestBed; return TestBed;
} }
static overridePipe(pipe: Type<any>, override: MetadataOverride<PipeMetadataType>): static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed {
typeof TestBed {
getTestBed().overridePipe(pipe, override); getTestBed().overridePipe(pipe, override);
return TestBed; return TestBed;
} }
@ -144,10 +142,10 @@ export class TestBed implements Injector {
private _compilerOptions: CompilerOptions[] = []; private _compilerOptions: CompilerOptions[] = [];
private _moduleOverrides: [Type<any>, MetadataOverride<NgModuleMetadataType>][] = []; private _moduleOverrides: [Type<any>, MetadataOverride<NgModule>][] = [];
private _componentOverrides: [Type<any>, MetadataOverride<ComponentMetadataType>][] = []; private _componentOverrides: [Type<any>, MetadataOverride<Component>][] = [];
private _directiveOverrides: [Type<any>, MetadataOverride<DirectiveMetadataType>][] = []; private _directiveOverrides: [Type<any>, MetadataOverride<Directive>][] = [];
private _pipeOverrides: [Type<any>, MetadataOverride<PipeMetadataType>][] = []; private _pipeOverrides: [Type<any>, MetadataOverride<Pipe>][] = [];
private _providers: Provider[] = []; private _providers: Provider[] = [];
private _declarations: Array<Type<any>|any[]|any> = []; private _declarations: Array<Type<any>|any[]|any> = [];
@ -316,22 +314,22 @@ export class TestBed implements Injector {
return FunctionWrapper.apply(fn, params); return FunctionWrapper.apply(fn, params);
} }
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModuleMetadataType>): void { overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
this._assertNotInstantiated('overrideModule', 'override module metadata'); this._assertNotInstantiated('overrideModule', 'override module metadata');
this._moduleOverrides.push([ngModule, override]); this._moduleOverrides.push([ngModule, override]);
} }
overrideComponent(component: Type<any>, override: MetadataOverride<ComponentMetadataType>): void { overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
this._assertNotInstantiated('overrideComponent', 'override component metadata'); this._assertNotInstantiated('overrideComponent', 'override component metadata');
this._componentOverrides.push([component, override]); this._componentOverrides.push([component, override]);
} }
overrideDirective(directive: Type<any>, override: MetadataOverride<DirectiveMetadataType>): void { overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
this._assertNotInstantiated('overrideDirective', 'override directive metadata'); this._assertNotInstantiated('overrideDirective', 'override directive metadata');
this._directiveOverrides.push([directive, override]); this._directiveOverrides.push([directive, override]);
} }
overridePipe(pipe: Type<any>, override: MetadataOverride<PipeMetadataType>): void { overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
this._assertNotInstantiated('overridePipe', 'override pipe metadata'); this._assertNotInstantiated('overridePipe', 'override pipe metadata');
this._pipeOverrides.push([pipe, override]); this._pipeOverrides.push([pipe, override]);
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Compiler, CompilerOptions, ComponentMetadataType, DirectiveMetadataType, Injector, NgModuleMetadataType, PipeMetadataType, Type} from '@angular/core'; import {Compiler, CompilerOptions, Component, Directive, Injector, NgModule, Pipe, Type} from '@angular/core';
import {unimplemented} from './facade/errors'; import {unimplemented} from './facade/errors';
import {MetadataOverride} from './metadata_override'; import {MetadataOverride} from './metadata_override';
@ -19,18 +19,16 @@ import {MetadataOverride} from './metadata_override';
*/ */
export class TestingCompiler extends Compiler { export class TestingCompiler extends Compiler {
get injector(): Injector { throw unimplemented(); } get injector(): Injector { throw unimplemented(); }
overrideModule(module: Type<any>, overrides: MetadataOverride<NgModuleMetadataType>): void { overrideModule(module: Type<any>, overrides: MetadataOverride<NgModule>): void {
throw unimplemented(); throw unimplemented();
} }
overrideDirective(directive: Type<any>, overrides: MetadataOverride<DirectiveMetadataType>): overrideDirective(directive: Type<any>, overrides: MetadataOverride<Directive>): void {
void {
throw unimplemented(); throw unimplemented();
} }
overrideComponent(component: Type<any>, overrides: MetadataOverride<ComponentMetadataType>): overrideComponent(component: Type<any>, overrides: MetadataOverride<Component>): void {
void {
throw unimplemented(); throw unimplemented();
} }
overridePipe(directive: Type<any>, overrides: MetadataOverride<PipeMetadataType>): void { overridePipe(directive: Type<any>, overrides: MetadataOverride<Pipe>): void {
throw unimplemented(); throw unimplemented();
} }
} }