diff --git a/modules/angular2/angular2.ts b/modules/angular2/angular2.ts index 18f92394cd..21f99ae925 100644 --- a/modules/angular2/angular2.ts +++ b/modules/angular2/angular2.ts @@ -56,7 +56,8 @@ export { Self, Parent, Ancestor, - Unbounded + Unbounded, + DependencyProvider } from './di'; export * from './core'; diff --git a/modules/angular2/di.ts b/modules/angular2/di.ts index 71fd8487bb..5f51b4546f 100644 --- a/modules/angular2/di.ts +++ b/modules/angular2/di.ts @@ -33,6 +33,7 @@ export { resolveBindings, Injector, ProtoInjector, + DependencyProvider, PUBLIC_AND_PRIVATE, PUBLIC, PRIVATE, diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts index 8eed251d4a..c503e31aba 100644 --- a/modules/angular2/src/core/compiler/element_injector.ts +++ b/modules/angular2/src/core/compiler/element_injector.ts @@ -28,6 +28,7 @@ import { resolveBindings, Visibility, VisibilityAnnotation, + DependencyProvider, self } from 'angular2/di'; import { @@ -429,7 +430,7 @@ export class ProtoElementInjector { } -export class ElementInjector extends TreeNode { +export class ElementInjector extends TreeNode implements DependencyProvider { private _host: ElementInjector; private _preBuiltObjects = null; @@ -447,9 +448,7 @@ export class ElementInjector extends TreeNode { constructor(public _proto: ProtoElementInjector, parent: ElementInjector) { super(parent); - this._injector = new Injector(this._proto.protoInjector); - this._injector.ei = this; // TODO savkin remove after mergin DI and EI - + this._injector = new Injector(this._proto.protoInjector, null, this); // we couple ourselves to the injector strategy to avoid polymoprhic calls var injectorStrategy = this._injector.internalStrategy; this._strategy = injectorStrategy instanceof InjectorInlineStrategy ? @@ -588,7 +587,7 @@ export class ElementInjector extends TreeNode { isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); } - getDependency(dep: any): any { + getDependency(injector: Injector, binding: ResolvedBinding, dep: Dependency): any { var key: Key = dep.key; if (!(dep instanceof DirectiveDependency)) return undefinedValue; diff --git a/modules/angular2/src/di/injector.ts b/modules/angular2/src/di/injector.ts index b20417278b..4e77aae30f 100644 --- a/modules/angular2/src/di/injector.ts +++ b/modules/angular2/src/di/injector.ts @@ -402,6 +402,13 @@ export class BindingWithVisibility { getKeyId(): number { return this.binding.key.id; } } +/** + * Used to provide dependencies that cannot be easily expressed as bindings. + */ +export interface DependencyProvider { + getDependency(injector: Injector, binding: ResolvedBinding, dependency: Dependency): any; +} + /** * A dependency injection container used for resolving dependencies. * @@ -476,10 +483,12 @@ export class Injector { * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a *recursive list of more * bindings. + * @param `depProvider` */ - static resolveAndCreate(bindings: List>): Injector { + static resolveAndCreate(bindings: List>, + depProvider: DependencyProvider = null): Injector { var resolvedBindings = Injector.resolve(bindings); - return Injector.fromResolvedBindings(resolvedBindings); + return Injector.fromResolvedBindings(resolvedBindings, depProvider); } /** @@ -488,11 +497,13 @@ export class Injector { * * @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the * {@link Injector}. + * @param `depProvider` */ - static fromResolvedBindings(bindings: List): Injector { + static fromResolvedBindings(bindings: List, + depProvider: DependencyProvider = null): Injector { var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC)); var proto = new ProtoInjector(bd, 0); - var inj = new Injector(proto); + var inj = new Injector(proto, null, depProvider); return inj; } @@ -500,10 +511,8 @@ export class Injector { _isBoundary: boolean = false; _constructionCounter: number = 0; - // TODO vsavkin remove it after DI and EI are merged - ei: any; - - constructor(public _proto: ProtoInjector, public _parent: Injector = null) { + constructor(public _proto: ProtoInjector, public _parent: Injector = null, + private _depProvider: DependencyProvider = null) { this._strategy = _proto._strategy.createInjectorStrategy(this); } @@ -555,11 +564,12 @@ export class Injector { * * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a * recursive list of more bindings. - * + * @param `depProvider` */ - resolveAndCreateChild(bindings: List>): Injector { + resolveAndCreateChild(bindings: List>, + depProvider: DependencyProvider = null): Injector { var resovledBindings = Injector.resolve(bindings); - return this.createChildFromResolved(resovledBindings); + return this.createChildFromResolved(resovledBindings, depProvider); } /** @@ -567,12 +577,14 @@ export class Injector { * * @param `bindings`: A sparse list of {@link ResolvedBinding}s. * See `resolve` for the {@link Injector}. + * @param `depProvider` * @returns a new child {@link Injector}. */ - createChildFromResolved(bindings: List): Injector { + createChildFromResolved(bindings: List, + depProvider: DependencyProvider = null): Injector { var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC)); var proto = new ProtoInjector(bd, 1); - var inj = new Injector(proto); + var inj = new Injector(proto, null, depProvider); inj._parent = this; return inj; } @@ -588,26 +600,26 @@ export class Injector { var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19; try { - d0 = length > 0 ? this._getByDependency(deps[0], visibility) : null; - d1 = length > 1 ? this._getByDependency(deps[1], visibility) : null; - d2 = length > 2 ? this._getByDependency(deps[2], visibility) : null; - d3 = length > 3 ? this._getByDependency(deps[3], visibility) : null; - d4 = length > 4 ? this._getByDependency(deps[4], visibility) : null; - d5 = length > 5 ? this._getByDependency(deps[5], visibility) : null; - d6 = length > 6 ? this._getByDependency(deps[6], visibility) : null; - d7 = length > 7 ? this._getByDependency(deps[7], visibility) : null; - d8 = length > 8 ? this._getByDependency(deps[8], visibility) : null; - d9 = length > 9 ? this._getByDependency(deps[9], visibility) : null; - d10 = length > 10 ? this._getByDependency(deps[10], visibility) : null; - d11 = length > 11 ? this._getByDependency(deps[11], visibility) : null; - d12 = length > 12 ? this._getByDependency(deps[12], visibility) : null; - d13 = length > 13 ? this._getByDependency(deps[13], visibility) : null; - d14 = length > 14 ? this._getByDependency(deps[14], visibility) : null; - d15 = length > 15 ? this._getByDependency(deps[15], visibility) : null; - d16 = length > 16 ? this._getByDependency(deps[16], visibility) : null; - d17 = length > 17 ? this._getByDependency(deps[17], visibility) : null; - d18 = length > 18 ? this._getByDependency(deps[18], visibility) : null; - d19 = length > 19 ? this._getByDependency(deps[19], visibility) : null; + d0 = length > 0 ? this._getByDependency(binding, deps[0], visibility) : null; + d1 = length > 1 ? this._getByDependency(binding, deps[1], visibility) : null; + d2 = length > 2 ? this._getByDependency(binding, deps[2], visibility) : null; + d3 = length > 3 ? this._getByDependency(binding, deps[3], visibility) : null; + d4 = length > 4 ? this._getByDependency(binding, deps[4], visibility) : null; + d5 = length > 5 ? this._getByDependency(binding, deps[5], visibility) : null; + d6 = length > 6 ? this._getByDependency(binding, deps[6], visibility) : null; + d7 = length > 7 ? this._getByDependency(binding, deps[7], visibility) : null; + d8 = length > 8 ? this._getByDependency(binding, deps[8], visibility) : null; + d9 = length > 9 ? this._getByDependency(binding, deps[9], visibility) : null; + d10 = length > 10 ? this._getByDependency(binding, deps[10], visibility) : null; + d11 = length > 11 ? this._getByDependency(binding, deps[11], visibility) : null; + d12 = length > 12 ? this._getByDependency(binding, deps[12], visibility) : null; + d13 = length > 13 ? this._getByDependency(binding, deps[13], visibility) : null; + d14 = length > 14 ? this._getByDependency(binding, deps[14], visibility) : null; + d15 = length > 15 ? this._getByDependency(binding, deps[15], visibility) : null; + d16 = length > 16 ? this._getByDependency(binding, deps[16], visibility) : null; + d17 = length > 17 ? this._getByDependency(binding, deps[17], visibility) : null; + d18 = length > 18 ? this._getByDependency(binding, deps[18], visibility) : null; + d19 = length > 19 ? this._getByDependency(binding, deps[19], visibility) : null; } catch (e) { if (e instanceof AbstractBindingError) e.addKey(binding.key); throw e; @@ -689,8 +701,11 @@ export class Injector { return obj; } - private _getByDependency(dep: Dependency, bindingVisibility: number): any { - var special = isPresent(this.ei) ? this.ei.getDependency(dep) : undefinedValue; + private _getByDependency(binding: ResolvedBinding, dep: Dependency, + bindingVisibility: number): any { + var special = isPresent(this._depProvider) ? + this._depProvider.getDependency(this, binding, dep) : + undefinedValue; if (special !== undefinedValue) { return special; } else { diff --git a/modules/angular2/src/test_lib/spies.dart b/modules/angular2/src/test_lib/spies.dart index c1723247ea..752aa4492a 100644 --- a/modules/angular2/src/test_lib/spies.dart +++ b/modules/angular2/src/test_lib/spies.dart @@ -1,6 +1,7 @@ library test_lib.spies; import 'package:angular2/change_detection.dart'; +import 'package:angular2/di.dart'; import './test_lib.dart'; @proxy @@ -21,4 +22,9 @@ class SpyPipe extends SpyObject implements Pipe { @proxy class SpyPipeFactory extends SpyObject implements PipeFactory { noSuchMethod(m) => super.noSuchMethod(m); +} + +@proxy +class SpyDependencyProvider extends SpyObject implements DependencyProvider { + noSuchMethod(m) => super.noSuchMethod(m); } \ No newline at end of file diff --git a/modules/angular2/src/test_lib/spies.ts b/modules/angular2/src/test_lib/spies.ts index b707ad3c63..a1eea19765 100644 --- a/modules/angular2/src/test_lib/spies.ts +++ b/modules/angular2/src/test_lib/spies.ts @@ -3,6 +3,9 @@ import { ProtoChangeDetector, DynamicChangeDetector } from 'angular2/change_detection'; + +import {DependencyProvider} from 'angular2/di'; + import {BasePipe} from 'angular2/src/change_detection/pipes/pipe'; import {SpyObject, proxy} from './test_lib'; @@ -18,4 +21,6 @@ export class SpyPipe extends SpyObject { constructor() { super(BasePipe); } } -export class SpyPipeFactory extends SpyObject {} \ No newline at end of file +export class SpyPipeFactory extends SpyObject {} + +export class SpyDependencyProvider extends SpyObject {} \ No newline at end of file diff --git a/modules/angular2/test/di/injector_spec.ts b/modules/angular2/test/di/injector_spec.ts index 0035e15730..dd5d80a4c9 100644 --- a/modules/angular2/test/di/injector_spec.ts +++ b/modules/angular2/test/di/injector_spec.ts @@ -1,5 +1,13 @@ import {isBlank, BaseException, stringify} from 'angular2/src/facade/lang'; -import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib'; +import { + describe, + ddescribe, + it, + iit, + expect, + beforeEach, + SpyDependencyProvider +} from 'angular2/test_lib'; import { Injector, bind, @@ -107,8 +115,8 @@ export function main() { strategyClass: InjectorDynamicStrategy }].forEach((context) => { - function createInjector(bindings: any[]) { - return Injector.resolveAndCreate(bindings.concat(context['bindings'])); + function createInjector(bindings: any[], dependencyProvider = null) { + return Injector.resolveAndCreate(bindings.concat(context['bindings']), dependencyProvider); } describe(`injector ${context['strategy']}`, () => { @@ -315,6 +323,20 @@ export function main() { var injector = createInjector([bind('null').toValue(null)]); expect(injector.get('null')).toBe(null); }); + + it('should use custom dependency provider', () => { + var e = new Engine(); + + var depProvider = new SpyDependencyProvider(); + depProvider.spy("getDependency").andReturn(e); + + var bindings = Injector.resolve([Car]); + var injector = Injector.fromResolvedBindings(bindings, depProvider); + + expect(injector.get(Car).engine).toEqual(e); + expect(depProvider.spy("getDependency")) + .toHaveBeenCalledWith(injector, bindings[0], bindings[0].dependencies[0]); + }); });