feat(di): add support for optional dependencies

This commit is contained in:
vsavkin
2015-02-27 07:42:51 -08:00
committed by Misko Hevery
parent 23786aaa92
commit ba0a1ec459
7 changed files with 102 additions and 31 deletions

View File

@ -58,6 +58,24 @@ export class InjectLazy {
}
}
/**
* A parameter annotation that marks a dependency as optional.
*
* ```
* class AComponent {
* constructor(@Optional() dp:Dependency) {
* this.dp = dp;
* }
* }
* ```
*
*/
export class Optional {
@CONST()
constructor() {
}
}
/**
* `DependencyAnnotation` is used by the framework to extend DI.
*

View File

@ -2,20 +2,26 @@ import {FIELD, Type, isBlank, isPresent} from 'angular2/src/facade/lang';
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/reflection/reflection';
import {Key} from './key';
import {Inject, InjectLazy, InjectPromise, DependencyAnnotation} from './annotations';
import {Inject, InjectLazy, InjectPromise, Optional, DependencyAnnotation} from './annotations';
import {NoAnnotationError} from './exceptions';
export class Dependency {
key:Key;
asPromise:boolean;
lazy:boolean;
optional:boolean;
properties:List;
constructor(key:Key, asPromise:boolean, lazy:boolean, properties:List) {
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List) {
this.key = key;
this.asPromise = asPromise;
this.lazy = lazy;
this.optional = optional;
this.properties = properties;
}
static fromKey(key:Key) {
return new Dependency(key, false, false, false, []);
}
}
export class Binding {
@ -64,7 +70,7 @@ export class BindingBuilder {
return new Binding(
Key.get(this.token),
(aliasInstance) => aliasInstance,
[new Dependency(Key.get(aliasToken), false, false, [])],
[Dependency.fromKey(Key.get(aliasToken))],
false
);
}
@ -90,7 +96,7 @@ export class BindingBuilder {
_constructDependencies(factoryFunction:Function, dependencies:List) {
return isBlank(dependencies) ?
_dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => new Dependency(Key.get(t), false, false, []));
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
}
}
@ -102,36 +108,44 @@ function _dependenciesFor(typeOrFunc):List {
}
function _extractToken(typeOrFunc, annotations) {
var type;
var depProps = [];
var token = null;
var optional = false;
var lazy = false;
var asPromise = false;
for (var i = 0; i < annotations.length; ++i) {
var paramAnnotation = annotations[i];
if (paramAnnotation instanceof Type) {
type = paramAnnotation;
token = paramAnnotation;
} else if (paramAnnotation instanceof Inject) {
return _createDependency(paramAnnotation.token, false, false, []);
token = paramAnnotation.token;
} else if (paramAnnotation instanceof InjectPromise) {
return _createDependency(paramAnnotation.token, true, false, []);
token = paramAnnotation.token;
asPromise = true;
} else if (paramAnnotation instanceof InjectLazy) {
return _createDependency(paramAnnotation.token, false, true, []);
token = paramAnnotation.token;
lazy = true;
} else if (paramAnnotation instanceof Optional) {
optional = true;
} else if (paramAnnotation instanceof DependencyAnnotation) {
ListWrapper.push(depProps, paramAnnotation);
}
}
if (isPresent(type)) {
return _createDependency(type, false, false, depProps);
if (isPresent(token)) {
return _createDependency(token, asPromise, lazy, optional, depProps);
} else {
throw new NoAnnotationError(typeOrFunc);
}
}
function _createDependency(token, asPromise, lazy, depProps):Dependency {
return new Dependency(Key.get(token), asPromise, lazy, depProps);
function _createDependency(token, asPromise, lazy, optional, depProps):Dependency {
return new Dependency(Key.get(token), asPromise, lazy, optional, depProps);
}

View File

@ -38,11 +38,15 @@ export class Injector {
}
get(token) {
return this._getByKey(Key.get(token), false, false);
return this._getByKey(Key.get(token), false, false, false);
}
getOptional(token) {
return this._getByKey(Key.get(token), false, false, true);
}
asyncGet(token) {
return this._getByKey(Key.get(token), true, false);
return this._getByKey(Key.get(token), true, false, false);
}
createChild(bindings:List):Injector {
@ -60,9 +64,9 @@ export class Injector {
return ListWrapper.createFixedSize(Key.numberOfKeys + 1);
}
_getByKey(key:Key, returnPromise:boolean, returnLazy:boolean) {
_getByKey(key:Key, returnPromise:boolean, returnLazy:boolean, optional:boolean) {
if (returnLazy) {
return () => this._getByKey(key, returnPromise, false);
return () => this._getByKey(key, returnPromise, false, optional);
}
var strategy = returnPromise ? this._asyncStrategy : this._syncStrategy;
@ -74,14 +78,19 @@ export class Injector {
if (isPresent(instance)) return instance;
if (isPresent(this._parent)) {
return this._parent._getByKey(key, returnPromise, returnLazy);
return this._parent._getByKey(key, returnPromise, returnLazy, optional);
}
if (optional) {
return null;
} else {
throw new NoProviderError(key);
}
throw new NoProviderError(key);
}
_resolveDependencies(key:Key, binding:Binding, forceAsync:boolean):List {
try {
var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy);
var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional);
return ListWrapper.map(binding.dependencies, getDependency);
} catch (e) {
this._clear(key);