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,8 +7,15 @@
*/
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.
*
* ### 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);
* ```
*
* 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
*
@ -49,12 +57,33 @@ import {stringify} from '../facade/lang';
* ```
* @stable
*/
export class InjectMetadata {
constructor(public token: any) {}
toString(): string { return `@Inject(${stringify(this.token)})`; }
(token: any): any;
new (token: any): Inject;
}
/**
* 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
* the dependency is not found.
*
@ -76,20 +105,32 @@ export class InjectMetadata {
* ```
* @stable
*/
export class OptionalMetadata {
toString(): string { return `@Optional()`; }
(): 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))
@ -121,11 +162,32 @@ export class DependencyMetadata {
* ```
* @stable
*/
export class InjectableMetadata {
constructor() {}
(): any;
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.
*
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
@ -153,11 +215,33 @@ export class InjectableMetadata {
* ```
* @stable
*/
export class SelfMetadata {
toString(): string { return `@Self()`; }
(): any;
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.
*
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
@ -183,11 +267,32 @@ export class SelfMetadata {
* ```
* @stable
*/
export class SkipSelfMetadata {
toString(): string { return `@SkipSelf()`; }
(): any;
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
* closest host.
*
@ -240,6 +345,31 @@ export class SkipSelfMetadata {
*```
* @stable
*/
export class HostMetadata {
toString(): string { return `@Host()`; }
(): any;
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
*/
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,175 +71,82 @@ 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 {}
/**
* Type of the ContentChildren decorator / constructor function.
*
* @stable
*/
export interface ContentChildrenMetadataFactory {
/**
* Configures a content query.
*
* Content queries are set before the `ngAfterContentInit` callback is called.
@ -253,16 +167,40 @@ export class QueryMetadata extends DependencyMetadata {
* ```
* @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});
}
(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;
}
// 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.
*
* Content queries are set before the `ngAfterContentInit` callback is called.
@ -275,67 +213,57 @@ export class ContentChildrenMetadata extends QueryMetadata {
* })
* class SomeDir {
* @ContentChild(ChildDirective) contentChild;
* @ContentChild('container_ref') containerChild
*
* ngAfterContentInit() {
* // contentChild is set
* // containerChild is set
* }
* }
* ```
* @stable
*
* ```html
* <container #container_ref>
* <item>a</item>
* <item>b</item>
* </container>
* ```
*/
export class ContentChildMetadata extends QueryMetadata {
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}): ContentChild;
}
/**
* Similar to {@link ContentChildMetadata}, but querying the component view, instead
* of the content children.
* Type of the ContentChild metadata.
*
* ### 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 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 {
/**
* always `true` to differentiate it with {@link QueryMetadata}.
*/
get isViewQuery() { return true; }
}
/**
* 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);

View File

@ -11,23 +11,18 @@ import {ChangeDetectionStrategy} from '../change_detection/constants';
import {InjectableMetadata, Provider} from '../di';
import {isPresent} from '../facade/lang';
import {Type} from '../type';
import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators';
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.
*
* {@link DirectiveMetadata}s with an embedded view are called {@link ComponentMetadata}s.
@ -62,11 +57,13 @@ export interface DirectiveMetadataType {
* depth-first order. The
* 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}:
*
* 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
* 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"`.
*
* ### 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
* found, the injector supplies `null` instead of throwing an error.
*
@ -312,7 +311,8 @@ export interface DirectiveMetadataType {
* location in the current view
* 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
* `<template>` element. Thus a
* directive in a child view cannot inject the directive that created it.
@ -340,9 +340,11 @@ export interface DirectiveMetadataType {
* </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
* 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
*
@ -403,12 +405,48 @@ export interface DirectiveMetadataType {
* </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
* 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.
*
@ -441,7 +479,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* 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
@ -487,8 +525,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* ```
*
*/
get inputs(): string[] { return this._inputs; }
private _inputs: string[];
inputs?: string[];
/**
* 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; }
private _outputs: string[];
outputs?: string[];
/**
* 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
@ -668,8 +704,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* }
* ```
*/
get providers(): Provider[] { return this._providers; }
private _providers: Provider[];
providers?: Provider[];
/**
* 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.
*
@ -727,45 +761,38 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* }
* ```
*/
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;
}
queries?: {[key: string]: any};
}
/**
* 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
*/
export interface ComponentMetadataType extends DirectiveMetadataType {
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[]>;
}
/**
export interface ComponentMetadataFactory {
/**
* Declare reusable UI building blocks for an application.
*
* Each Angular component requires a single `@Component` annotation. The
* `@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.
*
* When a component is instantiated, Angular
@ -784,9 +811,48 @@ export interface ComponentMetadataType extends DirectiveMetadataType {
* ### Example
*
* {@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
*/
export class ComponentMetadata extends DirectiveMetadata implements ComponentMetadataType {
export interface Component extends Directive {
/**
* 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
* 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.
@ -835,8 +901,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
*
* ```
*/
get viewProviders(): Provider[] { return this._viewProviders; }
private _viewProviders: Provider[];
viewProviders?: Provider[];
/**
* 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.
@ -866,26 +931,26 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
*
* <!-- TODO: what's the url relative to? -->
*/
templateUrl: string;
templateUrl?: string;
/**
* Specifies an inline template for an Angular component.
*
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
*/
template: string;
template?: string;
/**
* Specifies stylesheet URLs for an Angular component.
*
* <!-- TODO: what's the url relative to? -->
*/
styleUrls: string[];
styleUrls?: string[];
/**
* 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
@ -958,7 +1023,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* - {@link animate animate()}
* - {@link keyframes keyframes()}
*/
animations: AnimationEntryMetadata[];
animations?: AnimationEntryMetadata[];
/**
* Specify how the template and the styles should be encapsulated.
@ -966,9 +1031,9 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* has styles,
* 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
@ -976,73 +1041,88 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}.
*/
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;
}
entryComponents?: Array<Type<any>|any[]>;
}
/**
* 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
*/
export interface PipeMetadataType {
name: string;
pure?: boolean;
}
/**
export interface PipeMetadataFactory {
/**
* Declare reusable pipe function.
*
* 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.
*
* ### Example
*
* {@example core/ts/metadata/metadata.ts region='pipe'}
* @stable
*/
export class PipeMetadata extends InjectableMetadata implements PipeMetadataType {
name: string;
/** @internal */
_pure: boolean;
(obj: Pipe): TypeDecorator;
constructor({name, pure}: PipeMetadataType) {
super();
this.name = name;
this._pure = pure;
}
get pure(): boolean { return isPresent(this._pure) ? this._pure : true; }
/**
* See the {@link Pipe} decorator.
*/
new (obj: Pipe): Pipe;
}
/**
* 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.
*
* Angular automatically updates data-bound properties during change detection.
@ -1082,15 +1162,37 @@ export class PipeMetadata extends InjectableMetadata implements PipeMetadataType
* ```
* @stable
*/
export class InputMetadata {
constructor(
/**
* Name used when instantiating a component in the template.
*/
public bindingPropertyName?: string) {}
(bindingPropertyName?: string): any;
new (bindingPropertyName?: string): any;
}
/**
* 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.
*
* When an output property emits an event, an event handler attached to that event
@ -1130,11 +1232,34 @@ export class InputMetadata {
* ```
* @stable
*/
export class OutputMetadata {
constructor(public bindingPropertyName?: string) {}
(bindingPropertyName?: string): any;
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.
*
* Angular automatically checks host property bindings during change detection.
@ -1153,13 +1278,14 @@ export class OutputMetadata {
* @Directive({selector: '[ngModel]'})
* class NgModelStatus {
* constructor(public control:NgModel) {}
* @HostBinding('class.valid') get valid { return this.control.valid; }
* @HostBinding('class.invalid') get invalid { return this.control.invalid; }
* @HostBinding('class.valid') get valid() { return this.control.valid; }
* @HostBinding('class.invalid') get invalid() { return this.control.invalid; }
* }
*
* @Component({
* selector: 'app',
* template: `<input [(ngModel)]="prop">`
* template: `<input [(ngModel)]="prop">`,
* directives: [FORM_DIRECTIVES, NgModelStatus]
* })
* class App {
* prop;
@ -1167,11 +1293,34 @@ export class OutputMetadata {
* ```
* @stable
*/
export class HostBindingMetadata {
constructor(public hostPropertyName?: string) {}
(hostPropertyName?: string): any;
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.
*
* Angular will invoke the decorated method when the host element emits the specified event.
@ -1197,12 +1346,33 @@ export class HostBindingMetadata {
*
* @Component({
* selector: 'app',
* template: `<button counting>Increment</button>`
* template: `<button counting>Increment</button>`,
* directives: [CountClicks]
* })
* class App {}
* ```
* @stable
* @Annotation
*/
export class HostListenerMetadata {
constructor(public eventName: string, public args?: string[]) {}
(eventName: string, args?: string[]): any;
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
*/
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 ParamDecoratorFactory(...args: any[]): any {
var annotationInstance = Object.create(annotationCls.prototype);
annotationCls.apply(annotationInstance, args);
if (this instanceof annotationCls) {
return annotationInstance;
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 {
(<any>ParamDecorator).annotation = annotationInstance;
return ParamDecorator;
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 {
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 {
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();
}
}