docs(di): add docs to Injector

Closes #4254
This commit is contained in:
vsavkin 2015-09-21 14:19:03 -07:00 committed by Victor Savkin
parent 794e3870c5
commit b0effe8e27
9 changed files with 418 additions and 152 deletions

View File

@ -11,8 +11,6 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/core/facade/async';
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection'; import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import { import {
Injector, Injector,
ProtoInjector,
Visibility,
Key, Key,
Dependency, Dependency,
bind, bind,
@ -24,8 +22,10 @@ import {
resolveForwardRef, resolveForwardRef,
DependencyProvider DependencyProvider
} from 'angular2/src/core/di'; } from 'angular2/src/core/di';
import {UNDEFINED} from 'angular2/src/core/di/injector';
import { import {
UNDEFINED,
ProtoInjector,
Visibility,
InjectorInlineStrategy, InjectorInlineStrategy,
InjectorDynamicStrategy, InjectorDynamicStrategy,
BindingWithVisibility BindingWithVisibility

View File

@ -18,13 +18,7 @@ export {
export * from './di/decorators'; export * from './di/decorators';
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref'; export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
export { export {Injector, DependencyProvider} from './di/injector';
Injector,
ProtoInjector,
BindingWithVisibility,
DependencyProvider,
Visibility
} from './di/injector';
export { export {
Binding, Binding,
BindingBuilder, BindingBuilder,

View File

@ -207,6 +207,11 @@ export class Binding {
/** /**
* Creates multiple bindings matching the same token (a multi-binding). * Creates multiple bindings matching the same token (a multi-binding).
* *
* Multi-bindings are used for creating pluggable service, where the system comes
* with some default bindings, and the user can register additonal bindings.
* The combination of the default bindings and the additional bindings will be
* used to drive the behavior of the system.
*
* ### Example * ### Example
* *
* ```typescript * ```typescript

View File

@ -195,6 +195,18 @@ export class InvalidBindingError extends BaseException {
* *
* expect(() => Injector.resolveAndCreate([A])).toThrowError(); * expect(() => Injector.resolveAndCreate([A])).toThrowError();
* ``` * ```
*
* This error is also thrown when the class not marked with {@link @Injectable} has parameter types.
*
* ```typescript
* class B {}
*
* class A {
* constructor(b:B) {} // no information about the parameter types of A is available at runtime.
* }
*
* expect(() => Injector.resolveAndCreate([A,B])).toThrowError();
* ```
*/ */
export class NoAnnotationError extends BaseException { export class NoAnnotationError extends BaseException {
constructor(typeOrFunc, params: any[][]) { constructor(typeOrFunc, params: any[][]) {

View File

@ -394,76 +394,103 @@ export interface DependencyProvider {
} }
/** /**
* A dependency injection container used for resolving dependencies. * A dependency injection container used for instantiating objects and resolving dependencies.
* *
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the * An `Injector` is a replacement for a `new` operator, which can automatically resolve the
* constructor dependencies. * constructor dependencies.
*
* In typical use, application code asks for the dependencies in the constructor and they are * In typical use, application code asks for the dependencies in the constructor and they are
* resolved by the `Injector`. * resolved by the `Injector`.
* *
* ## Example: * ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
* *
* Suppose that we want to inject an `Engine` into class `Car`, we would define it like this: * The following example creates an `Injector` configured to create `Engine` and `Car`.
* *
* ```javascript * ```typescript
* @Injectable()
* class Engine { * class Engine {
* } * }
* *
* @Injectable()
* class Car { * class Car {
* constructor(@Inject(Engine) engine) { * constructor(public engine:Engine) {}
* }
* } * }
* *
* var injector = Injector.resolveAndCreate([Car, Engine]);
* var car = injector.get(Car);
* expect(car instanceof Car).toBe(true);
* expect(car.engine instanceof Engine).toBe(true);
* ``` * ```
* *
* Next we need to write the code that creates and instantiates the `Injector`. We then ask for the * Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
* `root` object, `Car`, so that the `Injector` can recursively build all of that object's
*dependencies.
*
* ```javascript
* main() {
* var injector = Injector.resolveAndCreate([Car, Engine]);
*
* // Get a reference to the `root` object, which will recursively instantiate the tree.
* var car = injector.get(Car);
* }
* ```
* Notice that we don't use the `new` operator because we explicitly want to have the `Injector`
* resolve all of the object's dependencies automatically. * resolve all of the object's dependencies automatically.
*/ */
export class Injector { export class Injector {
/** /**
* Turns a list of binding definitions into an internal resolved list of resolved bindings. * Turns an array of binding definitions into an array of resolved bindings.
* *
* A resolution is a process of flattening multiple nested lists and converting individual * A resolution is a process of flattening multiple nested arrays and converting individual
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` * bindings into an array of {@link ResolvedBinding}s.
* for the {@link Injector} for performance-sensitive code.
* *
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a * ### Example ([live demo](http://plnkr.co/edit/AiXTHi?p=preview))
* recursive list of more bindings.
* *
* The returned list is sparse, indexed by `id` for the {@link Key}. It is generally not useful to * ```typescript
*application code * @Injectable()
* other than for passing it to {@link Injector} functions that require resolved binding lists, * class Engine {
*such as * }
* `fromResolvedBindings` and `createChildFromResolved`. *
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var bindings = Injector.resolve([Car, [[Engine]]]);
*
* expect(bindings.length).toEqual(2);
*
* expect(bindings[0] instanceof ResolvedBinding).toBe(true);
* expect(bindings[0].key.displayName).toBe("Car");
* expect(bindings[0].dependencies.length).toEqual(1);
* expect(bindings[0].factory).toBeDefined();
*
* expect(bindings[1].key.displayName).toBe("Engine");
* });
* ```
*
* See {@link fromResolvedBindings} for more info.
*/ */
static resolve(bindings: Array<Type | Binding | any[]>): ResolvedBinding[] { static resolve(bindings: Array<Type | Binding | any[]>): ResolvedBinding[] {
return resolveBindings(bindings); return resolveBindings(bindings);
} }
/** /**
* Resolves bindings and creates an injector based on those bindings. This function is slower than * Resolves an array of bindings and creates an injector from those bindings.
* the corresponding `fromResolvedBindings` because it needs to resolve bindings first. See
*`resolve`
* for the {@link Injector}.
* *
* Prefer `fromResolvedBindings` in performance-critical code that creates lots of injectors. * The passed-in bindings can be an array of `Type`, {@link Binding},
* or a recursive array of more bindings.
* *
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a * The method also takes an optional {@link DependencyProvider}, which is used to
*recursive list of more * resolve dependencies that cannot be expressed as bindings.
* bindings. *
* @param `depProvider` * ### Example ([live demo](http://plnkr.co/edit/ePOccA?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Car, Engine]);
* expect(injector.get(Car) instanceof Car).toBe(true);
* ```
*
* This function is slower than the corresponding `fromResolvedBindings`
* because it needs to resolve the passed-in bindings first.
* See {@link resolve} and {@link fromResolvedBindings}.
*/ */
static resolveAndCreate(bindings: Array<Type | Binding | any[]>, static resolveAndCreate(bindings: Array<Type | Binding | any[]>,
depProvider: DependencyProvider = null): Injector { depProvider: DependencyProvider = null): Injector {
@ -472,12 +499,29 @@ export class Injector {
} }
/** /**
* Creates an injector from previously resolved bindings. This bypasses resolution and flattening. * Creates an injector from previously resolved bindings.
*
* This API is the recommended way to construct injectors in performance-sensitive parts. * This API is the recommended way to construct injectors in performance-sensitive parts.
* *
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the * The method also takes an optional {@link DependencyProvider}, which is used to
* {@link Injector}. * resolve dependencies that cannot be expressed as bindings.
* @param `depProvider` *
* ### Example ([live demo](http://plnkr.co/edit/KrSMci?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var bindings = Injector.resolve([Car, Engine]);
* var injector = Injector.fromResolvedBindings(bindings);
* expect(injector.get(Car) instanceof Car).toBe(true);
* ```
*/ */
static fromResolvedBindings(bindings: ResolvedBinding[], static fromResolvedBindings(bindings: ResolvedBinding[],
depProvider: DependencyProvider = null): Injector { depProvider: DependencyProvider = null): Injector {
@ -491,71 +535,128 @@ export class Injector {
_isHost: boolean = false; _isHost: boolean = false;
_constructionCounter: number = 0; _constructionCounter: number = 0;
constructor(public _proto: ProtoInjector, public _parent: Injector = null, /**
* Private
*/
constructor(public _proto: any /* ProtoInjector */, public _parent: Injector = null,
private _depProvider: DependencyProvider = null, private _depProvider: DependencyProvider = null,
private _debugContext: Function = null) { private _debugContext: Function = null) {
this._strategy = _proto._strategy.createInjectorStrategy(this); this._strategy = _proto._strategy.createInjectorStrategy(this);
} }
/** /**
* Returns debug information about the injector. * @private
*
* This information is included into exceptions thrown by the injector.
*/ */
debugContext(): any { return this._debugContext(); } debugContext(): any { return this._debugContext(); }
/** /**
* Retrieves an instance from the injector. * Retrieves an instance from the injector based on the provided token.
* Throws {@link NoBindingError} if not found.
* *
* @param `token`: usually the `Type` of an object. (Same as the token used while setting up a * ### Example ([live demo](http://plnkr.co/edit/HeXSHg?p=preview))
*binding). *
* @returns an instance represented by the token. Throws if not found. * ```typescript
* var injector = Injector.resolveAndCreate([
* bind("validToken").toValue("Value")
* ]);
* expect(injector.get("validToken")).toEqual("Value");
* expect(() => injector.get("invalidToken")).toThrowError();
* ```
*
* `Injector` returns itself when given `Injector` as a token.
*
* ```typescript
* var injector = Injector.resolveAndCreate([]);
* expect(injector.get(Injector)).toBe(injector);
* ```
*/ */
get(token: any): any { get(token: any): any {
return this._getByKey(Key.get(token), null, null, false, Visibility.PublicAndPrivate); return this._getByKey(Key.get(token), null, null, false, Visibility.PublicAndPrivate);
} }
/** /**
* Retrieves an instance from the injector. * Retrieves an instance from the injector based on the provided token.
* Returns null if not found.
* *
* @param `token`: usually a `Type`. (Same as the token used while setting up a binding). * ### Example ([live demo](http://plnkr.co/edit/tpEbEy?p=preview))
* @returns an instance represented by the token. Returns `null` if not found. *
* ```typescript
* var injector = Injector.resolveAndCreate([
* bind("validToken").toValue("Value")
* ]);
* expect(injector.getOptional("validToken")).toEqual("Value");
* expect(injector.getOptional("invalidToken")).toBe(null);
* ```
*
* `Injector` returns itself when given `Injector` as a token.
*
* ```typescript
* var injector = Injector.resolveAndCreate([]);
* expect(injector.getOptional(Injector)).toBe(injector);
* ```
*/ */
getOptional(token: any): any { getOptional(token: any): any {
return this._getByKey(Key.get(token), null, null, true, Visibility.PublicAndPrivate); return this._getByKey(Key.get(token), null, null, true, Visibility.PublicAndPrivate);
} }
/** /**
* Retrieves an instance from the injector. * @private
*
* @param `index`: index of an instance.
* @returns an instance represented by the index. Throws if not found.
*/ */
getAt(index: number): any { return this._strategy.getObjAtIndex(index); } getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
/** /**
* Direct parent of this injector. * Parent of this injector.
*
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* -->
*
* ### Example ([live demo](http://plnkr.co/edit/eosMGo?p=preview))
*
* ```typescript
* var parent = Injector.resolveAndCreate([]);
* var child = parent.resolveAndCreateChild([]);
* expect(child.parent).toBe(parent);
* ```
*/ */
get parent(): Injector { return this._parent; } get parent(): Injector { return this._parent; }
/** /**
* @private
* Internal. Do not use. * Internal. Do not use.
*
* We return `any` not to export the InjectorStrategy type. * We return `any` not to export the InjectorStrategy type.
*/ */
get internalStrategy(): any { return this._strategy; } get internalStrategy(): any { return this._strategy; }
/** /**
* Creates a child injector and loads a new set of bindings into it. * Resolves an array of bindings and creates a child injector from those bindings.
* *
* A resolution is a process of flattening multiple nested lists and converting individual * <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve` * -->
* for the {@link Injector} for performance-sensitive code. *
* * The passed-in bindings can be an array of `Type`, {@link Binding},
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a * or a recursive array of more bindings.
* recursive list of more bindings. *
* @param `depProvider` * The methods also takes an optional {@link DependencyProvider}, which is used to
*/ * resolved dependencies that cannot be expressed as bindings.
*
* ### Example ([live demo](http://plnkr.co/edit/opB3T4?p=preview))
*
* ```typescript
* class ParentBinding {}
* class ChildBinding {}
*
* var parent = Injector.resolveAndCreate([ParentBinding]);
* var child = parent.resolveAndCreateChild([ChildBinding]);
*
* expect(child.get(ParentBinding) instanceof ParentBinding).toBe(true);
* expect(child.get(ChildBinding) instanceof ChildBinding).toBe(true);
* expect(child.get(ParentBinding)).toBe(parent.get(ParentBinding));
* ```
*
* This function is slower than the corresponding `createChildFromResolved`
* because it needs to resolve the passed-in bindings first.
* See {@link resolve} and {@link createChildFromResolved}.
*/
resolveAndCreateChild(bindings: Array<Type | Binding | any[]>, resolveAndCreateChild(bindings: Array<Type | Binding | any[]>,
depProvider: DependencyProvider = null): Injector { depProvider: DependencyProvider = null): Injector {
var resolvedBindings = Injector.resolve(bindings); var resolvedBindings = Injector.resolve(bindings);
@ -563,12 +664,32 @@ export class Injector {
} }
/** /**
* Creates a child injector and loads a new set of {@link ResolvedBinding}s into it. * Creates a child injector from previously resolved bindings.
* *
* @param `bindings`: A sparse list of {@link ResolvedBinding}s. * <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
* See `resolve` for the {@link Injector}. * -->
* @param `depProvider` *
* @returns a new child {@link Injector}. * This API is the recommended way to construct injectors in performance-sensitive parts.
*
* The methods also takes an optional {@link DependencyProvider}, which is used to
* resolved dependencies that cannot be expressed as bindings.
*
* ### Example ([live demo](http://plnkr.co/edit/VhyfjN?p=preview))
*
* ```typescript
* class ParentBinding {}
* class ChildBinding {}
*
* var parentBindings = Injector.resolve([ParentBinding]);
* var childBindings = Injector.resolve([ChildBinding]);
*
* var parent = Injector.fromResolvedBindings(parentBindings);
* var child = parent.createChildFromResolved(childBindings);
*
* expect(child.get(ParentBinding) instanceof ParentBinding).toBe(true);
* expect(child.get(ChildBinding) instanceof ChildBinding).toBe(true);
* expect(child.get(ParentBinding)).toBe(parent.get(ParentBinding));
* ```
*/ */
createChildFromResolved(bindings: ResolvedBinding[], createChildFromResolved(bindings: ResolvedBinding[],
depProvider: DependencyProvider = null): Injector { depProvider: DependencyProvider = null): Injector {
@ -582,8 +703,26 @@ export class Injector {
/** /**
* Resolves a binding and instantiates an object in the context of the injector. * Resolves a binding and instantiates an object in the context of the injector.
* *
* @param `binding`: either a type or a binding. * The created object does not get cached by the injector.
* @returns an object created using binding. *
* ### Example ([live demo](http://plnkr.co/edit/yvVXoB?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Engine]);
*
* var car = injector.resolveAndInstantiate(Car);
* expect(car.engine).toBe(injector.get(Engine));
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
* ```
*/ */
resolveAndInstantiate(binding: Type | Binding): any { resolveAndInstantiate(binding: Type | Binding): any {
return this.instantiateResolved(Injector.resolve([binding])[0]); return this.instantiateResolved(Injector.resolve([binding])[0]);
@ -592,8 +731,25 @@ export class Injector {
/** /**
* Instantiates an object using a resolved binding in the context of the injector. * Instantiates an object using a resolved binding in the context of the injector.
* *
* @param `binding`: a resolved binding * The created object does not get cached by the injector.
* @returns an object created using binding. *
* ### Example ([live demo](http://plnkr.co/edit/ptCImQ?p=preview))
*
* ```typescript
* @Injectable()
* class Engine {
* }
*
* @Injectable()
* class Car {
* constructor(public engine:Engine) {}
* }
*
* var injector = Injector.resolveAndCreate([Engine]);
* var carBinding = Injector.resolve([Car])[0];
* var car = injector.instantiateResolved(carBinding);
* expect(car.engine).toBe(injector.get(Engine));
* expect(car).not.toBe(injector.instantiateResolved(carBinding));
*/ */
instantiateResolved(binding: ResolvedBinding): any { instantiateResolved(binding: ResolvedBinding): any {
return this._instantiateBinding(binding, Visibility.PublicAndPrivate); return this._instantiateBinding(binding, Visibility.PublicAndPrivate);

View File

@ -3,10 +3,41 @@ import {CONST, CONST_EXPR, stringify, isBlank, isPresent} from "angular2/src/cor
/** /**
* A parameter metadata that specifies a dependency. * A parameter metadata that specifies a dependency.
* *
* ``` * ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview))
* class AComponent { *
* constructor(@Inject(MyService) aService:MyService) {} * ```typescript
* class Engine {}
*
* @Injectable()
* class Car {
* engine;
* constructor(@Inject("MyEngine") engine:Engine) {
* this.engine = engine;
* }
* } * }
*
* var injector = Injector.resolveAndCreate([
* bind("MyEngine").toClass(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);
* ``` * ```
*/ */
@CONST() @CONST()
@ -19,12 +50,21 @@ export class InjectMetadata {
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if * A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if
* the dependency is not found. * the dependency is not found.
* *
* ``` * ### Example ([live demo](http://plnkr.co/edit/AsryOm?p=preview))
* class AComponent { *
* constructor(@Optional() aService:MyService) { * ```typescript
* this.aService = aService; * class Engine {}
*
* @Injectable()
* class Car {
* engine;
* constructor(@Optional() engine:Engine) {
* this.engine = engine;
* } * }
* } * }
*
* var injector = Injector.resolveAndCreate([Car]);
* expect(injector.get(Car).engine).toBeNull();
* ``` * ```
*/ */
@CONST() @CONST()
@ -64,16 +104,34 @@ export class DependencyMetadata {
} }
/** /**
* A marker metadata that marks a class as available to `Injector` for creation. Used by tooling * A marker metadata that marks a class as available to {@link Injector} for creation.
* for generating constructor stubs.
* *
* ``` * ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview))
*
* ```typescript
* @Injectable()
* class UsefulService {}
*
* @Injectable()
* class NeedsService { * class NeedsService {
* constructor(svc:UsefulService) {} * constructor(public service:UsefulService) {}
* } * }
* *
* @Injectable * 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 UsefulService {}
*
* class NeedsService {
* constructor(public service:UsefulService) {}
* }
*
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
* expect(() => injector.get(NeedsService)).toThrowError();
* ``` * ```
*/ */
@CONST() @CONST()
@ -82,21 +140,32 @@ export class InjectableMetadata {
} }
/** /**
* Specifies that an injector should retrieve a dependency from itself. * Specifies that an {@link Injector} should retrieve a dependency only from itself.
* *
* ## Example * ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
* *
* ``` * ```typescript
* class Dependency { * class Dependency {
* } * }
* *
* @Injectable()
* class NeedsDependency { * class NeedsDependency {
* constructor(public @Self() dependency:Dependency) {} * dependency;
* dependency;
* constructor(@Self() dependency:Dependency) {
* this.dependency = dependency;
* }
* } * }
* *
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]); * var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
* var nd = inj.get(NeedsDependency); * var nd = inj.get(NeedsDependency);
* expect(nd.dependency).toBeAnInstanceOf(Dependency); *
* expect(nd.dependency instanceof Dependency).toBe(true);
*
* var inj = Injector.resolveAndCreate([Dependency]);
* var child = inj.resolveAndCreateChild([NeedsDependency]);
* expect(() => child.get(NeedsDependency)).toThrowError();
* ``` * ```
*/ */
@CONST() @CONST()
@ -107,28 +176,26 @@ export class SelfMetadata {
/** /**
* Specifies that the dependency resolution should start from the parent injector. * Specifies that the dependency resolution should start from the parent injector.
* *
* ## Example * ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
* *
* * ```typescript
* ``` * class Dependency {
* class Service {}
*
* class ParentService implements Service {
* } * }
* *
* class ChildService implements Service { * @Injectable()
* constructor(public @SkipSelf() parentService:Service) {} * class NeedsDependency {
* dependency;
* constructor(@SkipSelf() dependency:Dependency) {
* this.dependency = dependency;
* }
* } * }
* *
* var parent = Injector.resolveAndCreate([ * var parent = Injector.resolveAndCreate([Dependency]);
* bind(Service).toClass(ParentService) * var child = parent.resolveAndCreateChild([NeedsDependency]);
* ]); * expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);
* var child = parent.resolveAndCreateChild([ *
* bind(Service).toClass(ChildSerice) * var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
* ]); * expect(() => inj.get(NeedsDependency)).toThrowError();
* var s = child.get(Service);
* expect(s).toBeAnInstanceOf(ChildService);
* expect(s.parentService).toBeAnInstanceOf(ParentService);
* ``` * ```
*/ */
@CONST() @CONST()
@ -140,24 +207,59 @@ export class SkipSelfMetadata {
* Specifies that an injector should retrieve a dependency from any injector until reaching the * Specifies that an injector should retrieve a dependency from any injector until reaching the
* closest host. * closest host.
* *
* ## Example * 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))
* class Dependency { *
* 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);
* }
* } * }
* *
* class NeedsDependency { * @Component({
* constructor(public @Host() dependency:Dependency) {} * selector: 'parent-cmp',
* bindings: [HostService]
* })
* @View({
* template: `
* Dir: <child-directive></child-directive>
* `,
* directives: [ChildDirective]
* })
* class ParentCmp {
* } * }
* *
* var parent = Injector.resolveAndCreate([ * @Component({
* bind(Dependency).toClass(HostDependency) * selector: 'app',
* ]); * bindings: [OtherService]
* var child = parent.resolveAndCreateChild([]); * })
* var grandChild = child.resolveAndCreateChild([NeedsDependency, Depedency]); * @View({
* var nd = grandChild.get(NeedsDependency); * template: `
* expect(nd.dependency).toBeAnInstanceOf(HostDependency); * Parent: <parent-cmp></parent-cmp>
* ``` * `,
* directives: [ParentCmp]
* })
* class App {
* }
*
* bootstrap(App);
*```
*/ */
@CONST() @CONST()
export class HostMetadata { export class HostMetadata {

View File

@ -6,7 +6,8 @@ import {
StringWrapper, StringWrapper,
isString isString
} from 'angular2/src/core/facade/lang'; } from 'angular2/src/core/facade/lang';
import {DependencyMetadata, resolveForwardRef} from 'angular2/src/core/di'; import {resolveForwardRef} from 'angular2/src/core/di';
import {DependencyMetadata} from 'angular2/src/core/di/metadata';
/** /**
* Specifies that a constant attribute value should be injected. * Specifies that a constant attribute value should be injected.

View File

@ -4,12 +4,10 @@ import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_li
import {SpyDependencyProvider} from '../spies'; import {SpyDependencyProvider} from '../spies';
import { import {
Injector, Injector,
ProtoInjector,
bind, bind,
ResolvedBinding, ResolvedBinding,
Key, Key,
forwardRef, forwardRef,
DependencyMetadata,
Injectable, Injectable,
InjectMetadata, InjectMetadata,
SelfMetadata, SelfMetadata,
@ -17,12 +15,17 @@ import {
SkipSelfMetadata, SkipSelfMetadata,
Optional, Optional,
Inject, Inject,
BindingWithVisibility,
Visibility,
Binding Binding
} from 'angular2/core'; } from 'angular2/core';
import {DependencyMetadata} from 'angular2/src/core/di/metadata';
import {InjectorInlineStrategy, InjectorDynamicStrategy} from 'angular2/src/core/di/injector'; import {
InjectorInlineStrategy,
InjectorDynamicStrategy,
ProtoInjector,
BindingWithVisibility,
Visibility
} from 'angular2/src/core/di/injector';
class CustomDependencyMetadata extends DependencyMetadata {} class CustomDependencyMetadata extends DependencyMetadata {}

View File

@ -115,9 +115,6 @@ const NG_API = [
'BindingBuilder.toFactory', 'BindingBuilder.toFactory',
'BindingBuilder.toValue', 'BindingBuilder.toValue',
'BindingWithVisibility',
'BindingWithVisibility.getKeyId',
'By', // TODO: not sure 'By', // TODO: not sure
'By.all', 'By.all',
'By.css', 'By.css',
@ -351,6 +348,9 @@ const NG_API = [
'DebugElement.queryAll', 'DebugElement.queryAll',
'DebugElement.triggerEventHandler', 'DebugElement.triggerEventHandler',
'DependencyMetadata',
'DependencyMetadata.token',
'DecimalPipe', 'DecimalPipe',
'DecimalPipe.constructor', 'DecimalPipe.constructor',
'DecimalPipe.transform', 'DecimalPipe.transform',
@ -370,8 +370,6 @@ const NG_API = [
'Dependency', 'Dependency',
'Dependency.fromKey', 'Dependency.fromKey',
'DependencyMetadata',
'DependencyMetadata.token',
'Directive', 'Directive',
'Directive.constructor', 'Directive.constructor',
@ -750,9 +748,6 @@ const NG_API = [
'PropertyMetadata', 'PropertyMetadata',
'ProtoInjector',
'ProtoInjector.getBindingAtIndex',
'ProtoViewRef', 'ProtoViewRef',
'Query', 'Query',
@ -979,8 +974,6 @@ const NG_API = [
'ViewRef.renderFragment', 'ViewRef.renderFragment',
'ViewRef.setLocal', 'ViewRef.setLocal',
'Visibility',
'WrappedException', 'WrappedException',
'WrappedException.captureStackTrace', 'WrappedException.captureStackTrace',
'WrappedException.constructor', 'WrappedException.constructor',