diff --git a/packages/upgrade/src/common/downgrade_component_adapter.ts b/packages/upgrade/src/common/downgrade_component_adapter.ts index 4e44bfcf9f..6ba0238675 100644 --- a/packages/upgrade/src/common/downgrade_component_adapter.ts +++ b/packages/upgrade/src/common/downgrade_component_adapter.ts @@ -209,12 +209,16 @@ export class DowngradeComponentAdapter { registerCleanup() { const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy()); + let destroyed = false; - this.element.on !('$destroy', () => { - this.componentScope.$destroy(); - this.componentRef.injector.get(TestabilityRegistry) - .unregisterApplication(this.componentRef.location.nativeElement); - destroyComponentRef(); + this.element.on !('$destroy', () => this.componentScope.$destroy()); + this.componentScope.$on('$destroy', () => { + if (!destroyed) { + destroyed = true; + this.componentRef.injector.get(TestabilityRegistry) + .unregisterApplication(this.componentRef.location.nativeElement); + destroyComponentRef(); + } }); } diff --git a/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts b/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts index 06d9ed44dc..81f21e6a1c 100644 --- a/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts +++ b/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts @@ -263,6 +263,8 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck { if (this.controllerInstance && isFunction(this.controllerInstance.$onDestroy)) { this.controllerInstance.$onDestroy(); } + + this.componentScope.$destroy(); } setComponentProperty(name: string, value: any) { diff --git a/packages/upgrade/test/common/downgrade_component_adapter_spec.ts b/packages/upgrade/test/common/downgrade_component_adapter_spec.ts index d7110815ba..c428c24bd8 100644 --- a/packages/upgrade/test/common/downgrade_component_adapter_spec.ts +++ b/packages/upgrade/test/common/downgrade_component_adapter_spec.ts @@ -85,15 +85,21 @@ withEachNg1Version(() => { let element: angular.IAugmentedJQuery; class mockScope implements angular.IScope { + private destroyListeners: (() => void)[] = []; + $new() { return this; } $watch(exp: angular.Ng1Expression, fn?: (a1?: any, a2?: any) => void) { return () => {}; } $on(event: string, fn?: (event?: any, ...args: any[]) => void) { + if (event === '$destroy' && fn) { + this.destroyListeners.push(fn); + } return () => {}; } $destroy() { - return () => {}; + let listener: (() => void)|undefined; + while ((listener = this.destroyListeners.shift())) listener(); } $apply(exp?: angular.Ng1Expression) { return () => {}; diff --git a/packages/upgrade/test/dynamic/test_helpers.ts b/packages/upgrade/test/dynamic/test_helpers.ts index 284ef8bb6f..7723ecc361 100644 --- a/packages/upgrade/test/dynamic/test_helpers.ts +++ b/packages/upgrade/test/dynamic/test_helpers.ts @@ -12,6 +12,11 @@ import {$ROOT_SCOPE} from '@angular/upgrade/src/common/constants'; export * from '../common/test_helpers'; +export function $apply(adapter: UpgradeAdapterRef, exp: angular.Ng1Expression) { + const $rootScope = adapter.ng1Injector.get($ROOT_SCOPE) as angular.IRootScopeService; + $rootScope.$apply(exp); +} + export function $digest(adapter: UpgradeAdapterRef) { const $rootScope = adapter.ng1Injector.get($ROOT_SCOPE) as angular.IRootScopeService; $rootScope.$digest(); diff --git a/packages/upgrade/test/dynamic/upgrade_spec.ts b/packages/upgrade/test/dynamic/upgrade_spec.ts index ae3044b89a..48d3412834 100644 --- a/packages/upgrade/test/dynamic/upgrade_spec.ts +++ b/packages/upgrade/test/dynamic/upgrade_spec.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, NgZone, OnChanges, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core'; +import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, NgZone, OnChanges, OnDestroy, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core'; import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import * as angular from '@angular/upgrade/src/common/angular1'; import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter'; -import {$digest, html, multiTrim, withEachNg1Version} from './test_helpers'; +import {$apply, $digest, html, multiTrim, withEachNg1Version} from './test_helpers'; declare global { export var inject: Function; @@ -582,6 +582,49 @@ withEachNg1Version(() => { }); })); + it('should properly run cleanup with multiple levels of nesting', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let destroyed = false; + + @Component( + {selector: 'ng2-outer', template: '