diff --git a/packages/angular/src/directives/navigation/ion-router-outlet.ts b/packages/angular/src/directives/navigation/ion-router-outlet.ts index 2187f28b37..c84f5ce23b 100644 --- a/packages/angular/src/directives/navigation/ion-router-outlet.ts +++ b/packages/angular/src/directives/navigation/ion-router-outlet.ts @@ -1,8 +1,29 @@ -import { Directive } from '@angular/core'; +import { Location } from '@angular/common'; +import { Directive, Attribute, Optional, SkipSelf, ElementRef, NgZone } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; import { IonRouterOutlet as IonRouterOutletBase } from '@ionic/angular/common'; @Directive({ selector: 'ion-router-outlet', }) // eslint-disable-next-line @angular-eslint/directive-class-suffix -export class IonRouterOutlet extends IonRouterOutletBase {} +export class IonRouterOutlet extends IonRouterOutletBase { + /** + * We need to pass in the correct instance of IonRouterOutlet + * otherwise parentOutlet will be null in a nested outlet context. + * This results in APIs such as NavController.pop not working + * in nested outlets because the parent outlet cannot be found. + */ + constructor( + @Attribute('name') name: string, + @Optional() @Attribute('tabs') tabs: string, + commonLocation: Location, + elementRef: ElementRef, + router: Router, + zone: NgZone, + activatedRoute: ActivatedRoute, + @SkipSelf() @Optional() readonly parentOutlet?: IonRouterOutlet + ) { + super(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet); + } +} diff --git a/packages/angular/standalone/src/navigation/router-outlet.ts b/packages/angular/standalone/src/navigation/router-outlet.ts index 2b750736e0..d5e6becab2 100644 --- a/packages/angular/standalone/src/navigation/router-outlet.ts +++ b/packages/angular/standalone/src/navigation/router-outlet.ts @@ -1,4 +1,6 @@ -import { Directive } from '@angular/core'; +import { Location } from '@angular/common'; +import { Directive, Attribute, Optional, SkipSelf, ElementRef, NgZone } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; import { IonRouterOutlet as IonRouterOutletBase, ProxyCmp } from '@ionic/angular/common'; import { defineCustomElement } from '@ionic/core/components/ion-router-outlet.js'; @@ -10,4 +12,23 @@ import { defineCustomElement } from '@ionic/core/components/ion-router-outlet.js standalone: true, }) // eslint-disable-next-line @angular-eslint/directive-class-suffix -export class IonRouterOutlet extends IonRouterOutletBase {} +export class IonRouterOutlet extends IonRouterOutletBase { + /** + * We need to pass in the correct instance of IonRouterOutlet + * otherwise parentOutlet will be null in a nested outlet context. + * This results in APIs such as NavController.pop not working + * in nested outlets because the parent outlet cannot be found. + */ + constructor( + @Attribute('name') name: string, + @Optional() @Attribute('tabs') tabs: string, + commonLocation: Location, + elementRef: ElementRef, + router: Router, + zone: NgZone, + activatedRoute: ActivatedRoute, + @SkipSelf() @Optional() readonly parentOutlet?: IonRouterOutlet + ) { + super(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet); + } +} diff --git a/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts b/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts index 8a95ab408c..786b2b44ef 100644 --- a/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts @@ -27,5 +27,9 @@ describe('Nested Outlet', () => { cy.get('#goto-nested-page2').click(); }); + // Fixes https://github.com/ionic-team/ionic-framework/issues/28417 + it('parentOutlet should be defined', () => { + cy.get('#parent-outlet span').should('have.text', 'true'); + }); }); diff --git a/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts b/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts index a0558f299f..bb0901a112 100644 --- a/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts +++ b/packages/angular/test/base/e2e/src/standalone/tabs.spec.ts @@ -13,4 +13,9 @@ describe('Tabs', () => { cy.get('app-tab-two').should('be.visible'); cy.contains('Tab 2'); }); + + // Fixes https://github.com/ionic-team/ionic-framework/issues/28417 + it('parentOutlet should be defined', () => { + cy.get('#parent-outlet span').should('have.text', 'true'); + }); }); diff --git a/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.html b/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.html index 10d40ff324..56ac85776d 100644 --- a/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.html +++ b/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.html @@ -4,4 +4,7 @@ Go To Tabs Go To SECOND

+

+ Has Parent Outlet: {{ hasParentOutlet }} +

diff --git a/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.ts b/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.ts index 755aae861b..1b62c87c71 100644 --- a/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.ts +++ b/packages/angular/test/base/src/app/lazy/nested-outlet-page/nested-outlet-page.component.ts @@ -1,10 +1,16 @@ import { Component, NgZone, OnDestroy, OnInit } from '@angular/core'; +import { IonRouterOutlet } from '@ionic/angular'; @Component({ selector: 'app-nested-outlet-page', templateUrl: './nested-outlet-page.component.html', }) export class NestedOutletPageComponent implements OnDestroy, OnInit { + hasParentOutlet = false; + + constructor(private routerOutlet: IonRouterOutlet) { + this.hasParentOutlet = routerOutlet.parentOutlet != null; + } ngOnInit() { NgZone.assertInAngularZone(); diff --git a/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts b/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts index 8b6e8976ec..3bd2bd270b 100644 --- a/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts +++ b/packages/angular/test/base/src/app/standalone/tabs/tab1.component.ts @@ -1,12 +1,21 @@ import { Component } from '@angular/core'; +import { IonRouterOutlet } from '@ionic/angular/standalone'; @Component({ selector: 'app-tab-one', template: ` Tab 1 + +

+ Has Parent Outlet: {{ hasParentOutlet }} +

`, standalone: true, }) export class TabOneComponent { + hasParentOutlet = false; + constructor(private routerOutlet: IonRouterOutlet) { + this.hasParentOutlet = routerOutlet.parentOutlet != null; + } }