mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 01:52:19 +08:00
fix(angular): nav controller can pop views after leaving tabs outlet (#25690)
Resolves #18593
This commit is contained in:
@ -308,8 +308,19 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.activatedView = enteringView;
|
this.activatedView = enteringView;
|
||||||
this.stackCtrl.setActive(enteringView).then((data) => {
|
|
||||||
|
/**
|
||||||
|
* The top outlet is set prior to the entering view's transition completing,
|
||||||
|
* so that when we have nested outlets (e.g. ion-tabs inside an ion-router-outlet),
|
||||||
|
* the tabs outlet will be assigned as the top outlet when a view inside tabs is
|
||||||
|
* activated.
|
||||||
|
*
|
||||||
|
* In this scenario, activeWith is called for both the tabs and the root router outlet.
|
||||||
|
* To avoid a race condition, we assign the top outlet synchronously.
|
||||||
|
*/
|
||||||
this.navCtrl.setTopOutlet(this);
|
this.navCtrl.setTopOutlet(this);
|
||||||
|
|
||||||
|
this.stackCtrl.setActive(enteringView).then((data) => {
|
||||||
this.activateEvents.emit(cmpRef.instance);
|
this.activateEvents.emit(cmpRef.instance);
|
||||||
this.stackEvents.emit(data);
|
this.stackEvents.emit(data);
|
||||||
});
|
});
|
||||||
|
@ -243,6 +243,74 @@ describe('Tabs', () => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('entry url - /tabs/account', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('/tabs/account');
|
||||||
|
});
|
||||||
|
it('should pop to previous view when leaving tabs outlet', () => {
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 1');
|
||||||
|
|
||||||
|
cy.get('#goto-tab1-page2').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 2 (1)');
|
||||||
|
|
||||||
|
cy.get('#goto-global').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Global Page');
|
||||||
|
|
||||||
|
cy.get('#goto-prev-pop').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 2 (1)');
|
||||||
|
|
||||||
|
cy.get('#goto-prev').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 1');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that when entering the tabs outlet directly,
|
||||||
|
* the navController.pop() method does not pop the previous view,
|
||||||
|
* when you are at the root of the tabs outlet.
|
||||||
|
*/
|
||||||
|
cy.get('#goto-previous-page').click();
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('entry url - /', () => {
|
||||||
|
it('should pop to the root outlet from the tabs outlet', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Test App');
|
||||||
|
|
||||||
|
cy.get('ion-item').contains('Tabs test').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 1');
|
||||||
|
|
||||||
|
cy.get('#goto-tab1-page2').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 2 (1)');
|
||||||
|
|
||||||
|
cy.get('#goto-global').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Global Page');
|
||||||
|
|
||||||
|
cy.get('#goto-prev-pop').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 2 (1)');
|
||||||
|
|
||||||
|
cy.get('#goto-prev').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Tab 1 - Page 1');
|
||||||
|
|
||||||
|
cy.get('#goto-previous-page').click();
|
||||||
|
|
||||||
|
cy.get('ion-title').should('contain.text', 'Test App');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('entry url - /tabs/account/nested/1', () => {
|
describe('entry url - /tabs/account/nested/1', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit('/tabs/account/nested/1');
|
cy.visit('/tabs/account/nested/1');
|
||||||
|
@ -55,6 +55,10 @@ const routes: Routes = [
|
|||||||
path: 'tabs',
|
path: 'tabs',
|
||||||
loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
|
loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tabs-global',
|
||||||
|
loadChildren: () => import('./tabs-global/tabs-global.module').then(m => m.TabsGlobalModule)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'nested-outlet',
|
path: 'nested-outlet',
|
||||||
component: NestedOutletComponent,
|
component: NestedOutletComponent,
|
||||||
@ -68,7 +72,7 @@ const routes: Routes = [
|
|||||||
component: NestedOutletPage2Component
|
component: NestedOutletPage2Component
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
import { RouterModule } from "@angular/router";
|
||||||
|
import { TabsGlobalComponent } from "./tabs-global.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: TabsGlobalComponent
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class TabsGlobalRoutingModule { }
|
@ -0,0 +1,17 @@
|
|||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
|
||||||
|
<ion-title>
|
||||||
|
Global Page
|
||||||
|
</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<ion-content>
|
||||||
|
<ion-button id="goto-prev-pop" (click)="navCtrl.pop()">Go To Previous</ion-button>
|
||||||
|
</ion-content>
|
||||||
|
</ion-content>
|
@ -0,0 +1,17 @@
|
|||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { NavController } from "@ionic/angular";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component is used in conjunction with a tabs router-outlet,
|
||||||
|
* to validate the behavior of different routing APIs (e.g. NavController)
|
||||||
|
* when leaving and re-entering a router-outlet.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tabs-global',
|
||||||
|
templateUrl: 'tabs-global.component.html'
|
||||||
|
})
|
||||||
|
export class TabsGlobalComponent {
|
||||||
|
|
||||||
|
constructor(public navCtrl: NavController) { }
|
||||||
|
|
||||||
|
}
|
13
angular/test/base/src/app/tabs-global/tabs-global.module.ts
Normal file
13
angular/test/base/src/app/tabs-global/tabs-global.module.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
import { IonicModule } from "@ionic/angular";
|
||||||
|
import { TabsGlobalRoutingModule } from "./tabs-global-routing.module";
|
||||||
|
import { TabsGlobalComponent } from "./tabs-global.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
IonicModule,
|
||||||
|
TabsGlobalRoutingModule
|
||||||
|
],
|
||||||
|
declarations: [TabsGlobalComponent]
|
||||||
|
})
|
||||||
|
export class TabsGlobalModule { }
|
@ -12,6 +12,9 @@
|
|||||||
<p>
|
<p>
|
||||||
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
|
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
|
||||||
<ion-button routerLink="/tabs/contact" id="goto-tab2-page1">Go to Tab 2 - Page 1</ion-button>
|
<ion-button routerLink="/tabs/contact" id="goto-tab2-page1">Go to Tab 2 - Page 1</ion-button>
|
||||||
|
<ion-button routerLink="/tabs-global" id="goto-global">Go to Global Page</ion-button>
|
||||||
|
<ion-button routerLink="/tabs-global" id="goto-prev" (click)="navCtrl.pop()">Go to Previous Page (NavController).
|
||||||
|
</ion-button>
|
||||||
<ion-button routerLink="/tabs/account/nested/{{next()}}" id="goto-next">Go to Next</ion-button>
|
<ion-button routerLink="/tabs/account/nested/{{next()}}" id="goto-next">Go to Next</ion-button>
|
||||||
</p>
|
</p>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { NavController } from '@ionic/angular';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tabs-tab1-nested',
|
selector: 'app-tabs-tab1-nested',
|
||||||
@ -9,7 +10,8 @@ export class TabsTab1NestedComponent implements OnInit {
|
|||||||
id = '';
|
id = '';
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
) {}
|
public navCtrl: NavController
|
||||||
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.id = this.route.snapshot.paramMap.get('id');
|
this.id = this.route.snapshot.paramMap.get('id');
|
||||||
@ -18,4 +20,5 @@ export class TabsTab1NestedComponent implements OnInit {
|
|||||||
next() {
|
next() {
|
||||||
return parseInt(this.id, 10) + 1;
|
return parseInt(this.id, 10) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,6 @@
|
|||||||
id="goto-nested-page1-with-query-params">Go to Page 2 with Query Params</ion-button>
|
id="goto-nested-page1-with-query-params">Go to Page 2 with Query Params</ion-button>
|
||||||
<ion-button routerLink="/tabs/lazy/nested" id="goto-tab3-page2">Go to Tab 3 - Page 2</ion-button>
|
<ion-button routerLink="/tabs/lazy/nested" id="goto-tab3-page2">Go to Tab 3 - Page 2</ion-button>
|
||||||
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go to nested</ion-button>
|
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go to nested</ion-button>
|
||||||
|
<ion-button (click)="navCtrl.pop()" id="goto-previous-page">Go to Previous Page</ion-button>
|
||||||
</p>
|
</p>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Component, NgZone } from '@angular/core';
|
import { Component, NgZone } from '@angular/core';
|
||||||
|
import { NavController } from '@ionic/angular';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tabs-tab1',
|
selector: 'app-tabs-tab1',
|
||||||
@ -9,6 +10,8 @@ export class TabsTab1Component {
|
|||||||
segment = 'one';
|
segment = 'one';
|
||||||
changed = 'false';
|
changed = 'false';
|
||||||
|
|
||||||
|
constructor(public navCtrl: NavController) {}
|
||||||
|
|
||||||
ionViewWillEnter() {
|
ionViewWillEnter() {
|
||||||
NgZone.assertInAngularZone();
|
NgZone.assertInAngularZone();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
Reference in New Issue
Block a user