diff --git a/packages/angular/common/src/directives/navigation/stack-controller.ts b/packages/angular/common/src/directives/navigation/stack-controller.ts index 8241afa864f..d45e84fa168 100644 --- a/packages/angular/common/src/directives/navigation/stack-controller.ts +++ b/packages/angular/common/src/directives/navigation/stack-controller.ts @@ -65,40 +65,46 @@ export class StackController { const consumeResult = this.navCtrl.consumeTransition(); let { direction, animation, animationBuilder } = consumeResult; const leavingView = this.activeView; - const tabSwitch = isTabSwitch(enteringView, leavingView); - if (tabSwitch) { - direction = 'back'; - animation = undefined; - } + const leavingViewIndex = leavingView ? this.views.indexOf(leavingView) : -1; const viewsSnapshot = this.views.slice(); - let currentNavigation; - - const router = this.router as any; - - // Angular >= 7.2.0 - if (router.getCurrentNavigation) { - currentNavigation = router.getCurrentNavigation(); - - // Angular < 7.2.0 - } else if (router.navigations?.value) { - currentNavigation = router.navigations.value; - } + const currentNavigation = this.router.getCurrentNavigation(); /** - * If the navigation action - * sets `replaceUrl: true` - * then we need to make sure - * we remove the last item - * from our views stack + * If the navigation action sets `replaceUrl: true` then we need to make sure + * we remove the last item from our views stack */ - if (currentNavigation?.extras?.replaceUrl) { + if (currentNavigation?.extras?.replaceUrl && currentNavigation?.trigger !== 'popstate') { if (this.views.length > 0) { this.views.splice(-1, 1); } } + /** + * The user triggered a back navigation on a page that was navigated to with root. In this case, the new page + * becomes the root and the leavingView is removed. + * + * This can happen e.g. when navigating to a page with navigateRoot and then using the browser back button + */ + const isPopStateTransitionFromRootPage = + direction === 'back' && leavingView?.root && currentNavigation?.trigger === 'popstate'; + + if (isPopStateTransitionFromRootPage) { + direction = 'root'; + animation = undefined; + + if (leavingViewIndex >= 0) { + this.views.splice(leavingViewIndex, 1); + } + } + + const tabSwitch = isTabSwitch(enteringView, leavingView); + if (tabSwitch) { + direction = 'back'; + animation = undefined; + } + const reused = this.views.includes(enteringView); const views = this.insertView(enteringView, direction); diff --git a/packages/angular/common/src/directives/navigation/stack-utils.ts b/packages/angular/common/src/directives/navigation/stack-utils.ts index 41203407599..44d05e1f2b9 100644 --- a/packages/angular/common/src/directives/navigation/stack-utils.ts +++ b/packages/angular/common/src/directives/navigation/stack-utils.ts @@ -14,6 +14,7 @@ export const insertView = (views: RouteView[], view: RouteView, direction: Route const setRoot = (views: RouteView[], view: RouteView) => { views = views.filter((v) => v.stackId !== view.stackId); + view.root = true; views.push(view); return views; }; @@ -110,4 +111,5 @@ export interface RouteView { savedExtras?: NavigationExtras; unlistenEvents: () => void; animationBuilder?: AnimationBuilder; + root?: boolean; } diff --git a/packages/angular/common/src/providers/nav-controller.ts b/packages/angular/common/src/providers/nav-controller.ts index aff31b090b3..1ae3a0a603f 100644 --- a/packages/angular/common/src/providers/nav-controller.ts +++ b/packages/angular/common/src/providers/nav-controller.ts @@ -37,9 +37,9 @@ export class NavController { if (router) { router.events.subscribe((ev) => { if (ev instanceof NavigationStart) { + // restoredState is set if the browser back/forward button is used const id = ev.restoredState ? ev.restoredState.navigationId : ev.id; - this.guessDirection = id < this.lastNavId ? 'back' : 'forward'; - this.guessAnimation = !ev.restoredState ? this.guessDirection : undefined; + this.guessDirection = this.guessAnimation = id < this.lastNavId ? 'back' : 'forward'; this.lastNavId = this.guessDirection === 'forward' ? ev.id : id; } }); diff --git a/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts b/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts index 5ca72825f51..51811cce583 100644 --- a/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts @@ -138,6 +138,7 @@ function testForward() { cy.get('app-router-link-page #canGoBack').should('have.text', 'true'); cy.go('back'); + cy.wait(500); cy.testStack('ion-router-outlet', ['app-router-link']); cy.testLifeCycle('app-router-link', { ionViewWillEnter: 2, @@ -181,7 +182,7 @@ function testBack() { cy.get('app-router-link-page #canGoBack').should('have.text', 'false'); cy.go('back'); - cy.wait(100); + cy.wait(500); cy.testStack('ion-router-outlet', ['app-router-link']); cy.testLifeCycle('app-router-link', { ionViewWillEnter: 1,