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(