diff --git a/angular/src/directives/navigation/stack-controller.ts b/angular/src/directives/navigation/stack-controller.ts index a2f6134c1e..a1e326ca4a 100644 --- a/angular/src/directives/navigation/stack-controller.ts +++ b/angular/src/directives/navigation/stack-controller.ts @@ -139,7 +139,7 @@ export class StackController { enteringView.ref.changeDetectorRef.reattach(); return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder) - .then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location)) + .then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location, this.zone)) .then(() => ({ enteringView, direction, @@ -201,7 +201,7 @@ export class StackController { this.skipTransition = true; this.pop(1); } else if (this.activeView) { - cleanup(this.activeView, this.views, this.views, this.location); + cleanup(this.activeView, this.views, this.views, this.location, this.zone); } } @@ -294,11 +294,17 @@ export class StackController { } } -const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => { +const cleanupAsync = ( + activeRoute: RouteView, + views: RouteView[], + viewsSnapshot: RouteView[], + location: Location, + zone: NgZone +) => { if (typeof (requestAnimationFrame as any) === 'function') { return new Promise((resolve) => { requestAnimationFrame(() => { - cleanup(activeRoute, views, viewsSnapshot, location); + cleanup(activeRoute, views, viewsSnapshot, location, zone); resolve(); }); }); @@ -306,8 +312,18 @@ const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: return Promise.resolve(); }; -const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => { - viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView); +const cleanup = ( + activeRoute: RouteView, + views: RouteView[], + viewsSnapshot: RouteView[], + location: Location, + zone: NgZone +) => { + /** + * Re-enter the Angular zone when destroying page components. This will allow + * lifecycle events (`ngOnDestroy`) to be run inside the Angular zone. + */ + zone.run(() => viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView)); views.forEach((view) => { /** diff --git a/angular/test/test-app/e2e/src/nested-outlet.spec.ts b/angular/test/test-app/e2e/src/nested-outlet.spec.ts index e135d986d0..ef5405905d 100644 --- a/angular/test/test-app/e2e/src/nested-outlet.spec.ts +++ b/angular/test/test-app/e2e/src/nested-outlet.spec.ts @@ -20,6 +20,12 @@ describe('Nested Outlet', () => { cy.ionPageVisible('app-nested-outlet-page2'); cy.get('ion-router-outlet ion-router-outlet app-nested-outlet-page2 h1').should('have.text', 'Nested page 2'); + + cy.get('#goto-nested-page1').click(); + cy.ionPageVisible('app-nested-outlet-page'); + + cy.get('#goto-nested-page2').click(); }); + }); diff --git a/angular/test/test-app/src/app/nested-outlet-page/nested-outlet-page.component.ts b/angular/test/test-app/src/app/nested-outlet-page/nested-outlet-page.component.ts index 263aacbee3..755aae861b 100644 --- a/angular/test/test-app/src/app/nested-outlet-page/nested-outlet-page.component.ts +++ b/angular/test/test-app/src/app/nested-outlet-page/nested-outlet-page.component.ts @@ -1,8 +1,16 @@ -import { Component } from '@angular/core'; +import { Component, NgZone, OnDestroy, OnInit } from '@angular/core'; @Component({ selector: 'app-nested-outlet-page', templateUrl: './nested-outlet-page.component.html', }) -export class NestedOutletPageComponent { +export class NestedOutletPageComponent implements OnDestroy, OnInit { + + ngOnInit() { + NgZone.assertInAngularZone(); + } + + ngOnDestroy() { + NgZone.assertInAngularZone(); + } } diff --git a/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.html b/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.html index 67303be072..5b857a378d 100644 --- a/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.html +++ b/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.html @@ -1,6 +1,6 @@ -

Nested page 2

-

- Go To FIRST -

-
+

Nested page 2

+

+ Go To FIRST +

+ diff --git a/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.ts b/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.ts index 9794d11d74..0dd985c3a6 100644 --- a/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.ts +++ b/angular/test/test-app/src/app/nested-outlet-page2/nested-outlet-page2.component.ts @@ -1,8 +1,16 @@ -import { Component } from '@angular/core'; +import { Component, NgZone, OnDestroy, OnInit } from '@angular/core'; @Component({ selector: 'app-nested-outlet-page2', templateUrl: './nested-outlet-page2.component.html', }) -export class NestedOutletPage2Component { +export class NestedOutletPage2Component implements OnDestroy, OnInit { + + ngOnInit() { + NgZone.assertInAngularZone(); + } + + ngOnDestroy() { + NgZone.assertInAngularZone(); + } }