From 4e6aa9c2db797bbafd7abc750b48fa206bad8be7 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Mon, 25 Sep 2017 12:39:51 +0300 Subject: [PATCH] fix(upgrade): ensure downgraded components are destroyed in the Angular zone --- .../src/common/downgrade_component_adapter.ts | 4 +- .../downgrade_component_adapter_spec.ts | 4 +- .../integration/downgrade_module_spec.ts | 38 ++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/upgrade/src/common/downgrade_component_adapter.ts b/packages/upgrade/src/common/downgrade_component_adapter.ts index 13bb1ed9a4..9f66465133 100644 --- a/packages/upgrade/src/common/downgrade_component_adapter.ts +++ b/packages/upgrade/src/common/downgrade_component_adapter.ts @@ -203,11 +203,13 @@ export class DowngradeComponentAdapter { } registerCleanup(needsNgZone: boolean) { + const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy()); + this.element.on !('$destroy', () => { this.componentScope.$destroy(); this.componentRef.injector.get(TestabilityRegistry) .unregisterApplication(this.componentRef.location.nativeElement); - this.componentRef.destroy(); + destroyComponentRef(); if (needsNgZone) { this.appRef.detachView(this.componentRef.hostView); } diff --git a/packages/upgrade/test/common/downgrade_component_adapter_spec.ts b/packages/upgrade/test/common/downgrade_component_adapter_spec.ts index d2656ecf1e..7f91397afe 100644 --- a/packages/upgrade/test/common/downgrade_component_adapter_spec.ts +++ b/packages/upgrade/test/common/downgrade_component_adapter_spec.ts @@ -122,7 +122,7 @@ export function main() { let $compile = undefined as any; let $parse = undefined as any; let componentFactory: ComponentFactory; // testbed - let wrapCallback = undefined as any; + let wrapCallback = (cb: any) => cb; content = `

new component

@@ -183,7 +183,7 @@ export function main() { expect(registry.getAllTestabilities().length).toEqual(0); adapter.createComponent([]); expect(registry.getAllTestabilities().length).toEqual(1); - adapter.registerCleanup(true); + adapter.registerCleanup(); element.remove !(); expect(registry.getAllTestabilities().length).toEqual(0); }); diff --git a/packages/upgrade/test/static/integration/downgrade_module_spec.ts b/packages/upgrade/test/static/integration/downgrade_module_spec.ts index 4544e5c7b7..d3617d7b63 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 {Component, Inject, Injector, Input, NgModule, NgZone, OnChanges, StaticProvider, destroyPlatform} from '@angular/core'; +import {Component, Inject, Injector, Input, NgModule, NgZone, OnChanges, OnDestroy, StaticProvider, destroyPlatform} from '@angular/core'; import {async, fakeAsync, tick} from '@angular/core/testing'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; @@ -170,6 +170,42 @@ export function main() { }); })); + it('should destroy components inside the Angular zone', async(() => { + let destroyedInTheZone = false; + + @Component({selector: 'ng2', template: ''}) + class Ng2Component implements OnDestroy { + ngOnDestroy() { destroyedInTheZone = NgZone.isInAngularZone(); } + } + + @NgModule({ + declarations: [Ng2Component], + entryComponents: [Ng2Component], + imports: [BrowserModule], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + const bootstrapFn = (extraProviders: StaticProvider[]) => + platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); + const lazyModuleName = downgradeModule(bootstrapFn); + const ng1Module = + angular.module('ng1', [lazyModuleName]) + .directive( + 'ng2', downgradeComponent({component: Ng2Component, propagateDigest})); + + const element = html(''); + const $injector = angular.bootstrap(element, [ng1Module.name]); + const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; + + // Wait for the module to be bootstrapped. + setTimeout(() => { + $rootScope.$apply('hideNg2 = true'); + expect(destroyedInTheZone).toBe(true); + }); + })); + it('should propagate input changes inside the Angular zone', async(() => { let ng2Component: Ng2Component;