diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 86245cf72aba8..f433db1fb69f9 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -26,3 +26,4 @@ export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util'; export {makeDecorator as ɵmakeDecorator} from './util/decorators'; export {isObservable as ɵisObservable, isPromise as ɵisPromise, merge as ɵmerge} from './util/lang'; +export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider'; diff --git a/packages/core/src/view/provider.ts b/packages/core/src/view/provider.ts index 5439edec2ec28..a14650416f400 100644 --- a/packages/core/src/view/provider.ts +++ b/packages/core/src/view/provider.ts @@ -341,7 +341,7 @@ function callFactory( // - el2.injector.get(token, default) // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module // - mod2.injector.get(token, default) -const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {}; +export const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {}; export function resolveDep( view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef, diff --git a/packages/upgrade/src/static/upgrade_module.ts b/packages/upgrade/src/static/upgrade_module.ts index e663ca9356cf6..999526ae4cd44 100644 --- a/packages/upgrade/src/static/upgrade_module.ts +++ b/packages/upgrade/src/static/upgrade_module.ts @@ -6,16 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector, NgModule, NgZone, Testability} from '@angular/core'; +import {Injector, NgModule, NgZone, Testability, ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '@angular/core'; import * as angular from '../common/angular1'; -import {$$TESTABILITY, $DELEGATE, $INJECTOR, $PROVIDE, $ROOT_SCOPE, INJECTOR_KEY, UPGRADE_MODULE_NAME} from '../common/constants'; +import {$$TESTABILITY, $DELEGATE, $INJECTOR, $PROVIDE, INJECTOR_KEY, UPGRADE_MODULE_NAME} from '../common/constants'; import {controllerKey} from '../common/util'; import {angular1Providers, setTempInjectorRef} from './angular1_providers'; - /** * @whatItDoes * @@ -135,12 +134,16 @@ export class UpgradeModule { * The AngularJS `$injector` for the upgrade application. */ public $injector: any /*angular.IInjectorService*/; + /** The Angular Injector **/ + public injector: Injector; constructor( /** The root {@link Injector} for the upgrade application. */ - public injector: Injector, + injector: Injector, /** The bootstrap zone for the upgrade application */ - public ngZone: NgZone) {} + public ngZone: NgZone) { + this.injector = new NgAdapterInjector(injector); + } /** * Bootstrap an AngularJS application from this NgModule @@ -234,3 +237,19 @@ export class UpgradeModule { } } } + +class NgAdapterInjector implements Injector { + constructor(private modInjector: Injector) {} + + // When Angular locate a service in the component injector tree, the not found value is set to + // `NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR`. In such a case we should not walk up to the module + // injector. + // AngularJS only supports a single tree and should always check the module injector. + get(token: any, notFoundValue?: any): any { + if (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) { + return notFoundValue; + } + + return this.modInjector.get(token, notFoundValue); + } +} \ No newline at end of file diff --git a/packages/upgrade/test/static/integration/downgrade_component_spec.ts b/packages/upgrade/test/static/integration/downgrade_component_spec.ts index 0e44113338aee..db801f458dddd 100644 --- a/packages/upgrade/test/static/integration/downgrade_component_spec.ts +++ b/packages/upgrade/test/static/integration/downgrade_component_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, EventEmitter, NgModule, OnChanges, OnDestroy, SimpleChanges, destroyPlatform} from '@angular/core'; +import {Compiler, Component, ComponentFactoryResolver, EventEmitter, Injector, NgModule, NgModuleRef, OnChanges, OnDestroy, SimpleChanges, destroyPlatform} from '@angular/core'; import {async} from '@angular/core/testing'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; @@ -361,5 +361,55 @@ export function main() { expect(multiTrim(document.body.textContent)).toBe('parent(child)'); }); })); + + it('should work with ng2 lazy loaded components', async(() => { + + let componentInjector: Injector; + + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + constructor(injector: Injector) { componentInjector = injector; } + } + + @NgModule({ + declarations: [Ng2Component], + entryComponents: [Ng2Component], + imports: [BrowserModule, UpgradeModule], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + @Component({template: ''}) + class LazyLoadedComponent { + constructor(public module: NgModuleRef){}; + } + + @NgModule({ + declarations: [LazyLoadedComponent], + entryComponents: [LazyLoadedComponent], + }) + class LazyLoadedModule { + } + + const ng1Module = angular.module('ng1', []).directive( + 'ng2', downgradeComponent({component: Ng2Component})); + + const element = html(''); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { + const modInjector = upgrade.injector; + // Emulate the router lazy loading a module and creating a component + const compiler = modInjector.get(Compiler); + const modFactory = compiler.compileModuleSync(LazyLoadedModule); + const childMod = modFactory.create(modInjector); + const cmpFactory = + childMod.componentFactoryResolver.resolveComponentFactory(LazyLoadedComponent); + const lazyCmp = cmpFactory.create(componentInjector); + + expect(lazyCmp.instance.module).toBe(childMod.injector); + }); + + })); }); }