diff --git a/modules/core/src/application.js b/modules/core/src/application.js index 775829e4f3..dab33152c2 100644 --- a/modules/core/src/application.js +++ b/modules/core/src/application.js @@ -26,9 +26,9 @@ export var appElementToken = new OpaqueToken('AppElement'); export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType'); export var appDocumentToken = new OpaqueToken('AppDocument'); -// Exported only for tests that need to overwrite default document binding. -export function documentDependentBindings(appComponentType) { +function _injectorBindings(appComponentType) { return [ + bind(appDocumentToken).toValue(DOM.defaultDoc()), bind(appComponentAnnotatedTypeToken).toFactory((reader) => { // TODO(rado): inspect annotation here and warn if there are bindings, // lightDomServices, and other component annotations that are skipped @@ -70,11 +70,6 @@ export function documentDependentBindings(appComponentType) { ]; } -function _injectorBindings(appComponentType) { - return ListWrapper.concat([bind(appDocumentToken).toValue(DOM.defaultDoc())], - documentDependentBindings(appComponentType)); -} - function _createVmZone(givenReporter:Function){ var defaultErrorReporter = (exception, stackTrace) => { var longStackTrace = ListWrapper.join(stackTrace, "\n\n-----async gap-----\n"); @@ -99,10 +94,7 @@ export function bootstrap(appComponentType: Type, bindings=null, givenBootstrapE // TODO(rado): prepopulate template cache, so applications with only // index.html and main.js are possible. - if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings); - - var appInjector = _rootInjector.createChild(_injectorBindings(appComponentType)); - if (isPresent(bindings)) appInjector = appInjector.createChild(bindings); + var appInjector = _createAppInjector(appComponentType, bindings); PromiseWrapper.then(appInjector.asyncGet(LifeCycle), (lc) => { @@ -119,3 +111,11 @@ export function bootstrap(appComponentType: Type, bindings=null, givenBootstrapE return bootstrapProcess.promise; } + +function _createAppInjector(appComponentType: Type, bindings: List): Injector { + if (isBlank(_rootInjector)) _rootInjector = new Injector(_rootBindings); + var mergedBindings = isPresent(bindings) ? + ListWrapper.concat(_injectorBindings(appComponentType), bindings) : + _injectorBindings(appComponentType); + return _rootInjector.createChild(mergedBindings); +} diff --git a/modules/core/test/application_spec.js b/modules/core/test/application_spec.js index a689f0cc8c..3e80c52125 100644 --- a/modules/core/test/application_spec.js +++ b/modules/core/test/application_spec.js @@ -1,11 +1,11 @@ import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib'; -import {bootstrap, appDocumentToken, appElementToken, documentDependentBindings} +import {bootstrap, appDocumentToken, appElementToken} from 'core/application'; import {Component} from 'core/annotations/annotations'; import {DOM} from 'facade/dom'; import {ListWrapper} from 'facade/collection'; import {PromiseWrapper} from 'facade/async'; -import {bind} from 'di/di'; +import {bind, Inject} from 'di/di'; import {TemplateConfig} from 'core/annotations/template_config'; @Component({ @@ -36,8 +36,23 @@ class HelloRootCmp2 { } } +@Component({ + selector: 'hello-app', + template: new TemplateConfig({ + inline: '', + directives: [] + }) +}) +class HelloRootCmp3 { + appBinding; + + constructor(@Inject("appBinding") appBinding) { + this.appBinding = appBinding; + } +} + export function main() { - var fakeDoc, el, el2; + var fakeDoc, el, el2, testBindings; beforeEach(() => { fakeDoc = DOM.createHtmlDocument(); @@ -45,13 +60,9 @@ export function main() { el2 = DOM.createElement('hello-app-2', fakeDoc); DOM.appendChild(fakeDoc.body, el); DOM.appendChild(fakeDoc.body, el2); + testBindings = [bind(appDocumentToken).toValue(fakeDoc)]; }); - function testBindings(appComponentType) { - return ListWrapper.concat([bind(appDocumentToken).toValue(fakeDoc), - ], documentDependentBindings(appComponentType)); - } - describe('bootstrap factory method', () => { it('should throw if no element is found', (done) => { var injectorPromise = bootstrap(HelloRootCmp, [], (e,t) => {throw e;}); @@ -63,12 +74,12 @@ export function main() { }); it('should create an injector promise', () => { - var injectorPromise = bootstrap(HelloRootCmp, testBindings(HelloRootCmp)); + var injectorPromise = bootstrap(HelloRootCmp, testBindings); expect(injectorPromise).not.toBe(null); }); it('should resolve an injector promise and contain bindings', (done) => { - var injectorPromise = bootstrap(HelloRootCmp, testBindings(HelloRootCmp)); + var injectorPromise = bootstrap(HelloRootCmp, testBindings); injectorPromise.then((injector) => { expect(injector.get(appElementToken)).toBe(el); done(); @@ -76,7 +87,7 @@ export function main() { }); it('should provide the application component in the injector', (done) => { - var injectorPromise = bootstrap(HelloRootCmp, testBindings(HelloRootCmp)); + var injectorPromise = bootstrap(HelloRootCmp, testBindings); injectorPromise.then((injector) => { expect(injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp); done(); @@ -84,7 +95,7 @@ export function main() { }); it('should display hello world', (done) => { - var injectorPromise = bootstrap(HelloRootCmp, testBindings(HelloRootCmp)); + var injectorPromise = bootstrap(HelloRootCmp, testBindings); injectorPromise.then((injector) => { expect(injector.get(appElementToken) .shadowRoot.childNodes[0].nodeValue).toEqual('hello world!'); @@ -93,8 +104,8 @@ export function main() { }); it('should support multiple calls to bootstrap', (done) => { - var injectorPromise1 = bootstrap(HelloRootCmp, testBindings(HelloRootCmp)); - var injectorPromise2 = bootstrap(HelloRootCmp2, testBindings(HelloRootCmp2)); + var injectorPromise1 = bootstrap(HelloRootCmp, testBindings); + var injectorPromise2 = bootstrap(HelloRootCmp2, testBindings); PromiseWrapper.all([injectorPromise1, injectorPromise2]).then((injectors) => { expect(injectors[0].get(appElementToken) .shadowRoot.childNodes[0].nodeValue).toEqual('hello world!'); @@ -103,5 +114,17 @@ export function main() { done(); }); }); + + it("should make the provided binings available to the application component", (done) => { + var injectorPromise = bootstrap(HelloRootCmp3, [ + testBindings, + bind("appBinding").toValue("BoundValue") + ]); + + injectorPromise.then((injector) => { + expect(injector.get(HelloRootCmp3).appBinding).toEqual("BoundValue"); + done(); + }); + }); }); } diff --git a/modules/di/test/di/injector_spec.js b/modules/di/test/di/injector_spec.js index 6b49d82e68..07a79bd204 100644 --- a/modules/di/test/di/injector_spec.js +++ b/modules/di/test/di/injector_spec.js @@ -148,6 +148,16 @@ export function main() { expect(car).toBeAnInstanceOf(Car); }); + it("should use the last binding "+ + "when there are mutliple bindings for same token", function () { + var injector = new Injector([ + bind(Engine).toClass(Engine), + bind(Engine).toClass(TurboEngine) + ]); + + expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); + }); + it('should use non-type tokens', function () { var injector = new Injector([ bind('token').toValue('value')