diff --git a/aio/content/guide/upgrade-performance.md b/aio/content/guide/upgrade-performance.md
index 3ec8f1e4cb..f8089db313 100644
--- a/aio/content/guide/upgrade-performance.md
+++ b/aio/content/guide/upgrade-performance.md
@@ -281,22 +281,28 @@ The differences between `downgradeModule()` and `UpgradeModule` end here. The re
`upgrade/static` APIs and concepts work in the exact same way for both types of hybrid apps.
See [Upgrading from AngularJS](guide/upgrade) to learn about:
-- [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code).
+- [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code).
+ _NOTE: If you are downgrading multiple modules, you need to specify the name of the downgraded
+ module each component belongs to, when calling `downgradeComponent()`._
- [Using AngularJS Component Directives from Angular Code](guide/upgrade#using-angularjs-component-directives-from-angular-code).
- [Projecting AngularJS Content into Angular Components](guide/upgrade#projecting-angularjs-content-into-angular-components).
- [Transcluding Angular Content into AngularJS Component Directives](guide/upgrade#transcluding-angular-content-into-angularjs-component-directives).
- [Making AngularJS Dependencies Injectable to Angular](guide/upgrade#making-angularjs-dependencies-injectable-to-angular).
-- [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs).
+- [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs).
+ _NOTE: If you are downgrading multiple modules, you need to specify the name of the downgraded
+ module each injectable belongs to, when calling `downgradeInjectable()`._
While it is possible to downgrade injectables, downgraded injectables will not be available until
- the Angular module is instantiated. In order to be safe, you need to ensure that the downgraded
- injectables are not used anywhere _outside_ the part of the app that is controlled by Angular.
+ the Angular module that provides them is instantiated. In order to be safe, you need to ensure
+ that the downgraded injectables are not used anywhere _outside_ the part of the app where it is
+ guaranteed that their module has been instantiated.
For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
- from Angular components, but it is _not OK_ to use it in an AngularJS component that may be used
- independently of Angular.
+ from a downgraded Angular component provided by the same Angular module as the injectable, but it
+ is _not OK_ to use it in an AngularJS component that may be used independently of Angular or use
+ it in a downgraded Angular component from a different module.
diff --git a/packages/examples/upgrade/static/ts/lite-multi/e2e_test/static_lite_multi_spec.ts b/packages/examples/upgrade/static/ts/lite-multi/e2e_test/static_lite_multi_spec.ts
new file mode 100644
index 0000000000..3a4d571243
--- /dev/null
+++ b/packages/examples/upgrade/static/ts/lite-multi/e2e_test/static_lite_multi_spec.ts
@@ -0,0 +1,28 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {browser, by, element} from 'protractor';
+
+import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
+
+
+describe('upgrade/static (lite with multiple downgraded modules)', () => {
+ const navButtons = element.all(by.css('nav button'));
+ const mainContent = element(by.css('main'));
+
+ beforeEach(() => browser.get('/upgrade/static/ts/lite-multi/'));
+ afterEach(verifyNoBrowserErrors);
+
+ it('should correctly bootstrap multiple downgraded modules', () => {
+ navButtons.get(1).click();
+ expect(mainContent.getText()).toBe('Component B');
+
+ navButtons.get(0).click();
+ expect(mainContent.getText()).toBe('Component A | ng1(ng2)');
+ });
+});
diff --git a/packages/examples/upgrade/static/ts/lite-multi/module.ts b/packages/examples/upgrade/static/ts/lite-multi/module.ts
new file mode 100644
index 0000000000..d53a066e6d
--- /dev/null
+++ b/packages/examples/upgrade/static/ts/lite-multi/module.ts
@@ -0,0 +1,118 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+// #docplaster
+import {Component, Directive, ElementRef, Injectable, Injector, NgModule, StaticProvider, getPlatform} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
+import {UpgradeComponent, downgradeComponent, downgradeInjectable, downgradeModule} from '@angular/upgrade/static';
+
+
+declare var angular: ng.IAngularStatic;
+
+// An Angular module that declares an Angular service and a component,
+// which in turn uses an upgraded AngularJS component.
+@Component({
+ selector: 'ng2A',
+ template: 'Component A | ',
+})
+export class Ng2AComponent {
+}
+
+@Directive({
+ selector: 'ng1A',
+})
+export class Ng1AComponentFacade extends UpgradeComponent {
+ constructor(elementRef: ElementRef, injector: Injector) { super('ng1A', elementRef, injector); }
+}
+
+@Injectable()
+export class Ng2AService {
+ getValue() { return 'ng2'; }
+}
+
+@NgModule({
+ imports: [BrowserModule],
+ providers: [Ng2AService],
+ declarations: [Ng1AComponentFacade, Ng2AComponent],
+ entryComponents: [Ng2AComponent],
+})
+export class Ng2AModule {
+ ngDoBootstrap() {}
+}
+
+
+// Another Angular module that declares an Angular component.
+@Component({
+ selector: 'ng2B',
+ template: 'Component B',
+})
+export class Ng2BComponent {
+}
+
+@NgModule({
+ imports: [BrowserModule],
+ declarations: [Ng2BComponent],
+ entryComponents: [Ng2BComponent],
+})
+export class Ng2BModule {
+ ngDoBootstrap() {}
+}
+
+
+// The downgraded Angular modules.
+const downgradedNg2AModule = downgradeModule(
+ (extraProviders: StaticProvider[]) =>
+ (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(Ng2AModule));
+
+const downgradedNg2BModule = downgradeModule(
+ (extraProviders: StaticProvider[]) =>
+ (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(Ng2BModule));
+
+
+// The AngularJS app including downgraded modules, components and injectables.
+const appModule =
+ angular.module('exampleAppModule', [downgradedNg2AModule, downgradedNg2BModule])
+ .component('exampleApp', {
+ template: `
+
+
+
+
+
+
+ `,
+ controller: class ExampleAppController{page = 'A';},
+ })
+ .component('ng1A', {
+ template: 'ng1({{ $ctrl.value }})',
+ controller: [
+ 'ng2AService', class Ng1AController{
+ value = this.ng2AService.getValue(); constructor(private ng2AService: Ng2AService) {}
+ }
+ ],
+ })
+ .directive('ng2A', downgradeComponent({
+ component: Ng2AComponent,
+ downgradedModule: downgradedNg2AModule,
+ propagateDigest: false,
+ }))
+ .directive('ng2B', downgradeComponent({
+ component: Ng2BComponent,
+ downgradedModule: downgradedNg2BModule,
+ propagateDigest: false,
+ }))
+ .factory('ng2AService', downgradeInjectable(Ng2AService, downgradedNg2AModule));
+
+
+// Bootstrap the AngularJS app.
+angular.bootstrap(document.body, [appModule.name]);
diff --git a/packages/examples/upgrade/static/ts/lite/module.ts b/packages/examples/upgrade/static/ts/lite/module.ts
index 0a2e9b95f5..dec1613ebb 100644
--- a/packages/examples/upgrade/static/ts/lite/module.ts
+++ b/packages/examples/upgrade/static/ts/lite/module.ts
@@ -17,7 +17,6 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
/* tslint:disable: no-duplicate-imports */
import {UpgradeComponent} from '@angular/upgrade/static';
import {downgradeComponent} from '@angular/upgrade/static';
-import {downgradeInjectable} from '@angular/upgrade/static';
// #docregion basic-how-to
import {downgradeModule} from '@angular/upgrade/static';
// #enddocregion
diff --git a/packages/upgrade/src/common/downgrade_component.ts b/packages/upgrade/src/common/downgrade_component.ts
index 2fb420de7d..aebf63a81f 100644
--- a/packages/upgrade/src/common/downgrade_component.ts
+++ b/packages/upgrade/src/common/downgrade_component.ts
@@ -46,8 +46,14 @@ interface Thenable {
*
* @param info contains information about the Component that is being downgraded:
*
- * * `component: Type`: The type of the Component that will be downgraded
- * * `propagateDigest?: boolean`: Whether to perform {@link ChangeDetectorRef#detectChanges
+ * - `component: Type`: The type of the Component that will be downgraded
+ * - `downgradedModule?: string`: The name of the downgraded module (if any) that the component
+ * "belongs to", as returned by a call to `downgradeModule()`. It is the module, whose
+ * corresponding Angular module will be bootstrapped, when the component needs to be instantiated.
+ *
+ * (This option is only necessary when using `downgradeModule()` to downgrade more than one
+ * Angular module.)
+ * - `propagateDigest?: boolean`: Whether to perform {@link ChangeDetectorRef#detectChanges
* change detection} on the component on every
* [$digest](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest). If set to `false`,
* change detection will still be performed when any of the component's inputs changes.
@@ -59,7 +65,7 @@ interface Thenable {
* @publicApi
*/
export function downgradeComponent(info: {
- component: Type; propagateDigest?: boolean;
+ component: Type; downgradedModule?: string; propagateDigest?: boolean;
/** @deprecated since v4. This parameter is no longer used */
inputs?: string[];
/** @deprecated since v4. This parameter is no longer used */
@@ -96,7 +102,9 @@ export function downgradeComponent(info: {
let ranAsync = false;
if (!parentInjector) {
- const lazyModuleRef = $injector.get(LAZY_MODULE_REF) as LazyModuleRef;
+ const downgradedModule = info.downgradedModule || '';
+ const lazyModuleRefKey = `${LAZY_MODULE_REF}${downgradedModule}`;
+ const lazyModuleRef = $injector.get(lazyModuleRefKey) as LazyModuleRef;
needsNgZone = lazyModuleRef.needsNgZone;
parentInjector = lazyModuleRef.injector || lazyModuleRef.promise as Promise;
}
diff --git a/packages/upgrade/src/common/downgrade_injectable.ts b/packages/upgrade/src/common/downgrade_injectable.ts
index d0cc47d014..443070cc66 100644
--- a/packages/upgrade/src/common/downgrade_injectable.ts
+++ b/packages/upgrade/src/common/downgrade_injectable.ts
@@ -43,16 +43,35 @@ import {INJECTOR_KEY} from './constants';
*
* {@example upgrade/static/ts/full/module.ts region="example-app"}
*
+ *
+ *
+ * When using `downgradeModule()`, downgraded injectables will not be available until the Angular
+ * module that provides them is instantiated. In order to be safe, you need to ensure that the
+ * downgraded injectables are not used anywhere _outside_ the part of the app where it is
+ * guaranteed that their module has been instantiated.
+ *
+ * For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
+ * from a downgraded Angular component provided by the same Angular module as the injectable, but
+ * it is _not OK_ to use it in an AngularJS component that may be used independently of Angular or
+ * use it in a downgraded Angular component from a different module.
+ *
+ *
+ *
* @param token an `InjectionToken` that identifies a service provided from Angular.
+ * @param downgradedModule the name of the downgraded module (if any) that the injectable
+ * "belongs to", as returned by a call to `downgradeModule()`. It is the module, whose injector will
+ * be used for instantiating the injectable.
+ * (This option is only necessary when using `downgradeModule()` to downgrade more than one Angular
+ * module.)
*
* @returns a [factory function](https://docs.angularjs.org/guide/di) that can be
* used to register the service on an AngularJS module.
*
* @publicApi
*/
-export function downgradeInjectable(token: any): Function {
+export function downgradeInjectable(token: any, downgradedModule: string = ''): Function {
const factory = function(i: Injector) { return i.get(token); };
- (factory as any)['$inject'] = [INJECTOR_KEY];
+ (factory as any)['$inject'] = [`${INJECTOR_KEY}${downgradedModule}`];
return factory;
}
diff --git a/packages/upgrade/src/static/downgrade_module.ts b/packages/upgrade/src/static/downgrade_module.ts
index 00129fda76..3fa96d3f55 100644
--- a/packages/upgrade/src/static/downgrade_module.ts
+++ b/packages/upgrade/src/static/downgrade_module.ts
@@ -17,6 +17,8 @@ import {angular1Providers, setTempInjectorRef} from './angular1_providers';
import {NgAdapterInjector} from './util';
+let moduleUid = 0;
+
/**
* @description
*
@@ -104,7 +106,10 @@ import {NgAdapterInjector} from './util';
export function downgradeModule(
moduleFactoryOrBootstrapFn: NgModuleFactory|
((extraProviders: StaticProvider[]) => Promise>)): string {
- const LAZY_MODULE_NAME = UPGRADE_MODULE_NAME + '.lazy';
+ const lazyModuleName = `${UPGRADE_MODULE_NAME}.lazy${++moduleUid}`;
+ const lazyModuleRefKey = `${LAZY_MODULE_REF}${lazyModuleName}`;
+ const lazyInjectorKey = `${INJECTOR_KEY}${lazyModuleName}`;
+
const bootstrapFn = isFunction(moduleFactoryOrBootstrapFn) ?
moduleFactoryOrBootstrapFn :
(extraProviders: StaticProvider[]) =>
@@ -113,9 +118,10 @@ export function downgradeModule(
let injector: Injector;
// Create an ng1 module to bootstrap.
- angular.module(LAZY_MODULE_NAME, [])
+ angular.module(lazyModuleName, [])
+ .factory(INJECTOR_KEY, [lazyInjectorKey, identity])
.factory(
- INJECTOR_KEY,
+ lazyInjectorKey,
() => {
if (!injector) {
throw new Error(
@@ -123,7 +129,8 @@ export function downgradeModule(
}
return injector;
})
- .factory(LAZY_MODULE_REF, [
+ .factory(LAZY_MODULE_REF, [lazyModuleRefKey, identity])
+ .factory(lazyModuleRefKey, [
$INJECTOR,
($injector: angular.IInjectorService) => {
setTempInjectorRef($injector);
@@ -140,5 +147,9 @@ export function downgradeModule(
}
]);
- return LAZY_MODULE_NAME;
+ return lazyModuleName;
+}
+
+function identity(x: T): T {
+ return x;
}
diff --git a/packages/upgrade/test/common/downgrade_injectable_spec.ts b/packages/upgrade/test/common/downgrade_injectable_spec.ts
index 628d8f1237..ce6167ba2d 100644
--- a/packages/upgrade/test/common/downgrade_injectable_spec.ts
+++ b/packages/upgrade/test/common/downgrade_injectable_spec.ts
@@ -21,5 +21,16 @@ import {downgradeInjectable} from '@angular/upgrade/src/common/downgrade_injecta
expect(injector.get).toHaveBeenCalledWith('someToken');
expect(value).toEqual('service value');
});
+
+ it('should inject the specified module\'s injector when specifying a module name', () => {
+ const factory = downgradeInjectable('someToken', 'someModule');
+ expect(factory).toEqual(jasmine.any(Function));
+ expect((factory as any).$inject).toEqual([`${INJECTOR_KEY}someModule`]);
+
+ const injector = {get: jasmine.createSpy('get').and.returnValue('service value')};
+ const value = factory(injector);
+ expect(injector.get).toHaveBeenCalledWith('someToken');
+ expect(value).toEqual('service value');
+ });
});
}
diff --git a/packages/upgrade/test/static/integration/downgrade_module_spec.ts b/packages/upgrade/test/static/integration/downgrade_module_spec.ts
index d751d9a06b..c142e2678a 100644
--- a/packages/upgrade/test/static/integration/downgrade_module_spec.ts
+++ b/packages/upgrade/test/static/integration/downgrade_module_spec.ts
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ApplicationRef, Component, DoCheck, Inject, Injector, Input, NgModule, NgZone, OnChanges, OnDestroy, OnInit, StaticProvider, ViewRef, destroyPlatform} from '@angular/core';
+import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ApplicationRef, Component, DoCheck, Inject, Injector, Input, NgModule, NgZone, OnChanges, OnDestroy, OnInit, StaticProvider, Type, ViewRef, destroyPlatform, getPlatform} from '@angular/core';
import {async, fakeAsync, tick} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
@@ -25,6 +25,58 @@ withEachNg1Version(() => {
beforeEach(() => destroyPlatform());
+ it('should support multiple downgraded modules', async(() => {
+ @Component({selector: 'ng2A', template: 'a'})
+ class Ng2ComponentA {
+ }
+
+ @Component({selector: 'ng2B', template: 'b'})
+ class Ng2ComponentB {
+ }
+
+ @NgModule({
+ declarations: [Ng2ComponentA],
+ entryComponents: [Ng2ComponentA],
+ imports: [BrowserModule],
+ })
+ class Ng2ModuleA {
+ ngDoBootstrap() {}
+ }
+
+ @NgModule({
+ declarations: [Ng2ComponentB],
+ entryComponents: [Ng2ComponentB],
+ imports: [BrowserModule],
+ })
+ class Ng2ModuleB {
+ ngDoBootstrap() {}
+ }
+
+ const doDowngradeModule = (module: Type) => {
+ const bootstrapFn = (extraProviders: StaticProvider[]) =>
+ (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(module);
+ return downgradeModule(bootstrapFn);
+ };
+
+ const downModA = doDowngradeModule(Ng2ModuleA);
+ const downModB = doDowngradeModule(Ng2ModuleB);
+ const ng1Module = angular.module('ng1', [downModA, downModB])
+ .directive('ng2A', downgradeComponent({
+ component: Ng2ComponentA,
+ downgradedModule: downModA, propagateDigest,
+ }))
+ .directive('ng2B', downgradeComponent({
+ component: Ng2ComponentB,
+ downgradedModule: downModB, propagateDigest,
+ }));
+
+ const element = html(' | ');
+ angular.bootstrap(element, [ng1Module.name]);
+
+ // Wait for the module to be bootstrapped.
+ setTimeout(() => expect(element.textContent).toBe('a | b'));
+ }));
+
it('should support downgrading a component and propagate inputs', async(() => {
@Component(
{selector: 'ng2A', template: 'a({{ value }}) | '})
diff --git a/tools/public_api_guard/upgrade/static.d.ts b/tools/public_api_guard/upgrade/static.d.ts
index d36be96388..47b7425ce9 100644
--- a/tools/public_api_guard/upgrade/static.d.ts
+++ b/tools/public_api_guard/upgrade/static.d.ts
@@ -1,12 +1,13 @@
export declare function downgradeComponent(info: {
component: Type;
+ downgradedModule?: string;
propagateDigest?: boolean;
/** @deprecated */ inputs?: string[];
/** @deprecated */ outputs?: string[];
/** @deprecated */ selectors?: string[];
}): any;
-export declare function downgradeInjectable(token: any): Function;
+export declare function downgradeInjectable(token: any, downgradedModule?: string): Function;
export declare function downgradeModule(moduleFactoryOrBootstrapFn: NgModuleFactory | ((extraProviders: StaticProvider[]) => Promise>)): string;