From 1b15170c8963a0b9bb7797be7c9bc01b1bb6521d Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Mon, 12 Sep 2016 09:44:20 -0700 Subject: [PATCH] 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 --- .../compiler-cli/src/static_reflector.ts | 11 +- .../test/static_reflector_spec.ts | 2 +- .../compiler/src/directive_resolver.ts | 15 +- .../compiler/src/metadata_resolver.ts | 15 +- .../testing/directive_resolver_mock.ts | 2 +- modules/@angular/compiler/testing/index.ts | 13 +- modules/@angular/core/src/console.ts | 2 +- modules/@angular/core/src/di.ts | 6 +- modules/@angular/core/src/di/decorators.ts | 109 -- modules/@angular/core/src/di/metadata.ts | 522 +++--- modules/@angular/core/src/di/opaque_token.ts | 2 +- .../core/src/di/reflective_provider.ts | 8 +- .../@angular/core/src/linker/view_utils.ts | 2 +- modules/@angular/core/src/metadata.ts | 1113 +------------ modules/@angular/core/src/metadata/di.ts | 502 +++--- .../@angular/core/src/metadata/directives.ts | 1474 +++++++++-------- .../@angular/core/src/metadata/ng_module.ts | 76 +- .../core/src/testability/testability.ts | 2 +- modules/@angular/core/src/util/decorators.ts | 54 +- .../core/test/di/reflective_injector_spec.ts | 8 +- .../core/test/reflection/reflector_common.ts | 28 +- .../core/test/reflection/reflector_spec.ts | 2 +- .../core/test/util/decorators_spec.ts | 31 +- modules/@angular/core/testing/test_bed.ts | 28 +- .../@angular/core/testing/test_compiler.ts | 12 +- 25 files changed, 1573 insertions(+), 2466 deletions(-) delete mode 100644 modules/@angular/core/src/di/decorators.ts diff --git a/modules/@angular/compiler-cli/src/static_reflector.ts b/modules/@angular/compiler-cli/src/static_reflector.ts index 1150b1dbc1..6db04a24a4 100644 --- a/modules/@angular/compiler-cli/src/static_reflector.ts +++ b/modules/@angular/compiler-cli/src/static_reflector.ts @@ -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 { diff --git a/modules/@angular/compiler-cli/test/static_reflector_spec.ts b/modules/@angular/compiler-cli/test/static_reflector_spec.ts index a1d0fa0f7b..213054fdd2 100644 --- a/modules/@angular/compiler-cli/test/static_reflector_spec.ts +++ b/modules/@angular/compiler-cli/test/static_reflector_spec.ts @@ -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'); }); }); diff --git a/modules/@angular/compiler/src/directive_resolver.ts b/modules/@angular/compiler/src/directive_resolver.ts index 28c502fc5f..4f7cb5a219 100644 --- a/modules/@angular/compiler/src/directive_resolver.ts +++ b/modules/@angular/compiler/src/directive_resolver.ts @@ -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) ? (a.args).join(', ') : ''; - host[`(${a.eventName})`] = `${propName}(${args})`; + const hostListener: HostListenerMetadata = a; + var args = isPresent(hostListener.args) ? (hostListener.args).join(', ') : ''; + host[`(${hostListener.eventName})`] = `${propName}(${args})`; } else if (a instanceof QueryMetadata) { queries[propName] = a; } diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 50e265eb93..04d26c7466 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -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)) { (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|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( diff --git a/modules/@angular/compiler/testing/directive_resolver_mock.ts b/modules/@angular/compiler/testing/directive_resolver_mock.ts index f103d84f3f..772ba84da3 100644 --- a/modules/@angular/compiler/testing/directive_resolver_mock.ts +++ b/modules/@angular/compiler/testing/directive_resolver_mock.ts @@ -62,7 +62,7 @@ export class MockDirectiveResolver extends DirectiveResolver { let view = this._views.get(type); if (!view) { - view = metadata; + view = metadata; } let animations = view.animations; diff --git a/modules/@angular/compiler/testing/index.ts b/modules/@angular/compiler/testing/index.ts index ea151d9f2e..aed6935c45 100644 --- a/modules/@angular/compiler/testing/index.ts +++ b/modules/@angular/compiler/testing/index.ts @@ -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, override: MetadataOverride): void { + overrideModule(ngModule: Type, override: MetadataOverride): void { const oldMetadata = this._moduleResolver.resolve(ngModule, false); this._moduleResolver.setNgModule( ngModule, this._overrider.overrideMetadata(NgModuleMetadata, oldMetadata, override)); } - overrideDirective(directive: Type, override: MetadataOverride): void { + overrideDirective(directive: Type, override: MetadataOverride): void { const oldMetadata = this._directiveResolver.resolve(directive, false); this._directiveResolver.setDirective( directive, this._overrider.overrideMetadata(DirectiveMetadata, oldMetadata, override)); } - overrideComponent(component: Type, override: MetadataOverride): void { + overrideComponent(component: Type, override: MetadataOverride): void { const oldMetadata = this._directiveResolver.resolve(component, false); this._directiveResolver.setDirective( component, this._overrider.overrideMetadata(ComponentMetadata, oldMetadata, override)); } - overridePipe(pipe: Type, override: MetadataOverride): void { + overridePipe(pipe: Type, override: MetadataOverride): 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) { this._compiler.clearCacheFor(type); } diff --git a/modules/@angular/core/src/console.ts b/modules/@angular/core/src/console.ts index dfc0c08aee..4acc013f6b 100644 --- a/modules/@angular/core/src/console.ts +++ b/modules/@angular/core/src/console.ts @@ -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() diff --git a/modules/@angular/core/src/di.ts b/modules/@angular/core/src/di.ts index 37bdbfb503..cc05afff72 100644 --- a/modules/@angular/core/src/di.ts +++ b/modules/@angular/core/src/di.ts @@ -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'; diff --git a/modules/@angular/core/src/di/decorators.ts b/modules/@angular/core/src/di/decorators.ts deleted file mode 100644 index b756194549..0000000000 --- a/modules/@angular/core/src/di/decorators.ts +++ /dev/null @@ -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 = - 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); diff --git a/modules/@angular/core/src/di/metadata.ts b/modules/@angular/core/src/di/metadata.ts index a30e4f7b83..97074d3d83 100644 --- a/modules/@angular/core/src/di/metadata.ts +++ b/modules/@angular/core/src/di/metadata.ts @@ -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: + * `, + * directives: [ChildDirective] + * }) + * class ParentCmp { + * } + * + * @Component({ + * selector: 'app', + * providers: [OtherService], + * template: ` + * Parent: + * `, + * 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: - * `, - * directives: [ChildDirective] - * }) - * class ParentCmp { - * } - * - * @Component({ - * selector: 'app', - * providers: [OtherService], - * template: ` - * Parent: - * `, - * 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 +}; diff --git a/modules/@angular/core/src/di/opaque_token.ts b/modules/@angular/core/src/di/opaque_token.ts index 534b5deb01..2d728b13a7 100644 --- a/modules/@angular/core/src/di/opaque_token.ts +++ b/modules/@angular/core/src/di/opaque_token.ts @@ -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. diff --git a/modules/@angular/core/src/di/reflective_provider.ts b/modules/@angular/core/src/di/reflective_provider.ts index 4e877fa832..f07fe36d7e 100644 --- a/modules/@angular/core/src/di/reflective_provider.ts +++ b/modules/@angular/core/src/di/reflective_provider.ts @@ -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); } } diff --git a/modules/@angular/core/src/linker/view_utils.ts b/modules/@angular/core/src/linker/view_utils.ts index 3615400f78..ab7ee46379 100644 --- a/modules/@angular/core/src/linker/view_utils.ts +++ b/modules/@angular/core/src/linker/view_utils.ts @@ -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'; diff --git a/modules/@angular/core/src/metadata.ts b/modules/@angular/core/src/metadata.ts index 1e504664e3..03ebad232f 100644 --- a/modules/@angular/core/src/metadata.ts +++ b/modules/@angular/core/src/metadata.ts @@ -11,1102 +11,33 @@ * to be used by the decorator versions of these annotations. */ -import {AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; -import {ComponentMetadata, ComponentMetadataType, DirectiveMetadata, DirectiveMetadataType, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata, PipeMetadataType} from './metadata/directives'; -import {ModuleWithProviders, NgModuleMetadata, NgModuleMetadataType, SchemaMetadata} from './metadata/ng_module'; +import {Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren} from './metadata/di'; +import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives'; +import {ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module'; import {ViewEncapsulation} from './metadata/view'; import {Type} from './type'; -import {TypeDecorator, makeDecorator, makeParamDecorator, makePropDecorator} from './util/decorators'; +import {TypeDecorator, makeParamDecorator, makePropDecorator} from './util/decorators'; -export {ANALYZE_FOR_ENTRY_COMPONENTS, AttributeMetadata, ContentChildMetadata, ContentChildrenMetadata, QueryMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata} from './metadata/di'; -export {ComponentMetadata, ComponentMetadataType, DirectiveMetadata, DirectiveMetadataType, HostBindingMetadata, HostListenerMetadata, InputMetadata, OutputMetadata, PipeMetadata, PipeMetadataType} from './metadata/directives'; +export {ANALYZE_FOR_ENTRY_COMPONENTS, Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren} from './metadata/di'; +export {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives'; export {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './metadata/lifecycle_hooks'; -export {CUSTOM_ELEMENTS_SCHEMA, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModuleMetadata, NgModuleMetadataType, SchemaMetadata} from './metadata/ng_module'; +export {CUSTOM_ELEMENTS_SCHEMA, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModule, SchemaMetadata} from './metadata/ng_module'; export {ViewEncapsulation} from './metadata/view'; -/** - * Interface for the {@link DirectiveMetadata} decorator function. - * - * See {@link DirectiveMetadataFactory}. - * - * @stable - */ -export interface DirectiveDecorator extends TypeDecorator {} - -/** - * Interface for the {@link ComponentMetadata} decorator function. - * - * See {@link ComponentFactory}. - * - * @stable - */ -export interface ComponentDecorator extends TypeDecorator {} - -/** - * Interface for the {@link NgModuleMetadata} decorator function. - * - * See {@link NgModuleMetadataFactory}. - * - * @stable - */ -export interface NgModuleDecorator extends TypeDecorator {} - - -/** - * {@link DirectiveMetadata} factory for creating annotations, decorators or DSL. - * - * ### 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({...}) - * ] - * ``` - * - * @stable - */ -export interface DirectiveMetadataFactory { - (obj: DirectiveMetadataType): DirectiveDecorator; - new (obj: DirectiveMetadataType): DirectiveMetadata; -} - -/** - * {@link ComponentMetadata} factory for creating annotations, decorators or DSL. - * - * ### 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({...}) - * ] - * ``` - * - * @stable - */ -export interface ComponentMetadataFactory { - (obj: ComponentMetadataType): ComponentDecorator; - new (obj: ComponentMetadataType): ComponentMetadata; -} - -/** - * {@link AttributeMetadata} factory for creating annotations, decorators or DSL. - * - * ### 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 interface AttributeMetadataFactory { - (name: string): TypeDecorator; - new (name: string): AttributeMetadata; -} - -/** - * Factory for {@link ContentChildren}. - * @stable - */ -export interface ContentChildrenMetadataFactory { - (selector: Type|Function|string, - {descendants, read}?: {descendants?: boolean, read?: any}): any; - new ( - selector: Type|Function|string, - {descendants, read}?: {descendants?: boolean, read?: any}): ContentChildrenMetadata; -} - -/** - * Factory for {@link ContentChild}. - * @stable - */ -export interface ContentChildMetadataFactory { - (selector: Type|Function|string, {read}?: {read?: any}): any; - new (selector: Type|Function|string, {read}?: {read?: any}): ContentChildMetadataFactory; -} - -/** - * Factory for {@link ViewChildren}. - * @stable - */ -export interface ViewChildrenMetadataFactory { - (selector: Type|Function|string, {read}?: {read?: any}): any; - new (selector: Type|Function|string, {read}?: {read?: any}): ViewChildrenMetadata; -} - -/** - * Factory for {@link ViewChild}. - * @stable - */ -export interface ViewChildMetadataFactory { - (selector: Type|Function|string, {read}?: {read?: any}): any; - new (selector: Type|Function|string, {read}?: {read?: any}): ViewChildMetadataFactory; -} - - -/** - * {@link PipeMetadata} factory for creating decorators. - * - * ### Example - * - * {@example core/ts/metadata/metadata.ts region='pipe'} - * @stable - */ -export interface PipeMetadataFactory { - (obj: PipeMetadataType): any; - new (obj: PipeMetadataType): any; -} - -/** - * {@link InputMetadata} factory for creating decorators. - * - * See {@link InputMetadata}. - * @stable - */ -export interface InputMetadataFactory { - (bindingPropertyName?: string): any; - new (bindingPropertyName?: string): any; -} - -/** - * {@link OutputMetadata} factory for creating decorators. - * - * See {@link OutputMetadata}. - * @stable - */ -export interface OutputMetadataFactory { - (bindingPropertyName?: string): any; - new (bindingPropertyName?: string): any; -} - -/** - * {@link HostBindingMetadata} factory function. - * @stable - */ -export interface HostBindingMetadataFactory { - (hostPropertyName?: string): any; - new (hostPropertyName?: string): any; -} - -/** - * {@link HostListenerMetadata} factory function. - * @stable - */ -export interface HostListenerMetadataFactory { - (eventName: string, args?: string[]): any; - new (eventName: string, args?: string[]): any; -} - -/** - * {@link NgModuleMetadata} factory for creating annotations, decorators or DSL. - * - * @stable - */ -export interface NgModuleMetadataFactory { - (obj?: NgModuleMetadataType): NgModuleDecorator; - new (obj?: NgModuleMetadataType): NgModuleMetadata; -} - -// TODO(alexeagle): remove the duplication of this doc. It is copied from ComponentMetadata. -/** - * 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 - * binds to. - * - * When a component is instantiated, Angular - * - creates a shadow DOM for the component. - * - loads the selected template into the shadow DOM. - * - creates all the injectable objects configured with `providers` and `viewProviders`. - * - * All template expressions and statements are then evaluated against the component instance. - * - * ## Lifecycle hooks - * - * When the component class implements some {@linkDocs guide/lifecycle-hooks} the - * callbacks are called by the change detection at defined points in time during the life of the - * component. - * - * ### Example - * - * {@example core/ts/metadata/metadata.ts region='component'} - * @stable - * @Annotation - */ -export var Component: ComponentMetadataFactory = - makeDecorator(ComponentMetadata); - -// TODO(alexeagle): remove the duplication of this doc. It is copied from DirectiveMetadata. -/** - * Directives allow you to attach behavior to elements in the DOM. - * - * {@link DirectiveMetadata}s with an embedded view are called {@link ComponentMetadata}s. - * - * A directive consists of a single directive annotation and a controller class. When the - * directive's `selector` matches - * elements in the DOM, the following steps occur: - * - * 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor - * arguments. - * 2. Angular instantiates directives for each matched element using `ElementInjector` in a - * depth-first order, - * as declared in the HTML. - * - * ## Understanding How Injection Works - * - * There are three stages of injection resolution. - * - *Pre-existing Injectors*: - * - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if - * the dependency was - * specified as `@Optional`, returns `null`. - * - The platform injector resolves browser singleton resources, such as: cookies, title, - * location, and others. - * - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow - * the same parent-child hierarchy - * as the component instances in the DOM. - * - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each - * element has an `ElementInjector` - * which follow the same parent-child hierarchy as the DOM elements themselves. - * - * When a template is instantiated, it also must instantiate the corresponding directives in a - * 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 - * {@link ComponentMetadata}: - * - * 1. Dependencies on the current element - * 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 - * - * - * The `ElementInjector` can inject other directives, element-specific special objects, or it can - * delegate to the parent - * injector. - * - * To inject other directives, declare the constructor parameter as: - * - `directive:DirectiveType`: a directive on the current element only - * - `@Host() directive:DirectiveType`: any directive that matches the type between the current - * element and the - * Shadow DOM root. - * - `@Query(DirectiveType) query:QueryList`: A live collection of direct child - * directives. - * - `@QueryDescendants(DirectiveType) query:QueryList`: A live collection of any - * child directives. - * - * To inject element-specific special objects, declare the constructor parameter as: - * - `element: ElementRef` to obtain a reference to logical element in the view. - * - `viewContainer: ViewContainerRef` to control child template instantiation, for - * {@link DirectiveMetadata} directives only - * - `bindingPropagation: BindingPropagation` to control change detection in a more granular way. - * - * ### Example - * - * The following example demonstrates how dependency injection resolves constructor arguments in - * practice. - * - * - * Assume this HTML template: - * - * ``` - *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * ``` - * - * With the following `dependency` decorator and `SomeService` injectable class. - * - * ``` - * @Injectable() - * class SomeService { - * } - * - * @Directive({ - * selector: '[dependency]', - * inputs: [ - * 'id: dependency' - * ] - * }) - * class Dependency { - * id:string; - * } - * ``` - * - * Let's step through the different ways in which `MyDirective` could be declared... - * - * - * ### No injection - * - * Here the constructor is declared with no arguments, therefore nothing is injected into - * `MyDirective`. - * - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor() { - * } - * } - * ``` - * - * This directive would be instantiated with no dependencies. - * - * - * ### Component-level injection - * - * Directives can inject any injectable instance from the closest component injector or any of its - * parents. - * - * Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type - * from the parent - * component's injector. - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor(someService: SomeService) { - * } - * } - * ``` - * - * This directive would be instantiated with a dependency on `SomeService`. - * - * - * ### Injecting a directive from the current element - * - * Directives can inject other directives declared on the current element. - * - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor(dependency: Dependency) { - * expect(dependency.id).toEqual(3); - * } - * } - * ``` - * This directive would be instantiated with `Dependency` declared at the same element, in this case - * `dependency="3"`. - * - * ### Injecting a directive from any ancestor elements - * - * Directives can inject other directives declared on any ancestor element (in the current Shadow - * DOM), i.e. on the current element, the - * parent element, or its parents. - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor(@Host() dependency: Dependency) { - * expect(dependency.id).toEqual(2); - * } - * } - * ``` - * - * `@Host` checks the current element, the parent, as well as its parents recursively. If - * `dependency="2"` didn't - * exist on the direct parent, this injection would - * have returned - * `dependency="1"`. - * - * - * ### Injecting a live collection of direct child directives - * - * - * A directive can also query for other child directives. Since parent directives are instantiated - * before child directives, a directive can't simply inject the list of child directives. Instead, - * the directive injects a {@link QueryList}, which updates its contents as children are added, - * removed, or moved by a directive that uses a {@link ViewContainerRef} such as a `ngFor`, an - * `ngIf`, or an `ngSwitch`. - * - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor(@Query(Dependency) dependencies:QueryList) { - * } - * } - * ``` - * - * This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and - * 6. Here, `Dependency` 5 would not be included, because it is not a direct child. - * - * ### Injecting a live collection of descendant directives - * - * By passing the descendant flag to `@Query` above, we can include the children of the child - * elements. - * - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor(@Query(Dependency, {descendants: true}) dependencies:QueryList) { - * } - * } - * ``` - * - * This directive would be instantiated with a Query which would contain `Dependency` 4, 5 and 6. - * - * ### Optional injection - * - * The normal behavior of directives is to return an error when a specified dependency cannot be - * resolved. If you - * would like to inject `null` on unresolved dependency instead, you can annotate that dependency - * with `@Optional()`. - * This explicitly permits the author of a template to treat some of the surrounding directives as - * optional. - * - * ``` - * @Directive({ selector: '[my-directive]' }) - * class MyDirective { - * constructor(@Optional() dependency:Dependency) { - * } - * } - * ``` - * - * 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. - * - * ### Example - * - * Here we use a decorator directive to simply define basic tool-tip behavior. - * - * ``` - * @Directive({ - * selector: '[tooltip]', - * inputs: [ - * 'text: tooltip' - * ], - * host: { - * '(mouseenter)': 'onMouseEnter()', - * '(mouseleave)': 'onMouseLeave()' - * } - * }) - * class Tooltip{ - * text:string; - * overlay:Overlay; // NOT YET IMPLEMENTED - * overlayManager:OverlayManager; // NOT YET IMPLEMENTED - * - * constructor(overlayManager:OverlayManager) { - * this.overlayManager = overlayManager; - * } - * - * onMouseEnter() { - * // exact signature to be determined - * this.overlay = this.overlayManager.open(text, ...); - * } - * - * onMouseLeave() { - * this.overlay.close(); - * this.overlay = null; - * } - * } - * ``` - * In our HTML template, we can then add this behavior to a `
` or any other element with the - * `tooltip` selector, - * like so: - * - * ``` - *
- * ``` - * - * Directives can also control the instantiation, destruction, and positioning of inline template - * elements: - * - * A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at - * runtime. - * The {@link ViewContainerRef} is created as a result of `