diff --git a/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts b/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts new file mode 100644 index 0000000000..8ddefcfae6 --- /dev/null +++ b/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts @@ -0,0 +1,29 @@ +/** + * @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 and shared root module)', () => { + const compA = element(by.css('ng2-a')); + const compB = element(by.css('ng2-b')); + const compC = element(by.css('ng2-c')); + + beforeEach(() => browser.get('/upgrade/static/ts/lite-multi-shared/')); + afterEach(verifyNoBrowserErrors); + + it('should share the same injectable instance across downgraded modules A and B', () => { + expect(compA.getText()).toBe('Component A (Service ID: 2)'); + expect(compB.getText()).toBe('Component B (Service ID: 2)'); + }); + + it('should use a different injectable instance on downgraded module C', + () => { expect(compC.getText()).toBe('Component C (Service ID: 1)'); }); +}); diff --git a/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts b/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts new file mode 100644 index 0000000000..0024f14850 --- /dev/null +++ b/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts @@ -0,0 +1,152 @@ +/** + * @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 {Compiler, Component, Injectable, Injector, NgModule, StaticProvider, getPlatform} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; +import {downgradeComponent, downgradeModule} from '@angular/upgrade/static'; + + +declare var angular: ng.IAngularStatic; + +// An Angular service provided in root. Each instance of the service will get a new ID. +@Injectable({providedIn: 'root'}) +export class Ng2Service { + static nextId = 1; + id = Ng2Service.nextId++; +} + + +// An Angular module that will act as "root" for all downgraded modules, so that injectables +// provided in root will be available to all. +@NgModule({ + imports: [BrowserModule], +}) +export class Ng2RootModule { + ngDoBootstrap() {} +} + + +// An Angular module that declares an Angular component, +// which in turn uses an Angular service from the root module. +@Component({ + selector: 'ng2A', + template: 'Component A (Service ID: {{ service.id }})', +}) +export class Ng2AComponent { + constructor(public service: Ng2Service) {} +} + +@NgModule({ + declarations: [Ng2AComponent], + entryComponents: [Ng2AComponent], +}) +export class Ng2AModule { + ngDoBootstrap() {} +} + + +// Another Angular module that declares an Angular component, which uses the same service. +@Component({ + selector: 'ng2B', + template: 'Component B (Service ID: {{ service.id }})', +}) +export class Ng2BComponent { + constructor(public service: Ng2Service) {} +} + +@NgModule({ + declarations: [Ng2BComponent], + entryComponents: [Ng2BComponent], +}) +export class Ng2BModule { + ngDoBootstrap() {} +} + + +// A third Angular module that declares an Angular component, which uses the same service. +@Component({ + selector: 'ng2C', + template: 'Component C (Service ID: {{ service.id }})', +}) +export class Ng2CComponent { + constructor(public service: Ng2Service) {} +} + +@NgModule({ + imports: [BrowserModule], + declarations: [Ng2CComponent], + entryComponents: [Ng2CComponent], +}) +export class Ng2CModule { + ngDoBootstrap() {} +} + + +// The downgraded Angular modules. Modules A and B share a common root module. Module C does not. +// #docregion shared-root-module +let rootInjectorPromise: Promise|null = null; +const getRootInjector = (extraProviders: StaticProvider[]) => { + if (!rootInjectorPromise) { + rootInjectorPromise = platformBrowserDynamic(extraProviders) + .bootstrapModule(Ng2RootModule) + .then(moduleRef => moduleRef.injector); + } + return rootInjectorPromise; +}; + +const downgradedNg2AModule = downgradeModule(async(extraProviders: StaticProvider[]) => { + const rootInjector = await getRootInjector(extraProviders); + const moduleAFactory = await rootInjector.get(Compiler).compileModuleAsync(Ng2AModule); + return moduleAFactory.create(rootInjector); +}); +const downgradedNg2BModule = downgradeModule(async(extraProviders: StaticProvider[]) => { + const rootInjector = await getRootInjector(extraProviders); + const moduleBFactory = await rootInjector.get(Compiler).compileModuleAsync(Ng2BModule); + return moduleBFactory.create(rootInjector); +}); +// #enddocregion shared-root-module + +const downgradedNg2CModule = downgradeModule( + (extraProviders: StaticProvider[]) => + (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(Ng2CModule)); + + +// The AngularJS app including downgraded modules and components. +// #docregion shared-root-module +const appModule = + angular + .module( + 'exampleAppModule', [downgradedNg2AModule, downgradedNg2BModule, downgradedNg2CModule]) + // #enddocregion shared-root-module + .component('exampleApp', {template: ' | | '}) + .directive('ng2A', downgradeComponent({ + component: Ng2AComponent, + // Since there is more than one downgraded Angular module, + // specify which module this component belongs to. + downgradedModule: downgradedNg2AModule, + propagateDigest: false, + })) + .directive('ng2B', downgradeComponent({ + component: Ng2BComponent, + // Since there is more than one downgraded Angular module, + // specify which module this component belongs to. + downgradedModule: downgradedNg2BModule, + propagateDigest: false, + })) + .directive('ng2C', downgradeComponent({ + component: Ng2CComponent, + // Since there is more than one downgraded Angular module, + // specify which module this component belongs to. + downgradedModule: downgradedNg2CModule, + propagateDigest: false, + })); + + +// Bootstrap the AngularJS app. +angular.bootstrap(document.body, [appModule.name]); diff --git a/packages/examples/upgrade/static/ts/lite-multi/module.ts b/packages/examples/upgrade/static/ts/lite-multi/module.ts index 272db218fa..9f7468bbcb 100644 --- a/packages/examples/upgrade/static/ts/lite-multi/module.ts +++ b/packages/examples/upgrade/static/ts/lite-multi/module.ts @@ -103,14 +103,14 @@ const appModule = }) .directive('ng2A', downgradeComponent({ component: Ng2AComponent, - // Since there are more than one downgraded Angular module, + // Since there is more than one downgraded Angular module, // specify which module this component belongs to. downgradedModule: downgradedNg2AModule, propagateDigest: false, })) .directive('ng2B', downgradeComponent({ component: Ng2BComponent, - // Since there are more than one downgraded Angular module, + // Since there is more than one downgraded Angular module, // specify which module this component belongs to. downgradedModule: downgradedNg2BModule, propagateDigest: false, diff --git a/packages/upgrade/src/static/downgrade_module.ts b/packages/upgrade/src/static/downgrade_module.ts index 7d3591a9fe..3faaa5f003 100644 --- a/packages/upgrade/src/static/downgrade_module.ts +++ b/packages/upgrade/src/static/downgrade_module.ts @@ -101,6 +101,31 @@ let moduleUid = 0; * * * + * ### Downgrading multiple modules + * + * It is possible to downgrade multiple modules and include them in an AngularJS application. In + * that case, each downgraded module will be bootstrapped when an associated downgraded component or + * injectable needs to be instantiated. + * + * Things to keep in mind, when downgrading multiple modules: + * + * - Each downgraded component/injectable needs to be explicitly associated with a downgraded + * module. See `downgradeComponent()` and `downgradeInjectable()` for more details. + * + * - If you want some injectables to be shared among all downgraded modules, you can provide them as + * `StaticProvider`s, when creating the `PlatformRef` (e.g. via `platformBrowser` or + * `platformBrowserDynamic`). + * + * - When using {@link PlatformRef#bootstrapmodule `bootstrapModule()`} or + * {@link PlatformRef#bootstrapmodulefactory `bootstrapModuleFactory()`} to bootstrap the + * downgraded modules, each one is considered a "root" module. As a consequence, a new instance + * will be created for every injectable provided in `"root"` (via + * {@link Injectable#providedIn `providedIn`}). + * If this is not your intention, you can have a shared module (that will act as act as the "root" + * module) and create all downgraded modules using that module's injector: + * + * {@example upgrade/static/ts/lite-multi-shared/module.ts region="shared-root-module"} + * * @publicApi */ export function downgradeModule(