Skip to content

Commit

Permalink
fix(angular): ngOnDestroy runs inside angular zone (#24949)
Browse files Browse the repository at this point in the history
Resolves #22571
  • Loading branch information
sean-perkins committed Mar 22, 2022
1 parent 9e84ef7 commit a8fd2d9
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 15 deletions.
28 changes: 22 additions & 6 deletions angular/src/directives/navigation/stack-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -294,20 +294,36 @@ 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<void>((resolve) => {
requestAnimationFrame(() => {
cleanup(activeRoute, views, viewsSnapshot, location);
cleanup(activeRoute, views, viewsSnapshot, location, zone);
resolve();
});
});
}
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) => {
/**
Expand Down
6 changes: 6 additions & 0 deletions angular/test/test-app/e2e/src/nested-outlet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});

});

Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<ion-content>
<h1>Nested page 2</h1>
<p>
<ion-button routerLink="/nested-outlet/page">Go To FIRST</ion-button>
</p>
</ion-content>
<h1>Nested page 2</h1>
<p>
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go To FIRST</ion-button>
</p>
</ion-content>
Original file line number Diff line number Diff line change
@@ -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();
}
}

0 comments on commit a8fd2d9

Please sign in to comment.