docs: add DI to public docs
This commit is contained in:
parent
487c4d23c1
commit
87ac100c66
|
@ -402,6 +402,7 @@ function createDocsTasks(public) {
|
|||
var dgeni = new Dgeni([require(dgeniPackage)]);
|
||||
return dgeni.generate();
|
||||
} catch(x) {
|
||||
console.log(x);
|
||||
console.log(x.stack);
|
||||
throw x;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* Define public API for Angular here.
|
||||
* Define angular core API here.
|
||||
*/
|
||||
export * from './src/core/annotations/visibility';
|
||||
export * from './src/core/compiler/interfaces';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This is a description
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This is a description
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This is a description
|
||||
*/
|
||||
|
|
@ -30,13 +30,13 @@ export class PropertySetter extends DependencyAnnotation {
|
|||
*
|
||||
* ## Example
|
||||
*
|
||||
* suppose we have an `<input>` element and would like to know its `type`.
|
||||
* Suppose we have an `<input>` element and want to know its `type`.
|
||||
*
|
||||
* ```html
|
||||
* <input type="text">
|
||||
* ```
|
||||
*
|
||||
* A decorator could inject string literal `text` like so:
|
||||
* A decorator can inject string literal `text` like so:
|
||||
*
|
||||
* ```javascript
|
||||
* @Decorator({
|
||||
|
@ -71,7 +71,7 @@ export class Attribute extends DependencyAnnotation {
|
|||
/**
|
||||
* Specifies that a [QueryList] should be injected.
|
||||
*
|
||||
* See: [QueryList] for usage.
|
||||
* See: [QueryList] for usage and example.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
|
|
|
@ -33,38 +33,57 @@ import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
|||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class View {
|
||||
/**
|
||||
* Specify a template URL for an angular component.
|
||||
*
|
||||
* NOTE: either `templateURL` or `template` should be used, but not both.
|
||||
*/
|
||||
templateUrl:any; //string;
|
||||
|
||||
/**
|
||||
* Specify an inline template for an angular component.
|
||||
*
|
||||
* NOTE: either `templateURL` or `template` should be used, but not both.
|
||||
*/
|
||||
template:any; //string;
|
||||
|
||||
/**
|
||||
* Specify a list of directives that are active within a template. [TODO: true?]
|
||||
*
|
||||
* Directives must be listed explicitly to provide proper component encapsulation.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* @Component({
|
||||
* selector: 'my-component'
|
||||
* })
|
||||
* @View({
|
||||
* directives: [For]
|
||||
* template: '
|
||||
* <ul>
|
||||
* <li *for="item in items">{{item}}</li>
|
||||
* </ul>'
|
||||
* })
|
||||
* class MyComponent {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
directives:any; //List<Type>;
|
||||
formatters:any; //List<Type>;
|
||||
source:any;//List<View>;
|
||||
locale:any; //string
|
||||
device:any; //string
|
||||
|
||||
@CONST()
|
||||
constructor({
|
||||
templateUrl,
|
||||
template,
|
||||
directives,
|
||||
formatters,
|
||||
source,
|
||||
locale,
|
||||
device
|
||||
directives
|
||||
}: {
|
||||
templateUrl: string,
|
||||
template: string,
|
||||
directives: List<Type>,
|
||||
formatters: List<Type>,
|
||||
source: List<View>,
|
||||
locale: string,
|
||||
device: string
|
||||
directives: List<Type>
|
||||
})
|
||||
{
|
||||
this.templateUrl = templateUrl;
|
||||
this.template = template;
|
||||
this.directives = directives;
|
||||
this.formatters = formatters;
|
||||
this.source = source;
|
||||
this.locale = locale;
|
||||
this.device = device;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ import {isPresent} from 'angular2/src/facade/lang';
|
|||
* lifecycle.tick();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
@Injectable()
|
||||
|
|
|
@ -5,10 +5,11 @@ import {CONST} from "angular2/src/facade/lang";
|
|||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@Inject('aServiceToken') aService) {}
|
||||
* constructor(@Inject(MyService) aService:MyService) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
export class Inject {
|
||||
token;
|
||||
|
@ -23,12 +24,13 @@ export class Inject {
|
|||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@InjectPromise('aServiceToken') aServicePromise) {
|
||||
* aServicePromise.then(aService => ...);
|
||||
* constructor(@InjectPromise(MyService) aServicePromise:Promise<MyService>) {
|
||||
* aServicePromise.then(aService:MyService => ...);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
export class InjectPromise {
|
||||
token;
|
||||
|
@ -43,12 +45,13 @@ export class InjectPromise {
|
|||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@InjectLazy('aServiceToken') aServiceFn) {
|
||||
* aService = aServiceFn();
|
||||
* constructor(@InjectLazy(MyService) aServiceFn:Function) {
|
||||
* var aService:MyService = aServiceFn();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
export class InjectLazy {
|
||||
token;
|
||||
|
@ -59,16 +62,16 @@ export class InjectLazy {
|
|||
}
|
||||
|
||||
/**
|
||||
* A parameter annotation that marks a dependency as optional.
|
||||
*
|
||||
* A parameter annotation that marks a dependency as optional. (Injects `null` if not found.)
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@Optional() dp:Dependency) {
|
||||
* this.dp = dp;
|
||||
* constructor(@Optional() aService:MyService) {
|
||||
* this.aService = aService;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
export class Optional {
|
||||
@CONST()
|
||||
|
@ -102,6 +105,7 @@ export class Optional {
|
|||
* The framework can use `new Parent()` to handle the `aService` dependency
|
||||
* in a specific way.
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
export class DependencyAnnotation {
|
||||
@CONST()
|
||||
|
@ -114,8 +118,8 @@ export class DependencyAnnotation {
|
|||
}
|
||||
|
||||
/**
|
||||
* A class annotation that marks a class as available to `Injector`s for
|
||||
* creation.
|
||||
* A marker annotation that marks a class as available to `Injector`s for creation. Used by tooling for generating
|
||||
* constructor stubs.
|
||||
*
|
||||
* ```
|
||||
* class NeedsService {
|
||||
|
@ -125,6 +129,7 @@ export class DependencyAnnotation {
|
|||
* @Injectable
|
||||
* class UsefulService {}
|
||||
* ```
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
export class Injectable {
|
||||
@CONST()
|
||||
|
|
|
@ -5,6 +5,9 @@ import {Key} from './key';
|
|||
import {Inject, InjectLazy, InjectPromise, Optional, DependencyAnnotation} from './annotations';
|
||||
import {NoAnnotationError} from './exceptions';
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class Dependency {
|
||||
key:Key;
|
||||
asPromise:boolean;
|
||||
|
@ -28,15 +31,168 @@ export class Dependency {
|
|||
var _EMPTY_LIST = []; // TODO: make const when supported
|
||||
|
||||
/**
|
||||
* Declaration of a dependency binding.
|
||||
* Describes how the [Injector] should instantiate a given token.
|
||||
*
|
||||
* See [bind].
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(String, { toValue: 'Hello' })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(String)).toEqual('Hello');
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
export class Binding {
|
||||
|
||||
/**
|
||||
* Token used when retriving this binding. Usually the [Type].
|
||||
*/
|
||||
token;
|
||||
|
||||
/**
|
||||
* Bind an interface to an implementation / subclass.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy comparison.
|
||||
*
|
||||
* ```javascript
|
||||
*
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* new Binding(Vehicle, { toClass: Car })
|
||||
* ]);
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* new Binding(Vehicle, { toAlias: Car })
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toClass:Type;
|
||||
|
||||
/**
|
||||
* Bind a key to a value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(String, { toValue: 'Hello' })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(String)).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
toValue;
|
||||
|
||||
/**
|
||||
* Bind a key to an alias of an existing key.
|
||||
*
|
||||
* An alias means that we will return the same instance as if the alias token was used. (This is in contrast to
|
||||
* `toClass` where a separet instance of `toClass` will be returned.)
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy comparison.
|
||||
*
|
||||
* ```javascript
|
||||
*
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* new Binding(Vehicle, { toAlias: Car })
|
||||
* ]);
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* new Binding(Vehicle, { toClass: Car })
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toAlias;
|
||||
|
||||
/**
|
||||
* Bind a key to a function which computes the value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
||||
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
||||
* dependencies: [String] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
toFactory:Function;
|
||||
|
||||
/**
|
||||
* Bind a key to a function which computes the value asynchronously.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(Number, { toAsyncFactory: () => {
|
||||
* return new Promise((resolve) => resolve(1 + 2));
|
||||
* }}),
|
||||
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
||||
* dependencies: [String]})
|
||||
* ]);
|
||||
*
|
||||
* injector.asyncGet(Number).then((v) => expect(v).toBe(3));
|
||||
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
||||
* ```
|
||||
*
|
||||
* The interesting thing to note is that event thougt `Numeber` has an async factory, the `String` factory
|
||||
* function takes the resolved value. This shows that the [Injector] delays executing of the `String` factory
|
||||
* until after the `Number` is resolved. This can only be done if the `token` is retrive
|
||||
*/
|
||||
toAsyncFactory:Function;
|
||||
|
||||
/**
|
||||
* Used in conjunction with `toFactory` or `toAsyncFactory` and specifies the `token`s which should be injected
|
||||
* into the factory function.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
||||
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
||||
* dependencies: [String] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
dependencies:List;
|
||||
|
||||
@CONST()
|
||||
|
@ -59,6 +215,9 @@ export class Binding {
|
|||
this.dependencies = deps;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
resolve(): ResolvedBinding {
|
||||
var factoryFn:Function;
|
||||
var resolvedDeps;
|
||||
|
@ -90,11 +249,31 @@ export class Binding {
|
|||
}
|
||||
}
|
||||
|
||||
/// Dependency binding with resolved keys and dependencies.
|
||||
/**
|
||||
* An internal resolved representaion of a [Binding] used by [Injector].
|
||||
*
|
||||
* A [Binding] is resolved when it has a factory fonction. Binding to a class, alias, or value, are just convenience
|
||||
* methods, as [Injector] only operates on calling factory functions.
|
||||
*/
|
||||
export class ResolvedBinding {
|
||||
/**
|
||||
* A key, usually a [Type].
|
||||
*/
|
||||
key:Key;
|
||||
|
||||
/**
|
||||
* Factory function which can return an instance of [key].
|
||||
*/
|
||||
factory:Function;
|
||||
|
||||
/**
|
||||
* Arguments (dependencies) to the [factory] function.
|
||||
*/
|
||||
dependencies:List<Dependency>;
|
||||
|
||||
/**
|
||||
* Specifies if the [factory] function returns an [Promise]
|
||||
*/
|
||||
providedAsPromise:boolean;
|
||||
|
||||
constructor(key:Key, factory:Function, dependencies:List<Dependency>, providedAsPromise:boolean) {
|
||||
|
@ -106,7 +285,9 @@ export class ResolvedBinding {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides fluent API for imperative construction of [Binding] objects.
|
||||
* Provides fluent API for imperative construction of [Binding] objects. (JavaScript only.)
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
export function bind(token):BindingBuilder {
|
||||
return new BindingBuilder(token);
|
||||
|
@ -114,6 +295,7 @@ export function bind(token):BindingBuilder {
|
|||
|
||||
/**
|
||||
* Helper class for [bind] function.
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
export class BindingBuilder {
|
||||
token;
|
||||
|
@ -122,18 +304,107 @@ export class BindingBuilder {
|
|||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an interface to an implementation / subclass.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy comparison.
|
||||
*
|
||||
* ```javascript
|
||||
*
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* bind(Vehicle).toClass(Car)
|
||||
* ]);
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* bind(Vehicle).toAlias(Car)
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toClass(type:Type):Binding {
|
||||
return new Binding(this.token, {toClass: type});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a key to a value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(String).toValue('Hello')
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(String)).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
toValue(value):Binding {
|
||||
return new Binding(this.token, {toValue: value});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a key to an alias of an existing key.
|
||||
*
|
||||
* An alias means that we will return the same instance as if the alias token was used. (This is in contrast to
|
||||
* `toClass` where a separet instance of `toClass` will be returned.)
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy comparison.
|
||||
*
|
||||
* ```javascript
|
||||
*
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* bind(Vehicle).toAlias(Car)
|
||||
* ]);
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* bind(Vehicle).toClass(Car)
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toAlias(aliasToken):Binding {
|
||||
return new Binding(this.token, {toAlias: aliasToken});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a key to a function which computes the value.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(Number).toFactory(() => { return 1+2; }}),
|
||||
* bind(String).toFactory((v) => { return "Value: " + v; }, [String] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
toFactory(factoryFunction:Function, dependencies:List = null):Binding {
|
||||
return new Binding(this.token, {
|
||||
toFactory: factoryFunction,
|
||||
|
@ -141,6 +412,27 @@ export class BindingBuilder {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a key to a function which computes the value asynchronously.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(Number).toAsyncFactory(() => {
|
||||
* return new Promise((resolve) => resolve(1 + 2));
|
||||
* }),
|
||||
* bind(String).toFactory((v) => { return "Value: " + v; }, [String])
|
||||
* ]);
|
||||
*
|
||||
* injector.asyncGet(Number).then((v) => expect(v).toBe(3));
|
||||
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
||||
* ```
|
||||
*
|
||||
* The interesting thing to note is that event thougt `Numeber` has an async factory, the `String` factory
|
||||
* function takes the resolved value. This shows that the [Injector] delays executing of the `String` factory
|
||||
* until after the `Number` is resolved. This can only be done if the `token` is retrive
|
||||
*/
|
||||
toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding {
|
||||
return new Binding(this.token, {
|
||||
toAsyncFactory: factoryFunction,
|
||||
|
|
|
@ -24,8 +24,12 @@ function constructResolvingPath(keys:List) {
|
|||
}
|
||||
}
|
||||
|
||||
export class KeyMetadataError extends Error {}
|
||||
|
||||
/**
|
||||
* Base class for all errors arising from missconfigured bindings.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class ProviderError extends Error {
|
||||
keys:List;
|
||||
constructResolvingMessage:Function;
|
||||
|
@ -49,6 +53,12 @@ export class ProviderError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when trying to retrieve a dependency by [Key] from [Injector], but [Injector] does not have a [Binding] for
|
||||
* said [Key].
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class NoProviderError extends ProviderError {
|
||||
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
|
||||
constructor(key) {
|
||||
|
@ -59,6 +69,30 @@ export class NoProviderError extends ProviderError {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw when trying to retrieve async [Binding] using sync API.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(Number).toAsyncFactory(() => {
|
||||
* return new Promise((resolve) => resolve(1 + 2));
|
||||
* }),
|
||||
* bind(String).toFactory((v) => { return "Value: " + v; }, [String])
|
||||
* ]);
|
||||
*
|
||||
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
||||
* expect(() => {
|
||||
* injector.get(String);
|
||||
* }).toThrowError(AsycBindingError);
|
||||
* ```
|
||||
*
|
||||
* The above example throws because `String` dependes no `Numeber` which is async. If any binding in the dependency
|
||||
* graph is async then the graph can only be retrieved using `asyncGet` API.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class AsyncBindingError extends ProviderError {
|
||||
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
|
||||
constructor(key) {
|
||||
|
@ -70,6 +104,24 @@ export class AsyncBindingError extends ProviderError {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw when dependencies from a cyle.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* ```javascript
|
||||
* class A {
|
||||
* constructor(b:B) {}
|
||||
* }
|
||||
* class B {
|
||||
* constructor(a:A) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Retrieving `A` or `B` will throw `CyclicDependencyError` as such a graph can not be constructed.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class CyclicDependencyError extends ProviderError {
|
||||
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
|
||||
constructor(key) {
|
||||
|
@ -79,6 +131,14 @@ export class CyclicDependencyError extends ProviderError {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when constructing type returns with an Error.
|
||||
*
|
||||
* The `InstantiationError` class contains the original error plus dependency graph which caused this object to be
|
||||
* instantiated.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class InstantiationError extends ProviderError {
|
||||
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
|
||||
constructor(originalException, key) {
|
||||
|
@ -90,6 +150,11 @@ export class InstantiationError extends ProviderError {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when object other then [Binding] (or [Type]) is passed to [Injector] creation.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class InvalidBindingError extends Error {
|
||||
message:string;
|
||||
constructor(binding) {
|
||||
|
@ -102,6 +167,14 @@ export class InvalidBindingError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the class as no annotation information.
|
||||
*
|
||||
* Lack of annotation prevents the [Injector] from determininig what dependencies need to be injected int the
|
||||
* constructor.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class NoAnnotationError extends Error {
|
||||
message:string;
|
||||
constructor(typeOrFunc) {
|
||||
|
|
|
@ -20,6 +20,44 @@ function _isWaiting(obj):boolean {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* A dependency injection container used for resolving dependencies.
|
||||
*
|
||||
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the constructor dependencies.
|
||||
* In typical use, application code asks for the dependencies in the constructor and they are resolved by the
|
||||
* `Injector`.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* Suppose that we want to inject an `Engine` into class `Car`, we would define it like this:
|
||||
*
|
||||
* ```javascript
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* class Car {
|
||||
* constructor(@Inject(Engine) engine) {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* Next we need to write the code that creates and instantiates the `Injector`. We then ask for the `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.
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
export class Injector {
|
||||
_bindings:List;
|
||||
_instances:List;
|
||||
|
@ -29,13 +67,16 @@ export class Injector {
|
|||
_syncStrategy:_SyncInjectorStrategy;
|
||||
|
||||
/**
|
||||
* Creates/looks up factory functions and dependencies from binding
|
||||
* declarations and flattens bindings into a single [List].
|
||||
* Turns a list of binding definitions into internal resolved list of resolved bindings.
|
||||
*
|
||||
* The returned list is sparse, indexed by [Key.id]. It is generally not
|
||||
* useful to application code other than for passing it to [Injector]
|
||||
* functions that require resolved binding lists, such as
|
||||
* [fromResolvedBindings] and [createChildFromResolved].
|
||||
* A resolution is a process of flattening multiple nested lists and converting individual bindings into a
|
||||
* list of [ResolvedBinding]s. The resolution can be cached for performance sensitive code.
|
||||
*
|
||||
* @param [bindings] can be a list of [Type], [Binding], [ResolvedBinding], or a recursive list of more bindings.
|
||||
*
|
||||
* The returned list is sparse, indexed by [Key.id]. It is generally not useful to application code other than for
|
||||
* passing it to [Injector] functions that require resolved binding lists, such as [fromResolvedBindings] and
|
||||
* [createChildFromResolved].
|
||||
*/
|
||||
static resolve(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):List<ResolvedBinding> {
|
||||
var resolvedBindings = _resolveBindings(bindings);
|
||||
|
@ -45,22 +86,33 @@ export class Injector {
|
|||
|
||||
/**
|
||||
* Resolves bindings and creates an injector based on those bindings. This function is slower than the
|
||||
* corresponding [fromResolvedBindings] because it needs to resolve bindings. Prefer [fromResolvedBindings]
|
||||
* in performance-critical code that creates lots of injectors.
|
||||
* corresponding [fromResolvedBindings] because it needs to resolve bindings first. See [Injector.resolve].
|
||||
*
|
||||
* Prefer [fromResolvedBindings] in performance-critical code that creates lots of injectors.
|
||||
*
|
||||
* @param [bindings] can be a list of [Type], [Binding], [ResolvedBinding], or a recursive list of more bindings.
|
||||
* @param [defaultBindings] Setting to true will auto-create bindings.
|
||||
*/
|
||||
static resolveAndCreate(bindings:List/*<ResolvedBinding|Binding|Type|List>*/, {defaultBindings=false}={}) {
|
||||
return new Injector(Injector.resolve(bindings), null, defaultBindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an injector from previously resolved bindings. This bypasses a lot
|
||||
* of computation and is the recommended way to construct injectors in
|
||||
* performance-sensitive parts.
|
||||
* Creates an injector from previously resolved bindings. This bypasses resolution and flattening. This API is
|
||||
* recommended way to construct injectors in performance-sensitive parts.
|
||||
*
|
||||
* @param [bindings] A sparse list of [ResolvedBinding]s. See [Injector.resolve].
|
||||
* @param [defaultBindings] Setting to true will auto-create bindings.
|
||||
*/
|
||||
static fromResolvedBindings(bindings:List<ResolvedBinding>, {defaultBindings=false}={}) {
|
||||
return new Injector(bindings, null, defaultBindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [bindings] A sparse list of [ResolvedBinding]s. See [Injector.resolve].
|
||||
* @param [parent] Parent Injector or `null` if root injector.
|
||||
* @param [defaultBindings] Setting to true will auto-create bindings. (Only use with root injector.)
|
||||
*/
|
||||
constructor(bindings:List<ResolvedBinding>, parent:Injector, defaultBindings:boolean) {
|
||||
this._bindings = bindings;
|
||||
this._instances = this._createInstances();
|
||||
|
@ -70,22 +122,60 @@ export class Injector {
|
|||
this._syncStrategy = new _SyncInjectorStrategy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to retrieve an instance from the injector.
|
||||
*
|
||||
* @param [token] usually the [Type] of object. (Same as token used while setting up a binding).
|
||||
* @returns an instance represented by the token. Throws if not found.
|
||||
*/
|
||||
get(token) {
|
||||
|
||||
return this._getByKey(Key.get(token), false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to retrieve an instance from the injector.
|
||||
*
|
||||
* @param [token] usually the [Type] of object. (Same as token used while setting up a binding).
|
||||
* @returns an instance represented by the token. Returns `null` if not found.
|
||||
*/
|
||||
getOptional(token) {
|
||||
return this._getByKey(Key.get(token), false, false, true);
|
||||
}
|
||||
|
||||
asyncGet(token) {
|
||||
/**
|
||||
* Used to retrieve an instance from the injector asynchronously. Used with asynchronous bindings.
|
||||
*
|
||||
* @param [token] usually the [Type] of object. (Same as token used while setting up a binding).
|
||||
* @returns a [Promise] which resolves to the instance represented by the token.
|
||||
*/
|
||||
asyncGet(token):Promise {
|
||||
return this._getByKey(Key.get(token), true, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a child injector and load a new set of bindings into it.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested and converting individual bindings into a
|
||||
* list of [ResolvedBinding]s. The resolution can be cached [Injector.resolve] for performance sensitive
|
||||
* code.
|
||||
*
|
||||
* See: [Injector.resolve].
|
||||
*
|
||||
* @param [bindings] can be a list of [Type], [Binding], [ResolvedBinding], or a recursive list of more bindings.
|
||||
* @returns a new child `Injector`.
|
||||
*/
|
||||
resolveAndCreateChild(bindings:List/*<ResolvedBinding|Binding|Type|List>*/):Injector {
|
||||
return new Injector(Injector.resolve(bindings), this, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a child injector and load a new set of [ResolvedBinding] into it.
|
||||
*
|
||||
* @param [bindings] A sparse list of [ResolvedBinding]s. See [Injector.resolve].
|
||||
* @returns a new child `Injector`.
|
||||
*/
|
||||
createChildFromResolved(bindings:List<ResolvedBinding>):Injector {
|
||||
return new Injector(bindings, this, false);
|
||||
}
|
||||
|
@ -283,7 +373,7 @@ function _resolveBindings(bindings:List): List {
|
|||
} else if (unresolved instanceof Type) {
|
||||
resolved = bind(unresolved).toClass(unresolved).resolve();
|
||||
} else if (unresolved instanceof Binding) {
|
||||
resolved = unresolved.resolve();
|
||||
resolved = unresolved.resolve();
|
||||
} else if (unresolved instanceof List) {
|
||||
resolved = _resolveBindings(unresolved);
|
||||
} else if (unresolved instanceof BindingBuilder) {
|
||||
|
|
|
@ -1,34 +1,48 @@
|
|||
import {KeyMetadataError} from './exceptions';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
import {int, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
//import {int} from 'angular2/src/facade/lang';
|
||||
|
||||
// TODO: uncoment `int` once https://github.com/angular/angular/issues/1414 is fixed
|
||||
|
||||
/**
|
||||
* A unique object used for retrieving items from the Injector.
|
||||
*
|
||||
* [Key]s have:
|
||||
* - system wide unique [id].
|
||||
* - [token] usually the [Type] of the instance.
|
||||
*
|
||||
* [Key]s are used internaly in [Injector] becouse they have system wide unique [id]s which allow the injector to
|
||||
* index in arrays rather ther look up items in maps.
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
export class Key {
|
||||
token;
|
||||
id:int;
|
||||
id/* :int */;
|
||||
metadata:any;
|
||||
constructor(token, id:int) {
|
||||
constructor(token, id/* :int */) {
|
||||
this.token = token;
|
||||
this.id = id;
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
static setMetadata(key:Key, metadata):Key {
|
||||
if (isPresent(key.metadata) && key.metadata !== metadata) {
|
||||
throw new KeyMetadataError();
|
||||
}
|
||||
key.metadata = metadata;
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a [Key] for a token.
|
||||
*/
|
||||
static get(token):Key {
|
||||
return _globalKeyRegistry.get(token);
|
||||
}
|
||||
|
||||
static get numberOfKeys():int {
|
||||
/**
|
||||
* @returns number of [Key]s registered in the system.
|
||||
*/
|
||||
static get numberOfKeys()/* :int */ {
|
||||
return _globalKeyRegistry.numberOfKeys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class KeyRegistry {
|
||||
_allKeys:Map;
|
||||
constructor() {
|
||||
|
@ -47,7 +61,7 @@ export class KeyRegistry {
|
|||
return newKey;
|
||||
}
|
||||
|
||||
get numberOfKeys():int {
|
||||
get numberOfKeys()/* :int */ {
|
||||
return MapWrapper.size(this._allKeys);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
*
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
export class OpaqueToken {
|
||||
_desc:string;
|
||||
|
||||
|
|
|
@ -178,8 +178,6 @@ export class SwitchWhen {
|
|||
*
|
||||
* ```
|
||||
* <template [switch-default]>...</template>
|
||||
*
|
||||
* @exportedAs angular2/directives
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/directives
|
||||
|
|
|
@ -94,11 +94,7 @@ export class MockTemplateResolver extends TemplateResolver {
|
|||
view = new View({
|
||||
template: view.template,
|
||||
templateUrl: view.templateUrl,
|
||||
directives: directives,
|
||||
formatters: view.formatters,
|
||||
source: view.source,
|
||||
locale: view.locale,
|
||||
device: view.device
|
||||
directives: directives
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -107,11 +103,7 @@ export class MockTemplateResolver extends TemplateResolver {
|
|||
view = new View({
|
||||
template: inlineTemplate,
|
||||
templateUrl: null,
|
||||
directives: view.directives,
|
||||
formatters: view.formatters,
|
||||
source: view.source,
|
||||
locale: view.locale,
|
||||
device: view.device
|
||||
directives: view.directives
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -21,31 +21,5 @@ export function main() {
|
|||
expect(registry.get(registry.get('car'))).toBe(registry.get('car'));
|
||||
});
|
||||
|
||||
describe("metadata", function () {
|
||||
it("should assign metadata to a key", function () {
|
||||
var key = registry.get('car');
|
||||
|
||||
Key.setMetadata(key, "meta");
|
||||
|
||||
expect(key.metadata).toEqual("meta");
|
||||
});
|
||||
|
||||
it("should allow assigning the same metadata twice", function () {
|
||||
var key = registry.get('car');
|
||||
|
||||
Key.setMetadata(key, "meta");
|
||||
Key.setMetadata(key, "meta");
|
||||
|
||||
expect(key.metadata).toEqual("meta");
|
||||
});
|
||||
|
||||
it("should throw when assigning different metadata", function () {
|
||||
var key = registry.get('car');
|
||||
|
||||
Key.setMetadata(key, "meta1");
|
||||
|
||||
expect(() => Key.setMetadata(key, "meta2")).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue