2015-04-09 23:17:05 -07:00
|
|
|
import {Type, isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
|
2015-02-05 13:08:05 -08:00
|
|
|
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
|
|
|
import {reflector} from 'angular2/src/reflection/reflection';
|
2014-10-09 10:55:18 -04:00
|
|
|
import {Key} from './key';
|
2015-04-24 15:19:11 -07:00
|
|
|
import {
|
|
|
|
Inject,
|
|
|
|
InjectLazy,
|
|
|
|
InjectPromise,
|
|
|
|
Optional,
|
|
|
|
DependencyAnnotation
|
|
|
|
} from './annotations_impl';
|
2015-04-13 14:29:32 -07:00
|
|
|
import {NoAnnotationError} from './exceptions';
|
2015-05-13 15:54:46 -07:00
|
|
|
import {resolveForwardRef} from './forward_ref';
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
2014-10-09 10:55:18 -04:00
|
|
|
export class Dependency {
|
2015-04-24 15:19:11 -07:00
|
|
|
constructor(public key: Key, public asPromise: boolean, public lazy: boolean,
|
2015-05-11 10:28:07 -07:00
|
|
|
public optional: boolean, public properties: List<any>) {}
|
2015-02-27 07:42:51 -08:00
|
|
|
|
2015-04-24 15:19:11 -07:00
|
|
|
static fromKey(key: Key) { return new Dependency(key, false, false, false, []); }
|
2014-10-09 10:55:18 -04:00
|
|
|
}
|
2014-10-09 11:35:13 -04:00
|
|
|
|
2015-04-09 23:17:05 -07:00
|
|
|
var _EMPTY_LIST = []; // TODO: make const when supported
|
|
|
|
|
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Describes how the {@link Injector} should instantiate a given token.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
2015-04-17 13:01:07 -07:00
|
|
|
* See {@link bind}.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(String, { toValue: 'Hello' })
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(String)).toEqual('Hello');
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @exportedAs angular2/di
|
2015-04-09 23:17:05 -07:00
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
@CONST()
|
2014-09-30 14:56:33 -04:00
|
|
|
export class Binding {
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Token used when retrieving this binding. Usually the `Type`.
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
token;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds an interface to an implementation / subclass.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy
|
|
|
|
* comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```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);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toClass: Type;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(String, { toValue: 'Hello' })
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(String)).toEqual('Hello');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
toValue;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to the alias for an existing key.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* An alias means that {@link Injector} returns the same instance as if the alias token was used.
|
|
|
|
* This is in contrast to `toClass` where a separate instance of `toClass` is returned.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* Becuse `toAlias` and `toClass` are often confused the example contains both use cases for easy
|
|
|
|
* comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```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);
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-15 22:35:38 +00:00
|
|
|
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
|
|
|
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
toAlias;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a function which computes the value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
|
|
|
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
2015-05-14 14:28:47 +02:00
|
|
|
* dependencies: [Number] })
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(Number)).toEqual(3);
|
|
|
|
* expect(injector.get(String)).toEqual('Value: 3');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toFactory: Function;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a function which computes the value asynchronously.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## 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; },
|
2015-05-14 14:28:47 +02:00
|
|
|
* dependencies: [Number]})
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* injector.asyncGet(Number).then((v) => expect(v).toBe(3));
|
|
|
|
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
|
|
|
* ```
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* The interesting thing to note is that event though `Number` has an async factory, the `String`
|
|
|
|
* factory function takes the resolved value. This shows that the {@link Injector} delays
|
|
|
|
*executing the
|
|
|
|
*`String` factory
|
|
|
|
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using
|
|
|
|
* the `asyncGet` API in the {@link Injector}.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toAsyncFactory: Function;
|
2015-04-15 22:35:38 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-24 15:19:11 -07:00
|
|
|
* Used in conjunction with `toFactory` or `toAsyncFactory` and specifies a set of dependencies
|
|
|
|
* (as `token`s) which should be injected into the factory function.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* new Binding(Number, { toFactory: () => { return 1+2; }}),
|
|
|
|
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
2015-05-14 14:28:47 +02:00
|
|
|
* dependencies: [Number] })
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(Number)).toEqual(3);
|
|
|
|
* expect(injector.get(String)).toEqual('Value: 3');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
dependencies: List<any>;
|
|
|
|
|
|
|
|
constructor(token, {toClass, toValue, toAlias, toFactory, toAsyncFactory, deps}: {
|
|
|
|
toClass ?: Type, toValue ?: any, toAlias ?: any, toFactory ?: Function,
|
|
|
|
toAsyncFactory ?: Function, deps ?: List<any>}) {
|
2015-04-09 23:17:05 -07:00
|
|
|
this.token = token;
|
|
|
|
this.toClass = toClass;
|
|
|
|
this.toValue = toValue;
|
|
|
|
this.toAlias = toAlias;
|
|
|
|
this.toFactory = toFactory;
|
|
|
|
this.toAsyncFactory = toAsyncFactory;
|
|
|
|
this.dependencies = deps;
|
|
|
|
}
|
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Converts the {@link Binding} into {@link ResolvedBinding}.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* {@link Injector} internally only uses {@link ResolvedBinding}, {@link Binding} contains
|
|
|
|
* convenience binding syntax.
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
resolve(): ResolvedBinding {
|
2015-04-24 15:19:11 -07:00
|
|
|
var factoryFn: Function;
|
2015-04-09 23:17:05 -07:00
|
|
|
var resolvedDeps;
|
|
|
|
var isAsync = false;
|
|
|
|
if (isPresent(this.toClass)) {
|
2015-05-13 15:54:46 -07:00
|
|
|
var toClass = resolveForwardRef(this.toClass);
|
|
|
|
factoryFn = reflector.factory(toClass);
|
|
|
|
resolvedDeps = _dependenciesFor(toClass);
|
2015-04-09 23:17:05 -07:00
|
|
|
} else if (isPresent(this.toAlias)) {
|
|
|
|
factoryFn = (aliasInstance) => aliasInstance;
|
|
|
|
resolvedDeps = [Dependency.fromKey(Key.get(this.toAlias))];
|
|
|
|
} else if (isPresent(this.toFactory)) {
|
|
|
|
factoryFn = this.toFactory;
|
|
|
|
resolvedDeps = _constructDependencies(this.toFactory, this.dependencies);
|
|
|
|
} else if (isPresent(this.toAsyncFactory)) {
|
|
|
|
factoryFn = this.toAsyncFactory;
|
|
|
|
resolvedDeps = _constructDependencies(this.toAsyncFactory, this.dependencies);
|
|
|
|
isAsync = true;
|
|
|
|
} else {
|
|
|
|
factoryFn = () => this.toValue;
|
|
|
|
resolvedDeps = _EMPTY_LIST;
|
|
|
|
}
|
|
|
|
|
2015-05-13 15:54:46 -07:00
|
|
|
return new ResolvedBinding(Key.get(resolveForwardRef(this.token)), factoryFn, resolvedDeps,
|
|
|
|
isAsync);
|
2015-04-09 23:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* An internal resolved representation of a {@link Binding} used by the {@link Injector}.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* A {@link Binding} is resolved when it has a factory function. Binding to a class, alias, or
|
|
|
|
* value, are just convenience methods, as {@link Injector} only operates on calling factory
|
|
|
|
* functions.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
|
|
|
* @exportedAs angular2/di
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-09 23:17:05 -07:00
|
|
|
export class ResolvedBinding {
|
2015-05-11 10:28:07 -07:00
|
|
|
constructor(
|
|
|
|
/**
|
|
|
|
* A key, usually a `Type`.
|
|
|
|
*/
|
|
|
|
public key: Key,
|
2015-04-17 03:29:05 -07:00
|
|
|
|
2015-05-11 10:28:07 -07:00
|
|
|
/**
|
|
|
|
* Factory function which can return an instance of an object represented by a key.
|
|
|
|
*/
|
|
|
|
public factory: Function,
|
2015-04-17 03:29:05 -07:00
|
|
|
|
2015-05-11 10:28:07 -07:00
|
|
|
/**
|
|
|
|
* Arguments (dependencies) to the `factory` function.
|
|
|
|
*/
|
|
|
|
public dependencies: List<Dependency>,
|
2015-04-17 03:29:05 -07:00
|
|
|
|
2015-05-11 10:28:07 -07:00
|
|
|
/**
|
|
|
|
* Specifies whether the `factory` function returns a `Promise`.
|
|
|
|
*/
|
|
|
|
public providedAsPromise: boolean) {}
|
2014-09-30 14:56:33 -04:00
|
|
|
}
|
|
|
|
|
2015-04-09 23:17:05 -07:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Provides an API for imperatively constructing {@link Binding}s.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-20 16:37:49 +00:00
|
|
|
* This is only relevant for JavaScript. See {@link BindingBuilder}.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* bind(MyInterface).toClass(MyClass)
|
|
|
|
*
|
|
|
|
* ```
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* @exportedAs angular2/di
|
2015-04-09 23:17:05 -07:00
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
export function bind(token): BindingBuilder {
|
2014-09-30 14:56:33 -04:00
|
|
|
return new BindingBuilder(token);
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:17:05 -07:00
|
|
|
/**
|
2015-04-17 13:01:07 -07:00
|
|
|
* Helper class for the {@link bind} function.
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-15 22:35:38 +00:00
|
|
|
* @exportedAs angular2/di
|
2015-04-09 23:17:05 -07:00
|
|
|
*/
|
2014-09-30 14:56:33 -04:00
|
|
|
export class BindingBuilder {
|
2015-05-11 10:28:07 -07:00
|
|
|
constructor(public token) {}
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds an interface to an implementation / subclass.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* Because `toAlias` and `toClass` are often confused, the example contains both use cases for
|
|
|
|
* easy comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```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);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toClass(type: Type): Binding { return new Binding(this.token, {toClass: type}); }
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* bind(String).toValue('Hello')
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(String)).toEqual('Hello');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toValue(value): Binding { return new Binding(this.token, {toValue: value}); }
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to the alias for an existing key.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* 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.)
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* Becuse `toAlias` and `toClass` are often confused, the example contains both use cases for easy
|
|
|
|
* comparison.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ```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);
|
2015-04-17 03:29:05 -07:00
|
|
|
*
|
2015-04-15 22:35:38 +00:00
|
|
|
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
|
|
|
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toAlias(aliasToken): Binding { return new Binding(this.token, {toAlias: aliasToken}); }
|
2015-02-21 15:18:06 +01:00
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a function which computes the value.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* bind(Number).toFactory(() => { return 1+2; }}),
|
2015-05-03 16:14:32 -07:00
|
|
|
* bind(String).toFactory((v) => { return "Value: " + v; }, [Number] })
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* expect(injector.get(Number)).toEqual(3);
|
|
|
|
* expect(injector.get(String)).toEqual('Value: 3');
|
|
|
|
* ```
|
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
|
|
|
|
return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies});
|
2014-09-30 14:56:33 -04:00
|
|
|
}
|
|
|
|
|
2015-04-15 22:35:38 +00:00
|
|
|
/**
|
2015-04-17 03:29:05 -07:00
|
|
|
* Binds a key to a function which computes the value asynchronously.
|
2015-04-15 22:35:38 +00:00
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* var injector = Injector.resolveAndCreate([
|
|
|
|
* bind(Number).toAsyncFactory(() => {
|
|
|
|
* return new Promise((resolve) => resolve(1 + 2));
|
|
|
|
* }),
|
2015-05-03 16:14:32 -07:00
|
|
|
* bind(String).toFactory((v) => { return "Value: " + v; }, [Number])
|
2015-04-15 22:35:38 +00:00
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* injector.asyncGet(Number).then((v) => expect(v).toBe(3));
|
|
|
|
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
|
|
|
* ```
|
|
|
|
*
|
2015-04-24 15:19:11 -07:00
|
|
|
* The interesting thing to note is that event though `Number` has an async factory, the `String`
|
|
|
|
* factory function takes the resolved value. This shows that the {@link Injector} delays
|
|
|
|
* executing of the `String` factory
|
|
|
|
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using
|
2015-04-17 13:01:07 -07:00
|
|
|
* the `asyncGet` API in the {@link Injector}.
|
2015-04-15 22:35:38 +00:00
|
|
|
*/
|
2015-04-24 15:19:11 -07:00
|
|
|
toAsyncFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
|
|
|
|
return new Binding(this.token, {toAsyncFactory: factoryFunction, deps: dependencies});
|
2014-09-30 14:56:33 -04:00
|
|
|
}
|
2015-04-09 23:17:05 -07:00
|
|
|
}
|
2014-09-30 14:56:33 -04:00
|
|
|
|
2015-04-24 15:19:11 -07:00
|
|
|
function _constructDependencies(factoryFunction: Function, dependencies: List<any>) {
|
2015-04-09 23:17:05 -07:00
|
|
|
return isBlank(dependencies) ?
|
2015-04-24 15:19:11 -07:00
|
|
|
_dependenciesFor(factoryFunction) :
|
2015-05-13 15:54:46 -07:00
|
|
|
ListWrapper.map(dependencies,
|
|
|
|
(t) => Dependency.fromKey(Key.get(resolveForwardRef(t))));
|
2014-10-09 12:09:50 -04:00
|
|
|
}
|
2014-11-20 12:07:48 -08:00
|
|
|
|
2015-04-24 15:19:11 -07:00
|
|
|
function _dependenciesFor(typeOrFunc): List<any> {
|
2014-11-20 12:07:48 -08:00
|
|
|
var params = reflector.parameters(typeOrFunc);
|
|
|
|
if (isBlank(params)) return [];
|
2015-05-01 14:05:19 -07:00
|
|
|
if (ListWrapper.any(params, (p) => isBlank(p))) {
|
|
|
|
throw new NoAnnotationError(typeOrFunc);
|
|
|
|
}
|
2014-11-20 12:07:48 -08:00
|
|
|
return ListWrapper.map(params, (p) => _extractToken(typeOrFunc, p));
|
|
|
|
}
|
|
|
|
|
|
|
|
function _extractToken(typeOrFunc, annotations) {
|
|
|
|
var depProps = [];
|
2015-02-27 07:42:51 -08:00
|
|
|
var token = null;
|
|
|
|
var optional = false;
|
|
|
|
var lazy = false;
|
|
|
|
var asPromise = false;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
|
|
|
for (var i = 0; i < annotations.length; ++i) {
|
|
|
|
var paramAnnotation = annotations[i];
|
|
|
|
|
|
|
|
if (paramAnnotation instanceof Type) {
|
2015-02-27 07:42:51 -08:00
|
|
|
token = paramAnnotation;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
|
|
|
} else if (paramAnnotation instanceof Inject) {
|
2015-02-27 07:42:51 -08:00
|
|
|
token = paramAnnotation.token;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
|
|
|
} else if (paramAnnotation instanceof InjectPromise) {
|
2015-02-27 07:42:51 -08:00
|
|
|
token = paramAnnotation.token;
|
|
|
|
asPromise = true;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
|
|
|
} else if (paramAnnotation instanceof InjectLazy) {
|
2015-02-27 07:42:51 -08:00
|
|
|
token = paramAnnotation.token;
|
|
|
|
lazy = true;
|
|
|
|
|
|
|
|
} else if (paramAnnotation instanceof Optional) {
|
|
|
|
optional = true;
|
2014-11-20 12:07:48 -08:00
|
|
|
|
|
|
|
} else if (paramAnnotation instanceof DependencyAnnotation) {
|
2015-03-29 14:56:18 +02:00
|
|
|
if (isPresent(paramAnnotation.token)) {
|
2015-04-24 15:19:11 -07:00
|
|
|
token = paramAnnotation.token;
|
2015-03-29 14:56:18 +02:00
|
|
|
}
|
2014-11-20 12:07:48 -08:00
|
|
|
ListWrapper.push(depProps, paramAnnotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 15:54:46 -07:00
|
|
|
token = resolveForwardRef(token);
|
|
|
|
|
2015-02-27 07:42:51 -08:00
|
|
|
if (isPresent(token)) {
|
|
|
|
return _createDependency(token, asPromise, lazy, optional, depProps);
|
2014-11-20 12:07:48 -08:00
|
|
|
} else {
|
|
|
|
throw new NoAnnotationError(typeOrFunc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 15:19:11 -07:00
|
|
|
function _createDependency(token, asPromise, lazy, optional, depProps): Dependency {
|
2015-02-27 07:42:51 -08:00
|
|
|
return new Dependency(Key.get(token), asPromise, lazy, optional, depProps);
|
2014-11-20 12:07:48 -08:00
|
|
|
}
|