diff --git a/packages/angular/common/src/directives/navigation/router-outlet.ts b/packages/angular/common/src/directives/navigation/router-outlet.ts
index d0d6bb600c..556e49d606 100644
--- a/packages/angular/common/src/directives/navigation/router-outlet.ts
+++ b/packages/angular/common/src/directives/navigation/router-outlet.ts
@@ -116,6 +116,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
router: Router,
zone: NgZone,
activatedRoute: ActivatedRoute,
+ protected outletContent: ViewContainerRef,
@SkipSelf() @Optional() readonly parentOutlet?: IonRouterOutlet
) {
this.nativeEl = elementRef.nativeElement;
@@ -276,8 +277,16 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const component = snapshot.routeConfig!.component ?? snapshot.component;
- cmpRef = this.activated = this.location.createComponent(component, {
- index: this.location.length,
+ /**
+ * View components need to be added as a child of ion-router-outlet
+ * for page transitions and swipe to go back.
+ * However, createComponent mounts components as siblings of the
+ * ViewContainerRef. As a result, outletContent must reference
+ * an ng-container inside of ion-router-outlet and not
+ * ion-router-outlet itself.
+ */
+ cmpRef = this.activated = this.outletContent.createComponent(component, {
+ index: this.outletContent.length,
injector,
environmentInjector: environmentInjector ?? this.environmentInjector,
});
diff --git a/packages/angular/common/src/directives/navigation/stack-controller.ts b/packages/angular/common/src/directives/navigation/stack-controller.ts
index 8241afa864..0593219d3f 100644
--- a/packages/angular/common/src/directives/navigation/stack-controller.ts
+++ b/packages/angular/common/src/directives/navigation/stack-controller.ts
@@ -274,9 +274,6 @@ export class StackController {
if (enteringEl && enteringEl !== leavingEl) {
enteringEl.classList.add('ion-page');
enteringEl.classList.add('ion-page-invisible');
- if (enteringEl.parentElement !== containerEl) {
- containerEl.appendChild(enteringEl);
- }
if ((containerEl as any).commit) {
return containerEl.commit(enteringEl, leavingEl, {
diff --git a/packages/angular/src/directives/navigation/ion-router-outlet.ts b/packages/angular/src/directives/navigation/ion-router-outlet.ts
index c84f5ce23b..52cc2ebe62 100644
--- a/packages/angular/src/directives/navigation/ion-router-outlet.ts
+++ b/packages/angular/src/directives/navigation/ion-router-outlet.ts
@@ -1,13 +1,32 @@
import { Location } from '@angular/common';
-import { Directive, Attribute, Optional, SkipSelf, ElementRef, NgZone } from '@angular/core';
+import {
+ ViewChild,
+ ViewContainerRef,
+ Component,
+ Attribute,
+ Optional,
+ SkipSelf,
+ ElementRef,
+ NgZone,
+} from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { IonRouterOutlet as IonRouterOutletBase } from '@ionic/angular/common';
-@Directive({
+@Component({
selector: 'ion-router-outlet',
+ template: '',
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonRouterOutlet extends IonRouterOutletBase {
+ /**
+ * `static: true` must be set so the query results are resolved
+ * before change detection runs. Otherwise, the view container
+ * ref will be ion-router-outlet instead of ng-container, and
+ * the first view will be added as a sibling of ion-router-outlet
+ * instead of a child.
+ */
+ @ViewChild('outletContent', { read: ViewContainerRef, static: true }) outletContent: ViewContainerRef;
+
/**
* We need to pass in the correct instance of IonRouterOutlet
* otherwise parentOutlet will be null in a nested outlet context.
@@ -22,8 +41,9 @@ export class IonRouterOutlet extends IonRouterOutletBase {
router: Router,
zone: NgZone,
activatedRoute: ActivatedRoute,
+ outletContent: ViewContainerRef,
@SkipSelf() @Optional() readonly parentOutlet?: IonRouterOutlet
) {
- super(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet);
+ super(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, outletContent, parentOutlet);
}
}
diff --git a/packages/angular/standalone/src/navigation/router-outlet.ts b/packages/angular/standalone/src/navigation/router-outlet.ts
index d5e6becab2..5919462b82 100644
--- a/packages/angular/standalone/src/navigation/router-outlet.ts
+++ b/packages/angular/standalone/src/navigation/router-outlet.ts
@@ -1,5 +1,14 @@
import { Location } from '@angular/common';
-import { Directive, Attribute, Optional, SkipSelf, ElementRef, NgZone } from '@angular/core';
+import {
+ ViewChild,
+ ViewContainerRef,
+ Component,
+ 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';
@@ -7,12 +16,22 @@ import { defineCustomElement } from '@ionic/core/components/ion-router-outlet.js
@ProxyCmp({
defineCustomElementFn: defineCustomElement,
})
-@Directive({
+@Component({
selector: 'ion-router-outlet',
standalone: true,
+ template: '',
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonRouterOutlet extends IonRouterOutletBase {
+ /**
+ * `static: true` must be set so the query results are resolved
+ * before change detection runs. Otherwise, the view container
+ * ref will be ion-router-outlet instead of ng-container, and
+ * the first view will be added as a sibling of ion-router-outlet
+ * instead of a child.
+ */
+ @ViewChild('outletContent', { read: ViewContainerRef, static: true }) outletContent: ViewContainerRef;
+
/**
* We need to pass in the correct instance of IonRouterOutlet
* otherwise parentOutlet will be null in a nested outlet context.
@@ -27,8 +46,9 @@ export class IonRouterOutlet extends IonRouterOutletBase {
router: Router,
zone: NgZone,
activatedRoute: ActivatedRoute,
+ outletContent: ViewContainerRef,
@SkipSelf() @Optional() readonly parentOutlet?: IonRouterOutlet
) {
- super(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet);
+ super(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, outletContent, parentOutlet);
}
}