mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 01:03:03 +08:00
fix(angular): NavController works with nested outlets (#28421)
Issue number: resolves #28417
---------
<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->
<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
The common `IonRouterOutlet` was trying to inject another common
`IonRouterOutlet` into `parentOutlet`:
dc94ae01fe/packages/angular/common/src/directives/navigation/router-outlet.ts (L119)
None existed, so this field was `null`.
This is a problem if developers are using the module `IonRouterOutlet`
since parent router outlets will not be currently injected because
Angular is trying to use the common `IonRouterOutlet` not the module
`IonRouterOutlet`:
https://github.com/ionic-team/ionic-framework/blob/main/packages/angular/src/directives/navigation/ion-router-outlet.ts.
The same goes for the standalone `IonRouterOutlet`.
This resulted in things such as `NavController.pop` not working in
nested outlets because the parentOutlet was not defined.
## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
- `IonRouterOutlet` now injects the correct router outlet instance for
`parentOutlet`
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->
## Other information
<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
Dev build: `7.5.3-dev.11698328998.1a79f815`
This commit is contained in:
@ -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';
|
import { IonRouterOutlet as IonRouterOutletBase } from '@ionic/angular/common';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'ion-router-outlet',
|
selector: 'ion-router-outlet',
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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 { IonRouterOutlet as IonRouterOutletBase, ProxyCmp } from '@ionic/angular/common';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-router-outlet.js';
|
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,
|
standalone: true,
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -27,5 +27,9 @@ describe('Nested Outlet', () => {
|
|||||||
cy.get('#goto-nested-page2').click();
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,4 +13,9 @@ describe('Tabs', () => {
|
|||||||
cy.get('app-tab-two').should('be.visible');
|
cy.get('app-tab-two').should('be.visible');
|
||||||
cy.contains('Tab 2');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,4 +4,7 @@
|
|||||||
<ion-button routerLink="/lazy/tabs" id="goto-tabs">Go To Tabs</ion-button>
|
<ion-button routerLink="/lazy/tabs" id="goto-tabs">Go To Tabs</ion-button>
|
||||||
<ion-button routerLink="/lazy/nested-outlet/page2" id="goto-nested-page2">Go To SECOND</ion-button>
|
<ion-button routerLink="/lazy/nested-outlet/page2" id="goto-nested-page2">Go To SECOND</ion-button>
|
||||||
</p>
|
</p>
|
||||||
|
<p id="parent-outlet">
|
||||||
|
Has Parent Outlet: <span>{{ hasParentOutlet }}</span>
|
||||||
|
</p>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { IonRouterOutlet } from '@ionic/angular';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nested-outlet-page',
|
selector: 'app-nested-outlet-page',
|
||||||
templateUrl: './nested-outlet-page.component.html',
|
templateUrl: './nested-outlet-page.component.html',
|
||||||
})
|
})
|
||||||
export class NestedOutletPageComponent implements OnDestroy, OnInit {
|
export class NestedOutletPageComponent implements OnDestroy, OnInit {
|
||||||
|
hasParentOutlet = false;
|
||||||
|
|
||||||
|
constructor(private routerOutlet: IonRouterOutlet) {
|
||||||
|
this.hasParentOutlet = routerOutlet.parentOutlet != null;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
NgZone.assertInAngularZone();
|
NgZone.assertInAngularZone();
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { IonRouterOutlet } from '@ionic/angular/standalone';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tab-one',
|
selector: 'app-tab-one',
|
||||||
template: `
|
template: `
|
||||||
Tab 1
|
Tab 1
|
||||||
|
|
||||||
|
<p id="parent-outlet">
|
||||||
|
Has Parent Outlet: <span>{{ hasParentOutlet }}</span>
|
||||||
|
</p>
|
||||||
`,
|
`,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class TabOneComponent {
|
export class TabOneComponent {
|
||||||
|
hasParentOutlet = false;
|
||||||
|
|
||||||
|
constructor(private routerOutlet: IonRouterOutlet) {
|
||||||
|
this.hasParentOutlet = routerOutlet.parentOutlet != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user