diff --git a/aio/content/examples/upgrade-lazy-load-ajs/e2e/src/app.e2e-spec.ts b/aio/content/examples/upgrade-lazy-load-ajs/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..78a04fcdbf --- /dev/null +++ b/aio/content/examples/upgrade-lazy-load-ajs/e2e/src/app.e2e-spec.ts @@ -0,0 +1,79 @@ +import { browser, element, by, ExpectedConditions } from 'protractor'; + +describe('Lazy Loading AngularJS Tests', function () { + const pageElements = { + homePageHref: element(by.cssContainingText('app-root nav a', 'Home')), + homePageParagraph: element(by.css('app-root app-home p')), + ajsUsersPageHref: element(by.cssContainingText('app-root nav a', 'Users')), + ajsUsersPageParagraph: element(by.css('app-root app-angular-js div p')), + notFoundPageHref: element(by.cssContainingText('app-root nav a', '404 Page')), + notFoundPageParagraph: element(by.css('app-root app-app404 p')), + }; + + beforeAll(async() => { + await browser.get('/'); + }); + + it('should display \'Angular Home\' when visiting the home page', async() => { + await pageElements.homePageHref.click(); + + const paragraphText = await pageElements.homePageParagraph.getText(); + + expect(paragraphText).toEqual('Angular Home'); + }); + + it('should display \'Users Page\' page when visiting the AngularJS page at /users', async() => { + await pageElements.ajsUsersPageHref.click(); + await loadAngularJS(); + + const paragraphText = await pageElements.ajsUsersPageParagraph.getText(); + + expect(paragraphText).toEqual('Users Page'); + }); + + it('should display \'Angular 404\' when visiting an invalid URL', async() => { + await pageElements.notFoundPageHref.click(); + + const paragraphText = await pageElements.notFoundPageParagraph.getText(); + + expect(paragraphText).toEqual('Angular 404'); + }); + + // Workaround for https://github.com/angular/protractor/issues/4724 + async function loadAngularJS() { + // Abort if `resumeBootstrap` has already occured + if (await browser.executeScript(`return '__TESTABILITY__NG1_APP_ROOT_INJECTOR__' in window;`)) { + return; + } + + // Might have to re-insert the 'NG_DEFER_BOOTSTRAP!' if the name has been changed since protractor loaded the page + if (!await browser.executeScript('window.name.includes(\'NG_DEFER_BOOTSTRAP!\')')) { + await browser.executeScript('window.name = \'NG_DEFER_BOOTSTRAP!\' + name'); + } + + // Wait for the AngularJS bundle to download and initialize + await browser.wait(ExpectedConditions.presenceOf(element(by.css('app-root app-angular-js'))), 5000, 'AngularJS app'); + + // Run the protractor pre-bootstrap logic and resumeBootstrap + // Based on https://github.com/angular/protractor/blob/5.3.0/lib/browser.ts#L950-L969 + { + let moduleNames = []; + for (const {name, script, args} of browser.mockModules_) { + moduleNames.push(name); + await browser.executeScriptWithDescription(script, 'add mock module ' + name, ...args); + } + + await browser.executeScriptWithDescription( + // TODO: must manually assign __TESTABILITY__NG1_APP_ROOT_INJECTOR__ (https://github.com/angular/angular/issues/22723) + `window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = angular.resumeBootstrap(arguments[0]) ` + + `|| angular.element('app-angular-js').injector();`, + 'resume bootstrap', + moduleNames + ); + } + + // Wait for the initial AngularJS page to finish loading + await browser.waitForAngular(); + } +}); + diff --git a/aio/content/examples/upgrade-lazy-load-ajs/example-config.json b/aio/content/examples/upgrade-lazy-load-ajs/example-config.json index e69de29bb2..c3afe22ccd 100644 --- a/aio/content/examples/upgrade-lazy-load-ajs/example-config.json +++ b/aio/content/examples/upgrade-lazy-load-ajs/example-config.json @@ -0,0 +1,3 @@ +{ + "projectType": "cli-ajs" +} \ No newline at end of file diff --git a/aio/content/examples/upgrade-lazy-load-ajs/src/app/angular-js/angular-js.component.ts b/aio/content/examples/upgrade-lazy-load-ajs/src/app/angular-js/angular-js.component.ts index bc5d22c0f0..63c325d2b9 100644 --- a/aio/content/examples/upgrade-lazy-load-ajs/src/app/angular-js/angular-js.component.ts +++ b/aio/content/examples/upgrade-lazy-load-ajs/src/app/angular-js/angular-js.component.ts @@ -1,14 +1,22 @@ -import { Component, OnInit, ElementRef } from '@angular/core'; +import { Component, OnInit, OnDestroy, ElementRef } from '@angular/core'; import { LazyLoaderService } from '../lazy-loader.service'; @Component({ selector: 'app-angular-js', template: '
' }) -export class AngularJSComponent implements OnInit { - constructor(private lazyLoader: LazyLoaderService, private elRef: ElementRef) {} +export class AngularJSComponent implements OnInit, OnDestroy { + constructor( + private lazyLoader: LazyLoaderService, + private elRef: ElementRef + ) {} ngOnInit() { this.lazyLoader.load(this.elRef.nativeElement); } + + + ngOnDestroy() { + this.lazyLoader.destroy(); + } } diff --git a/aio/content/examples/upgrade-lazy-load-ajs/src/app/app.module.ts b/aio/content/examples/upgrade-lazy-load-ajs/src/app/app.module.ts index 4e199e90bf..817ab8e5d1 100644 --- a/aio/content/examples/upgrade-lazy-load-ajs/src/app/app.module.ts +++ b/aio/content/examples/upgrade-lazy-load-ajs/src/app/app.module.ts @@ -19,7 +19,6 @@ import { App404Component } from './app404/app404.component'; BrowserModule, AppRoutingModule ], - providers: [], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/aio/content/examples/upgrade-lazy-load-ajs/src/app/lazy-loader.service.ts b/aio/content/examples/upgrade-lazy-load-ajs/src/app/lazy-loader.service.ts index 50c8492fb9..40ffc6e4a1 100644 --- a/aio/content/examples/upgrade-lazy-load-ajs/src/app/lazy-loader.service.ts +++ b/aio/content/examples/upgrade-lazy-load-ajs/src/app/lazy-loader.service.ts @@ -1,23 +1,25 @@ import { Injectable } from '@angular/core'; +import * as angular from 'angular'; @Injectable({ providedIn: 'root' }) export class LazyLoaderService { - bootstrapped = false; + private app: angular.auto.IInjectorService; load(el: HTMLElement): void { - if (this.bootstrapped) { - return; - } - import('./angularjs-app').then(app => { try { - app.bootstrap(el); - this.bootstrapped = true; + this.app = app.bootstrap(el); } catch (e) { console.error(e); } }); } + + destroy() { + if (this.app) { + this.app.get('$rootScope').$destroy(); + } + } } diff --git a/aio/content/guide/upgrade.md b/aio/content/guide/upgrade.md index 75f0d3b95e..f017ce1d0a 100644 --- a/aio/content/guide/upgrade.md +++ b/aio/content/guide/upgrade.md @@ -868,6 +868,8 @@ As of Angular version 8, lazy loading code can be accomplished simply by using t The service uses the `import()` method to load your bundled AngularJS application lazily. This decreases the initial bundle size of your application as you're not loading code your user doesn't need yet. You also need to provide a way to _bootstrap_ the application manually after it has been loaded. AngularJS provides a way to manually bootstrap an application using the [angular.bootstrap()](https://docs.angularjs.org/api/ng/function/angular.bootstrap) method with a provided HTML element. Your AngularJS app should also expose a `bootstrap` method that bootstraps the AngularJS app. +To ensure any necessary teardown is triggered in the AngularJS app, such as removal of global listeners, you also implement a method to call the `$rootScope.destroy()` method. +