diff --git a/angular/test/apps/ng16/e2e/src/modal-nav-params.spec.ts b/angular/test/apps/ng16/e2e/src/modal-nav-params.spec.ts new file mode 100644 index 0000000000..6a5556518f --- /dev/null +++ b/angular/test/apps/ng16/e2e/src/modal-nav-params.spec.ts @@ -0,0 +1,20 @@ +describe('Modal Nav Params', () => { + + beforeEach(() => { + cy.visit('/version-test/modal-nav-params'); + }); + + it('should assign the rootParams when presented in a modal multiple times', () => { + cy.contains('Open Modal').click(); + cy.get('ion-modal').should('exist').should('be.visible'); + cy.get('ion-modal').contains('OK'); + + cy.contains("Close").click(); + cy.get('ion-modal').should('not.be.visible'); + + cy.contains('Open Modal').click(); + cy.get('ion-modal').should('exist').should('be.visible'); + cy.get('ion-modal').contains('OK').should('exist'); + }); + +}); diff --git a/angular/test/apps/ng16/src/app/version-test/modal-nav-params/modal-nav-params.component.ts b/angular/test/apps/ng16/src/app/version-test/modal-nav-params/modal-nav-params.component.ts new file mode 100644 index 0000000000..5f820e1be4 --- /dev/null +++ b/angular/test/apps/ng16/src/app/version-test/modal-nav-params/modal-nav-params.component.ts @@ -0,0 +1,45 @@ +import { Component } from "@angular/core"; + +import { IonicModule } from "@ionic/angular"; + +import { NavRootComponent } from "./nav-root.component"; + +@Component({ + selector: 'app-modal-nav-params', + template: ` + + + Modal Nav Params + + + + Open Modal + + + + + + Close + + + + + + + + + + `, + standalone: true, + imports: [IonicModule, NavRootComponent] +}) +export class ModalNavParamsComponent { + + root = NavRootComponent; + rootParams = { + params: { + id: 123 + } + }; + +} diff --git a/angular/test/apps/ng16/src/app/version-test/modal-nav-params/nav-root.component.ts b/angular/test/apps/ng16/src/app/version-test/modal-nav-params/nav-root.component.ts new file mode 100644 index 0000000000..ae545093dc --- /dev/null +++ b/angular/test/apps/ng16/src/app/version-test/modal-nav-params/nav-root.component.ts @@ -0,0 +1,38 @@ +import { JsonPipe } from "@angular/common"; +import { Component } from "@angular/core"; + +import { IonicModule } from "@ionic/angular"; + +/** + * This is used to track if any occurences of + * the ion-nav root component being attached to + * the DOM result in the rootParams not being + * assigned to the component instance. + * + * https://github.com/ionic-team/ionic-framework/issues/27146 + */ +let rootParamsException = false; + +@Component({ + selector: 'app-modal-content', + template: ` + {{ hasException ? 'ERROR' : 'OK' }} + `, + standalone: true, + imports: [IonicModule, JsonPipe] +}) +export class NavRootComponent { + + params: any; + + ngOnInit() { + if (this.params === undefined) { + rootParamsException = true; + } + } + + get hasException() { + return rootParamsException; + } + +} diff --git a/angular/test/apps/ng16/src/app/version-test/version-test-routing.module.ts b/angular/test/apps/ng16/src/app/version-test/version-test-routing.module.ts index be335bd1af..7cd8857683 100644 --- a/angular/test/apps/ng16/src/app/version-test/version-test-routing.module.ts +++ b/angular/test/apps/ng16/src/app/version-test/version-test-routing.module.ts @@ -3,7 +3,12 @@ import { RouterModule } from "@angular/router"; @NgModule({ imports: [ - RouterModule.forChild([]) + RouterModule.forChild([ + { + path: 'modal-nav-params', + loadComponent: () => import('./modal-nav-params/modal-nav-params.component').then(m => m.ModalNavParamsComponent) + } + ]) ], exports: [RouterModule] }) diff --git a/core/src/components/nav/nav.tsx b/core/src/components/nav/nav.tsx index 733428e9f6..d6290dee71 100644 --- a/core/src/components/nav/nav.tsx +++ b/core/src/components/nav/nav.tsx @@ -2,6 +2,7 @@ import type { EventEmitter } from '@stencil/core'; import { Build, Component, Element, Event, Method, Prop, Watch, h } from '@stencil/core'; import { getTimeGivenProgression } from '@utils/animation/cubic-bezier'; import { assert } from '@utils/helpers'; +import { printIonWarning } from '@utils/logging'; import type { TransitionOptions } from '@utils/transition'; import { lifecycle, setPageHidden, transition } from '@utils/transition'; @@ -36,6 +37,7 @@ export class Nav implements NavOutlet { private destroyed = false; private views: ViewController[] = []; private gesture?: Gesture; + private didLoad = false; @Element() el!: HTMLElement; @@ -77,12 +79,25 @@ export class Nav implements NavOutlet { @Watch('root') rootChanged() { const isDev = Build.isDev; - if (this.root !== undefined) { - if (!this.useRouter) { + + if (this.root === undefined) { + return; + } + + if (this.didLoad === false) { + /** + * If the component has not loaded yet, we can skip setting up the root component. + * It will be called when `componentDidLoad` fires. + */ + return; + } + + if (!this.useRouter) { + if (this.root !== undefined) { this.setRoot(this.root, this.rootParams); - } else if (isDev) { - console.warn(' does not support a root attribute when using ion-router.'); } + } else if (isDev) { + printIonWarning(' does not support a root attribute when using ion-router.', this.el); } } @@ -112,6 +127,9 @@ export class Nav implements NavOutlet { } async componentDidLoad() { + // We want to set this flag before any watch callbacks are manually called + this.didLoad = true; + this.rootChanged(); this.gesture = (await import('../../utils/gesture/swipe-back')).createSwipeBackGesture(