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
*/
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';
@ -169,16 +169,11 @@ export class StaticReflector implements ReflectorReader {
}
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => {
var metadata = Object.create(ctor.prototype);
ctor.apply(metadata, args);
return metadata;
});
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
}
private registerFunction(type: StaticSymbol, fn: any): void {
this.conversionMap.set(
type, (context: StaticSymbol, args: any[]) => { return fn.apply(undefined, args); });
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
}
private initializeConversionMap(): void {

View File

@ -431,7 +431,7 @@ describe('StaticReflector', () => {
const annotations = reflector.annotations(
host.getStaticSymbol('/tmp/src/static-method-ref.ts', 'MethodReference'));
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);
}
} else if (a instanceof OutputMetadata) {
if (isPresent(a.bindingPropertyName)) {
outputs.push(`${propName}: ${a.bindingPropertyName}`);
const output: OutputMetadata = a;
if (isPresent(output.bindingPropertyName)) {
outputs.push(`${propName}: ${output.bindingPropertyName}`);
} else {
outputs.push(propName);
}
} else if (a instanceof HostBindingMetadata) {
if (isPresent(a.hostPropertyName)) {
host[`[${a.hostPropertyName}]`] = propName;
const hostBinding: HostBindingMetadata = a;
if (isPresent(hostBinding.hostPropertyName)) {
host[`[${hostBinding.hostPropertyName}]`] = propName;
} else {
host[`[${propName}]`] = propName;
}
} else if (a instanceof HostListenerMetadata) {
var args = isPresent(a.args) ? (<any[]>a.args).join(', ') : '';
host[`(${a.eventName})`] = `${propName}(${args})`;
const hostListener: HostListenerMetadata = a;
var args = isPresent(hostListener.args) ? (<any[]>hostListener.args).join(', ') : '';
host[`(${hostListener.eventName})`] = `${propName}(${args})`;
} else if (a instanceof QueryMetadata) {
queries[propName] = a;
}

View File

@ -6,14 +6,14 @@
* 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 {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
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 {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver';
@ -484,7 +484,7 @@ export class CompileMetadataResolver {
let isSkipSelf = false;
let isOptional = false;
let query: QueryMetadata = null;
let viewQuery: ViewQueryMetadata = null;
let viewQuery: QueryMetadata = null;
var token: any = null;
if (isArray(param)) {
(<any[]>param).forEach((paramEntry) => {
@ -664,11 +664,16 @@ export class CompileMetadataResolver {
return res;
}
private _queryVarBindings(selector: any): string[] {
return StringWrapper.split(selector, /\s*,\s*/g);
}
getQueryMetadata(q: QueryMetadata, propertyName: string, typeOrFunc: Type<any>|Function):
cpl.CompileQueryMetadata {
var selectors: cpl.CompileTokenMetadata[];
if (q.isVarBindingQuery) {
selectors = q.varBindings.map(varName => this.getTokenMetadata(varName));
if (isString(q.selector)) {
selectors = this._queryVarBindings(q.selector).map(varName => this.getTokenMetadata(varName));
} else {
if (!isPresent(q.selector)) {
throw new Error(

View File

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

View File

@ -26,7 +26,7 @@ export * from './directive_resolver_mock';
export * from './ng_module_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 {TestingCompilerFactory, TestingCompiler} from './private_import_core';
import {platformCoreDynamic, RuntimeCompiler, DirectiveResolver, NgModuleResolver, PipeResolver} from '@angular/compiler';
@ -70,25 +70,24 @@ export class TestingCompilerImpl implements TestingCompiler {
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);
this._moduleResolver.setNgModule(
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);
this._directiveResolver.setDirective(
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);
this._directiveResolver.setDirective(
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);
this._pipeResolver.setPipe(
pipe, this._overrider.overrideMetadata(PipeMetadata, oldMetadata, override));
this._pipeResolver.setPipe(pipe, this._overrider.overrideMetadata(Pipe, oldMetadata, override));
}
clearCache(): void { this._compiler.clearCache(); }
clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); }

View File

@ -6,7 +6,7 @@
* 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';
@Injectable()

View File

@ -12,11 +12,7 @@
* The `di` module provides dependency injection container services.
*/
export {HostMetadata, InjectMetadata, InjectableMetadata, OptionalMetadata, SelfMetadata, SkipSelfMetadata} from './di/metadata';
// we have to reexport * because Dart and TS export two different sets of types
export * from './di/decorators';
export * from './di/metadata';
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,239 +7,369 @@
*/
import {stringify} from '../facade/lang';
import {makeParamDecorator} from '../util/decorators';
/**
* A parameter metadata that specifies a dependency.
* Type of the Inject decorator / constructor function.
*
* ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview))
*
* ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* engine;
* constructor(@Inject("MyEngine") engine:Engine) {
* this.engine = engine;
* }
* }
*
* var injector = Injector.resolveAndCreate([
* {provide: "MyEngine", useClass: Engine},
* Car
* ]);
*
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
* ```
*
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the parameter.
*
* ### Example
*
* ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* constructor(public engine: Engine) {} //same as constructor(@Inject(Engine) engine:Engine)
* }
*
* var injector = Injector.resolveAndCreate([Engine, Car]);
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
* ```
* @stable
*/
export class InjectMetadata {
constructor(public token: any) {}
toString(): string { return `@Inject(${stringify(this.token)})`; }
export interface InjectMetadataFactory {
/**
* A parameter metadata that specifies a dependency.
*
* ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview))
*
* ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* engine;
* constructor(@Inject("MyEngine") engine:Engine) {
* this.engine = engine;
* }
* }
*
* var injector = Injector.resolveAndCreate([
* {provide: "MyEngine", useClass: Engine},
* Car
* ]);
*
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
* ```
*
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the
* parameter.
*
* ### Example
*
* ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* constructor(public engine: Engine) {} //same as constructor(@Inject(Engine) engine:Engine)
* }
*
* var injector = Injector.resolveAndCreate([Engine, Car]);
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
* ```
* @stable
*/
(token: any): any;
new (token: any): Inject;
}
/**
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if
* the dependency is not found.
* Type of the Inject metadata.
*
* ### Example ([live demo](http://plnkr.co/edit/AsryOm?p=preview))
*
* ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* engine;
* constructor(@Optional() engine:Engine) {
* this.engine = engine;
* }
* }
*
* var injector = Injector.resolveAndCreate([Car]);
* expect(injector.get(Car).engine).toBeNull();
* ```
* @stable
*/
export class OptionalMetadata {
toString(): string { return `@Optional()`; }
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
* the dependency is not found.
*
* ### Example ([live demo](http://plnkr.co/edit/AsryOm?p=preview))
*
* ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* engine;
* constructor(@Optional() engine:Engine) {
* this.engine = engine;
* }
* }
*
* var injector = Injector.resolveAndCreate([Car]);
* expect(injector.get(Car).engine).toBeNull();
* ```
* @stable
*/
(): any;
new (): Optional;
}
/**
* `DependencyMetadata` is used by the framework to extend DI.
* This is internal to Angular and should not be used directly.
* Type of the Optional metadata.
*
* @stable
*/
export class DependencyMetadata {
get token(): any { return null; }
export interface Optional {}
/**
* 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.
*
* ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview))
*
* ```typescript
* @Injectable()
* class UsefulService {}
*
* @Injectable()
* class NeedsService {
* constructor(public service:UsefulService) {}
* }
*
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
* expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true);
* ```
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
* does not have `@Injectable` marker, as shown in the example below.
*
* ```typescript
* class UsefulService {}
*
* class NeedsService {
* constructor(public service:UsefulService) {}
* }
*
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
* expect(() => injector.get(NeedsService)).toThrowError();
* ```
* @stable
*/
(): any;
new (): Injectable;
}
/**
* A marker metadata that marks a class as available to {@link Injector} for creation.
* Type of the Injectable metadata.
*
* ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview))
*
* ```typescript
* @Injectable()
* class UsefulService {}
*
* @Injectable()
* class NeedsService {
* constructor(public service:UsefulService) {}
* }
*
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
* expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true);
* ```
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
* does not have `@Injectable` marker, as shown in the example below.
*
* ```typescript
* class UsefulService {}
*
* class NeedsService {
* constructor(public service:UsefulService) {}
* }
*
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
* expect(() => injector.get(NeedsService)).toThrowError();
* ```
* @stable
*/
export class InjectableMetadata {
constructor() {}
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.
*
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
*
* ```typescript
* class Dependency {
* }
*
* @Injectable()
* class NeedsDependency {
* dependency;
* constructor(@Self() dependency:Dependency) {
* this.dependency = dependency;
* }
* }
*
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
* var nd = inj.get(NeedsDependency);
*
* expect(nd.dependency instanceof Dependency).toBe(true);
*
* var inj = Injector.resolveAndCreate([Dependency]);
* var child = inj.resolveAndCreateChild([NeedsDependency]);
* expect(() => child.get(NeedsDependency)).toThrowError();
* ```
* @stable
*/
(): any;
new (): Self;
}
/**
* Specifies that an {@link Injector} should retrieve a dependency only from itself.
* Type of the Self metadata.
*
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
*
* ```typescript
* class Dependency {
* }
*
* @Injectable()
* class NeedsDependency {
* dependency;
* constructor(@Self() dependency:Dependency) {
* this.dependency = dependency;
* }
* }
*
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
* var nd = inj.get(NeedsDependency);
*
* expect(nd.dependency instanceof Dependency).toBe(true);
*
* var inj = Injector.resolveAndCreate([Dependency]);
* var child = inj.resolveAndCreateChild([NeedsDependency]);
* expect(() => child.get(NeedsDependency)).toThrowError();
* ```
* @stable
*/
export class SelfMetadata {
toString(): string { return `@Self()`; }
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.
*
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
*
* ```typescript
* class Dependency {
* }
*
* @Injectable()
* class NeedsDependency {
* dependency;
* constructor(@SkipSelf() dependency:Dependency) {
* this.dependency = dependency;
* }
* }
*
* var parent = Injector.resolveAndCreate([Dependency]);
* var child = parent.resolveAndCreateChild([NeedsDependency]);
* expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);
*
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
* expect(() => inj.get(NeedsDependency)).toThrowError();
* ```
* @stable
*/
(): any;
new (): SkipSelf;
}
/**
* Specifies that the dependency resolution should start from the parent injector.
* Type of the SkipSelf metadata.
*
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
*
* ```typescript
* class Dependency {
* }
*
* @Injectable()
* class NeedsDependency {
* dependency;
* constructor(@SkipSelf() dependency:Dependency) {
* this.dependency = dependency;
* }
* }
*
* var parent = Injector.resolveAndCreate([Dependency]);
* var child = parent.resolveAndCreateChild([NeedsDependency]);
* expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);
*
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
* expect(() => inj.get(NeedsDependency)).toThrowError();
* ```
* @stable
*/
export class SkipSelfMetadata {
toString(): string { return `@SkipSelf()`; }
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
* closest host.
*
* In Angular, a component element is automatically declared as a host for all the injectors in
* its view.
*
* ### Example ([live demo](http://plnkr.co/edit/GX79pV?p=preview))
*
* In the following example `App` contains `ParentCmp`, which contains `ChildDirective`.
* So `ParentCmp` is the host of `ChildDirective`.
*
* `ChildDirective` depends on two services: `HostService` and `OtherService`.
* `HostService` is defined at `ParentCmp`, and `OtherService` is defined at `App`.
*
*```typescript
* class OtherService {}
* class HostService {}
*
* @Directive({
* selector: 'child-directive'
* })
* class ChildDirective {
* constructor(@Optional() @Host() os:OtherService, @Optional() @Host() hs:HostService){
* console.log("os is null", os);
* console.log("hs is NOT null", hs);
* }
* }
*
* @Component({
* selector: 'parent-cmp',
* providers: [HostService],
* template: `
* Dir: <child-directive></child-directive>
* `,
* directives: [ChildDirective]
* })
* class ParentCmp {
* }
*
* @Component({
* selector: 'app',
* providers: [OtherService],
* template: `
* Parent: <parent-cmp></parent-cmp>
* `,
* directives: [ParentCmp]
* })
* class App {
* }
*```
* @stable
*/
(): any;
new (): Host;
}
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* closest host.
* Type of the Host metadata.
*
* In Angular, a component element is automatically declared as a host for all the injectors in
* its view.
*
* ### Example ([live demo](http://plnkr.co/edit/GX79pV?p=preview))
*
* In the following example `App` contains `ParentCmp`, which contains `ChildDirective`.
* So `ParentCmp` is the host of `ChildDirective`.
*
* `ChildDirective` depends on two services: `HostService` and `OtherService`.
* `HostService` is defined at `ParentCmp`, and `OtherService` is defined at `App`.
*
*```typescript
* class OtherService {}
* class HostService {}
*
* @Directive({
* selector: 'child-directive'
* })
* class ChildDirective {
* constructor(@Optional() @Host() os:OtherService, @Optional() @Host() hs:HostService){
* console.log("os is null", os);
* console.log("hs is NOT null", hs);
* }
* }
*
* @Component({
* selector: 'parent-cmp',
* providers: [HostService],
* template: `
* Dir: <child-directive></child-directive>
* `,
* directives: [ChildDirective]
* })
* class ParentCmp {
* }
*
* @Component({
* selector: 'app',
* providers: [OtherService],
* template: `
* Parent: <parent-cmp></parent-cmp>
* `,
* directives: [ParentCmp]
* })
* class App {
* }
*```
* @stable
*/
export class HostMetadata {
toString(): string { return `@Host()`; }
}
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
*/
import {Injectable} from './decorators';
import {Injectable} from './metadata';
/**
* 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 {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 {InvalidProviderError, MixingMultiProvidersWithRegularProvidersError, NoAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key';
@ -256,12 +256,6 @@ function _extractToken(
} else if (paramMetadata instanceof SkipSelfMetadata) {
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 {devModeEqual} from '../change_detection/change_detection';
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 {isBlank, isPresent, looseIdentical} from '../facade/lang';
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 {DependencyMetadata} from '../di/metadata';
import {OpaqueToken} from '../di/opaque_token';
import {StringWrapper, isString, stringify} from '../facade/lang';
import {Type} from '../type';
import {makeParamDecorator, makePropDecorator} from '../util/decorators';
/**
* 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');
/**
* Type of the Attribute decorator / constructor function.
*
* @stable
*/
export interface AttributeMetadataFactory {
/**
* Specifies that a constant attribute value should be injected.
*
* The directive can inject constant string literals of host element attributes.
@ -64,278 +71,199 @@ export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComp
* A decorator can inject string literal `text` like so:
*
* {@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
*/
export class AttributeMetadata extends DependencyMetadata {
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)})`; }
*/ (name: string): any;
new (name: string): Attribute;
}
/**
* Declares an injectable parameter to be a live list of directives or variable
* bindings from the content children of a directive.
* Type of the Attribute metadata.
*
* ### 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
*/
export class QueryMetadata extends DependencyMetadata {
/**
* whether we want to query only direct children (false) or all
* children (true).
*/
export interface Attribute { attributeName?: string; }
/**
* Attribute decorator and metadata.
*
* @stable
* @Annotation
*/
export const Attribute: AttributeMetadataFactory =
makeParamDecorator([['attributeName', undefined]]);
/**
* Type of the Query metadata.
*
* @stable
*/
export interface Query {
descendants: boolean;
first: boolean;
/**
* The DI token to read from an element that matches the selector.
*/
read: any;
constructor(
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)})`; }
isViewQuery: boolean;
selector: any;
}
// TODO: add an example after ContentChildren and ViewChildren are in master
export abstract class Query {}
/**
* Configures a content query.
* Type of the ContentChildren decorator / constructor function.
*
* Content queries are set before the `ngAfterContentInit` callback is called.
*
* ### Example
*
* ```
* @Directive({
* selector: 'someDir'
* })
* class SomeDir {
* @ContentChildren(ChildDirective) contentChildren: QueryList<ChildDirective>;
*
* ngAfterContentInit() {
* // contentChildren is set
* }
* }
* ```
* @stable
*/
export class ContentChildrenMetadata extends QueryMetadata {
constructor(
_selector: Type<any>|string,
{descendants = false, read = null}: {descendants?: boolean, read?: any} = {}) {
super(_selector, {descendants: descendants, read: read});
}
}
// TODO: add an example after ContentChild and ViewChild are in master
/**
* Configures a content query.
*
* Content queries are set before the `ngAfterContentInit` callback is called.
*
* ### Example
*
* ```
* @Directive({
* selector: 'someDir'
* })
* class SomeDir {
* @ContentChild(ChildDirective) contentChild;
*
* ngAfterContentInit() {
* // contentChild is set
* }
* }
* ```
* @stable
*/
export class ContentChildMetadata extends QueryMetadata {
constructor(_selector: Type<any>|string, {read = null}: {read?: any} = {}) {
super(_selector, {descendants: true, first: true, read: read});
}
}
/**
* Similar to {@link ContentChildMetadata}, but querying the component view, instead
* 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
*/
export class ViewQueryMetadata extends QueryMetadata {
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});
}
export interface ContentChildrenMetadataFactory {
/**
* always `true` to differentiate it with {@link QueryMetadata}.
* Configures a content query.
*
* Content queries are set before the `ngAfterContentInit` callback is called.
*
* ### Example
*
* ```
* @Directive({
* selector: 'someDir'
* })
* class SomeDir {
* @ContentChildren(ChildDirective) contentChildren: QueryList<ChildDirective>;
*
* ngAfterContentInit() {
* // contentChildren is set
* }
* }
* ```
* @stable
*/
get isViewQuery() { return true; }
(selector: Type<any>|Function|string,
{descendants, read}?: {descendants?: boolean, read?: any}): any;
new (
selector: Type<any>|Function|string,
{descendants, read}?: {descendants?: boolean, read?: any}): Query;
}
/**
* 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.
*
* Content queries are set before the `ngAfterContentInit` callback is called.
*
* ### Example
*
* ```
* @Directive({
* selector: 'someDir'
* })
* class SomeDir {
* @ContentChild(ChildDirective) contentChild;
* @ContentChild('container_ref') containerChild
*
* ngAfterContentInit() {
* // contentChild is set
* // containerChild is set
* }
* }
* ```
*
* ```html
* <container #container_ref>
* <item>a</item>
* <item>b</item>
* </container>
* ```
*/
(selector: Type<any>|Function|string, {read}?: {read?: any}): any;
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ContentChild;
}
/**
* Type of the ContentChild metadata.
*
* @stable
*/
export type ContentChild = Query;
/**
* 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 {
/**
* Declares a list of child element references.
*
* Angular automatically updates the list when the DOM is updated.
@ -412,15 +340,42 @@ export class ViewQueryMetadata extends QueryMetadata {
* }
* ```
* @stable
*/
export class ViewChildrenMetadata extends ViewQueryMetadata {
constructor(_selector: Type<any>|string, {read = null}: {read?: any} = {}) {
super(_selector, {descendants: true, read: read});
}
toString(): string { return `@ViewChildren(${stringify(this.selector)})`; }
*/ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChildren;
}
/**
* 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.
*
@ -490,9 +445,30 @@ export class ViewChildrenMetadata extends ViewQueryMetadata {
* }
* ```
* @stable
*/
export class ViewChildMetadata extends ViewQueryMetadata {
constructor(_selector: Type<any>|string, {read = null}: {read?: any} = {}) {
super(_selector, {descendants: true, first: true, read: read});
}
*/ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChild;
}
/**
* 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);

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectableMetadata, Provider} from '../di';
import {Provider} from '../di';
import {Type} from '../type';
import {TypeDecorator, makeDecorator} from '../util/decorators';
/**
* A wrapper around a module that also includes the providers.
@ -47,26 +48,26 @@ export const NO_ERRORS_SCHEMA: SchemaMetadata = {
name: 'no-errors-schema'
};
/**
* Interface for creating {@link NgModuleMetadata}
* Type of the NgModule decorator / constructor function.
*
* @stable
*/
export interface NgModuleMetadataType {
providers?: Provider[];
declarations?: Array<Type<any>|any[]>;
imports?: Array<Type<any>|ModuleWithProviders|any[]>;
exports?: Array<Type<any>|any[]>;
entryComponents?: Array<Type<any>|any[]>;
bootstrap?: Array<Type<any>|any[]>;
schemas?: Array<SchemaMetadata|any[]>;
id?: string;
export interface NgModuleMetadataFactory {
/**
* Defines an NgModule.
*/
(obj?: NgModule): TypeDecorator;
new (obj?: NgModule): NgModule;
}
/**
* Declares an Angular Module.
* Type of the NgModule metadata.
*
* @stable
*/
export class NgModuleMetadata extends InjectableMetadata implements NgModuleMetadataType {
export interface NgModule {
/**
* Defines the set of injectable objects that are available in the injector
* of this module.
@ -96,9 +97,7 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* }
* ```
*/
get providers(): Provider[] { return this._providers; }
private _providers: Provider[];
providers?: Provider[];
/**
* 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
@ -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
@ -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
@ -155,14 +154,14 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}.
*/
entryComponents: Array<Type<any>|any[]>;
entryComponents?: Array<Type<any>|any[]>;
/**
* Defines the components that should be bootstrapped when
* this module is bootstrapped. The components listed here
* 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
@ -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
* 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
* `getModuleFactory`. If left `undefined`, the `NgModule` will not be registered with
* `getModuleFactory`.
*/
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;
}
id?: string;
}
/**
* 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
*/
import {Injectable} from '../di/decorators';
import {Injectable} from '../di';
import {Map, MapWrapper} from '../facade/collection';
import {scheduleMicroTask} from '../facade/lang';
import {NgZone} from '../zone/ng_zone';

View File

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

View File

@ -7,15 +7,12 @@
*/
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 {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {expect} from '@angular/platform-browser/testing/matchers';
import {isBlank, isPresent, stringify} from '../../src/facade/lang';
class CustomDependencyMetadata extends DependencyMetadata {}
class Engine {}
class BrokenEngine {
@ -539,15 +536,12 @@ export function main() {
var providers = ReflectiveInjector.resolve([{
provide: 'token',
useFactory: (e: any /** TODO #9100 */) => 'result',
deps: [[new InjectMetadata('dep'), new CustomDependencyMetadata()]]
deps: [[new InjectMetadata('dep')]]
}]);
var provider = providers[0];
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', () => {

View File

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

View File

@ -20,7 +20,7 @@ class AType {
constructor(value: any /** TODO #9100 */) { this.value = value; }
}
@ClassDecorator('class')
@ClassDecorator({value: 'class'})
class ClassWithDecorators {
@PropDecorator('p1') @PropDecorator('p2') a: 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';
class TestAnnotation {
constructor(public arg: any) {}
}
class TerminalAnnotation {
terminal = true;
}
class DecoratedParent {}
class DecoratedChild extends DecoratedParent {}
export function main() {
var Reflect = global.Reflect;
var TerminalDecorator = makeDecorator(TerminalAnnotation);
var TestDecorator = makeDecorator(TestAnnotation, (fn: any) => fn.Terminal = TerminalDecorator);
var TerminalDecorator = makeDecorator({terminal: true});
var TestDecorator =
makeDecorator({marker: undefined}, Object, (fn: any) => fn.Terminal = TerminalDecorator);
describe('decorators', () => {
it('should invoke as decorator', () => {
function Type() {}
TestDecorator({marker: 'WORKS'})(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', () => {
var annotation = new (<any>TestDecorator)({marker: 'WORKS'});
expect(annotation instanceof TestAnnotation).toEqual(true);
expect(annotation.arg.marker).toEqual('WORKS');
expect(annotation instanceof TestDecorator).toEqual(true);
expect(annotation.marker).toEqual('WORKS');
});
it('should invoke as chain', () => {
var chain: any = TestDecorator({marker: 'WORKS'});
expect(typeof chain.Terminal).toEqual('function');
chain = chain.Terminal();
expect(chain.annotations[0] instanceof TestAnnotation).toEqual(true);
expect(chain.annotations[0].arg.marker).toEqual('WORKS');
expect(chain.annotations[1] instanceof TerminalAnnotation).toEqual(true);
expect(chain.annotations[0] instanceof TestDecorator).toEqual(true);
expect(chain.annotations[0].marker).toEqual('WORKS');
expect(chain.annotations[1] instanceof TerminalDecorator).toEqual(true);
});
it('should not apply decorators from the prototype chain', function() {
@ -59,13 +52,13 @@ export function main() {
var annotations = Reflect.getOwnMetadata('annotations', DecoratedChild);
expect(annotations.length).toBe(1);
expect(annotations[0].arg.marker).toEqual('child');
expect(annotations[0].marker).toEqual('child');
});
describe('Class', () => {
it('should create a class', () => {
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>{
constructor: function() {},
extendWorks: function() { return 'extend ' + this.arg; }
@ -89,7 +82,7 @@ export function main() {
expect(proto.extends).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', () => {

View File

@ -6,7 +6,7 @@
* 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 {ComponentFixture} from './component_fixture';
import {ListWrapper} from './facade/collection';
@ -104,26 +104,24 @@ export class TestBed implements Injector {
*/
static compileComponents(): Promise<any> { return getTestBed().compileComponents(); }
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModuleMetadataType>):
typeof TestBed {
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed {
getTestBed().overrideModule(ngModule, override);
return TestBed;
}
static overrideComponent(component: Type<any>, override: MetadataOverride<ComponentMetadataType>):
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>):
typeof TestBed {
getTestBed().overrideComponent(component, override);
return TestBed;
}
static overrideDirective(directive: Type<any>, override: MetadataOverride<DirectiveMetadataType>):
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>):
typeof TestBed {
getTestBed().overrideDirective(directive, override);
return TestBed;
}
static overridePipe(pipe: Type<any>, override: MetadataOverride<PipeMetadataType>):
typeof TestBed {
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed {
getTestBed().overridePipe(pipe, override);
return TestBed;
}
@ -144,10 +142,10 @@ export class TestBed implements Injector {
private _compilerOptions: CompilerOptions[] = [];
private _moduleOverrides: [Type<any>, MetadataOverride<NgModuleMetadataType>][] = [];
private _componentOverrides: [Type<any>, MetadataOverride<ComponentMetadataType>][] = [];
private _directiveOverrides: [Type<any>, MetadataOverride<DirectiveMetadataType>][] = [];
private _pipeOverrides: [Type<any>, MetadataOverride<PipeMetadataType>][] = [];
private _moduleOverrides: [Type<any>, MetadataOverride<NgModule>][] = [];
private _componentOverrides: [Type<any>, MetadataOverride<Component>][] = [];
private _directiveOverrides: [Type<any>, MetadataOverride<Directive>][] = [];
private _pipeOverrides: [Type<any>, MetadataOverride<Pipe>][] = [];
private _providers: Provider[] = [];
private _declarations: Array<Type<any>|any[]|any> = [];
@ -316,22 +314,22 @@ export class TestBed implements Injector {
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._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._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._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._pipeOverrides.push([pipe, override]);
}

View File

@ -6,7 +6,7 @@
* 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 {MetadataOverride} from './metadata_override';
@ -19,18 +19,16 @@ import {MetadataOverride} from './metadata_override';
*/
export class TestingCompiler extends Compiler {
get injector(): Injector { throw unimplemented(); }
overrideModule(module: Type<any>, overrides: MetadataOverride<NgModuleMetadataType>): void {
overrideModule(module: Type<any>, overrides: MetadataOverride<NgModule>): void {
throw unimplemented();
}
overrideDirective(directive: Type<any>, overrides: MetadataOverride<DirectiveMetadataType>):
void {
overrideDirective(directive: Type<any>, overrides: MetadataOverride<Directive>): void {
throw unimplemented();
}
overrideComponent(component: Type<any>, overrides: MetadataOverride<ComponentMetadataType>):
void {
overrideComponent(component: Type<any>, overrides: MetadataOverride<Component>): void {
throw unimplemented();
}
overridePipe(directive: Type<any>, overrides: MetadataOverride<PipeMetadataType>): void {
overridePipe(directive: Type<any>, overrides: MetadataOverride<Pipe>): void {
throw unimplemented();
}
}