Skip to content

Commit fce4422

Browse files
committed
fix(navigation): navs can have n child navs instead of just one
1 parent 04e78d8 commit fce4422

File tree

8 files changed

+80
-60
lines changed

8 files changed

+80
-60
lines changed

src/components/app/app.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,18 @@ export class App {
208208
/**
209209
* @return {NavController} Returns the active NavController. Using this method is preferred when we need access to the top-level navigation controller while on the outside views and handlers like `registerBackButtonAction()`
210210
*/
211-
getActiveNav(navId?: string): NavControllerBase {
211+
getActiveNavs(navId?: string): NavControllerBase[] {
212212
const portal = this._appRoot._getPortal(Constants.PORTAL_MODAL);
213213
if (portal.length() > 0) {
214-
return <NavControllerBase> findTopNav(portal);
214+
return <NavControllerBase[]> findTopNavs(portal);
215215
}
216216
if (!this._rootNavs || !this._rootNavs.size) {
217-
return null;
217+
return [];
218218
}
219219
if (this._rootNavs.size === 1) {
220-
return <NavControllerBase> findTopNav(this._rootNavs.values().next().value);
220+
return <NavControllerBase[]> findTopNavs(this._rootNavs.values().next().value);
221221
}
222-
return <NavControllerBase> findTopNav(this.getRootNavById(navId));
222+
return <NavControllerBase[]> findTopNavs(this.getRootNavById(navId));
223223
}
224224

225225
getRootNav(): any {
@@ -256,9 +256,9 @@ export class App {
256256

257257
getActiveNavContainers(): NavigationContainer[] {
258258
// for each root nav container, get it's active nav
259-
const list: NavigationContainer[] = [];
259+
let list: NavigationContainer[] = [];
260260
this._rootNavs.forEach((container: NavigationContainer) => {
261-
list.push(findTopNav(container));
261+
list = list.concat(findTopNavs(container));
262262
});
263263
return list;
264264
}
@@ -328,15 +328,16 @@ export class App {
328328
let navToPop: NavControllerBase = null;
329329
let mostRecentVC: ViewController = null;
330330
this._rootNavs.forEach((navContainer: NavigationContainer) => {
331-
const activeNav = this.getActiveNav(navContainer.id);
332-
const poppable = getPoppableNav(activeNav);
333-
if (poppable) {
331+
const activeNavs = this.getActiveNavs(navContainer.id);
332+
activeNavs.forEach(activeNav => console.log('navId: ', activeNav.id));
333+
const poppableNavs = activeNavs.map(activeNav => getPoppableNav(activeNav)).filter(nav => !!nav);
334+
poppableNavs.forEach(poppable => {
334335
const topViewController = poppable.last();
335336
if (poppable._isPortal || (topViewController && poppable.length() > 1 && (!mostRecentVC || topViewController._ts >= mostRecentVC._ts))) {
336337
mostRecentVC = topViewController;
337338
navToPop = poppable;
338339
}
339-
}
340+
});
340341
});
341342
if (navToPop) {
342343
return navToPop.pop();
@@ -419,15 +420,23 @@ function getPoppableNav(nav: NavControllerBase): NavControllerBase {
419420
return getPoppableNav(nav.parent);
420421
}
421422

422-
function findTopNav(nav: NavigationContainer): NavigationContainer {
423-
while (nav) {
424-
const childNav = nav.getActiveChildNav();
425-
if (!childNav) {
426-
break;
427-
}
428-
nav = childNav;
423+
export function findTopNavs(nav: NavigationContainer): NavigationContainer[] {
424+
//console.log('findTopNavs: nav.id: ', nav.id);
425+
let containers: NavigationContainer[] = [];
426+
const childNavs = nav.getActiveChildNavs();
427+
//console.log('findTopNavs: child navs: ', childNavs.length);
428+
if (!childNavs || !childNavs.length) {
429+
//console.log('pushing a nav: ', nav.id);
430+
containers.push(nav);
431+
} else {
432+
childNavs.forEach(childNav => {
433+
//console.log('calling findTopNavs with child: ', childNav.id);
434+
const topNavs = findTopNavs(childNav);
435+
containers = containers.concat(topNavs);
436+
});
429437
}
430-
return nav;
438+
//console.log('returning container: ', containers.length);
439+
return containers;
431440
}
432441

433442
const SKIP_BLURRING = ['INPUT', 'TEXTAREA', 'ION-INPUT', 'ION-TEXTAREA'];

src/components/app/test/app.spec.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App } from '../app';
1+
import { App, findTopNavs } from '../app';
22
import { ClickBlock } from '../click-block';
33
import { Config } from '../../../config/config';
44
import { mockApp, mockConfig, mockElementRef, mockNavController, mockPlatform, MockPlatform, mockRenderer, mockTab, mockTabs, mockView, mockViews } from '../../../util/mock-providers';
@@ -210,7 +210,7 @@ describe('App', () => {
210210
const view1 = mockView();
211211
mockViews(nav, [view1]);
212212

213-
expect(app.getActiveNav(nav.id)).toBe(nav);
213+
expect(app.getActiveNavs(nav.id)[0]).toBe(nav);
214214
expect(nav.first()).toBe(view1);
215215

216216
app.goBack();
@@ -233,7 +233,7 @@ describe('App', () => {
233233
const view1 = mockView();
234234
mockViews(nav, [view1]);
235235

236-
expect(app.getActiveNav(nav.id)).toBe(nav);
236+
expect(app.getActiveNavs(nav.id)[0]).toBe(nav);
237237
expect(nav.first()).toBe(view1);
238238

239239
app.goBack();
@@ -325,13 +325,21 @@ describe('App', () => {
325325

326326
tab2.setSelected(true);
327327
const nav2 = mockNavController();
328+
nav2.name = 'nav2';
328329
const nav3 = mockNavController();
330+
nav3.name = 'nav3';
329331
const nav4 = mockNavController();
332+
nav4.name = 'nav4';
333+
330334
tab1.registerChildNav(nav4);
335+
// tab 2 registers two child navs!!
331336
tab2.registerChildNav(nav2);
332337
tab2.registerChildNav(nav3);
333338

334-
expect(app.getActiveNav(nav.id)).toBe(nav3);
339+
const activeNavs = app.getActiveNavs(nav.id);
340+
expect(activeNavs.length).toEqual(2);
341+
expect(activeNavs[0]).toEqual(nav2);
342+
expect(activeNavs[1]).toEqual(nav3);
335343
});
336344

337345
it('should get active NavController when using tabs, nested in a root nav', () => {
@@ -346,11 +354,11 @@ describe('App', () => {
346354

347355
tab2.setSelected(true);
348356

349-
expect(app.getActiveNav(nav.id)).toBe(tab2);
357+
expect(app.getActiveNavs(nav.id)[0]).toBe(tab2);
350358

351359
tab2.setSelected(false);
352360
tab3.setSelected(true);
353-
expect(app.getActiveNav(nav.id)).toBe(tab3);
361+
expect(app.getActiveNavs(nav.id)[0]).toBe(tab3);
354362
});
355363

356364
it('should get active tab NavController when using tabs, and tabs is the root', () => {
@@ -362,11 +370,11 @@ describe('App', () => {
362370

363371
tab2.setSelected(true);
364372

365-
expect(app.getActiveNav(tabs.id)).toBe(tab2);
373+
expect(app.getActiveNavs(tabs.id)[0]).toBe(tab2);
366374

367375
tab2.setSelected(false);
368376
tab3.setSelected(true);
369-
expect(app.getActiveNav(tabs.id)).toBe(tab3);
377+
expect(app.getActiveNavs(tabs.id)[0]).toBe(tab3);
370378
});
371379

372380
it('should get active NavController when nested 3 deep', () => {
@@ -378,7 +386,7 @@ describe('App', () => {
378386
nav1.registerChildNav(nav2);
379387
nav2.registerChildNav(nav3);
380388

381-
expect(app.getActiveNav(nav1.id)).toBe(nav3);
389+
expect(app.getActiveNavs(nav1.id)[0]).toBe(nav3);
382390
});
383391

384392
it('should get active NavController when nested 2 deep', () => {
@@ -388,15 +396,15 @@ describe('App', () => {
388396

389397
nav1.registerChildNav(nav2);
390398

391-
const activeNav = app.getActiveNav(nav1.id);
399+
const activeNav = app.getActiveNavs(nav1.id)[0];
392400

393401
expect(activeNav).toBe(nav2);
394402
});
395403

396404
it('should get active NavController when only one nav controller', () => {
397405
const nav = mockNavController();
398406
app.registerRootNav(nav);
399-
expect(app.getActiveNav(nav.id)).toBe(nav);
407+
expect(app.getActiveNavs(nav.id)[0]).toBe(nav);
400408
});
401409

402410
it('should set/get the root nav controller', () => {
@@ -406,9 +414,9 @@ describe('App', () => {
406414
});
407415

408416
it('should not get an active NavController if there is not root set', () => {
409-
const activeNav = app.getActiveNav('');
417+
const activeNav = app.getActiveNavs('');
410418
const rootNav = app.getRootNavById('');
411-
expect(activeNav).toBeFalsy();
419+
expect(activeNav.length).toEqual(0);
412420
expect(rootNav).toBeFalsy();
413421
});
414422

@@ -425,8 +433,8 @@ describe('App', () => {
425433
rootNavOne.registerChildNav(childNavOne);
426434
rootNavTwo.registerChildNav(childNavTwo);
427435

428-
const activeNavOne = app.getActiveNav(rootNavOne.id);
429-
const activeNavTwo = app.getActiveNav(rootNavTwo.id);
436+
const activeNavOne = app.getActiveNavs(rootNavOne.id)[0];
437+
const activeNavTwo = app.getActiveNavs(rootNavTwo.id)[0];
430438

431439
expect(activeNavOne).toBe(childNavOne);
432440
expect(activeNavTwo).toBe(childNavTwo);
@@ -439,7 +447,7 @@ describe('App', () => {
439447
const childNavOne = mockNavController();
440448
rootNavOne.registerChildNav(childNavOne);
441449

442-
const result = app.getActiveNav();
450+
const result = app.getActiveNavs()[0];
443451

444452
expect(result).toEqual(childNavOne);
445453
});

src/components/tabs/tabs.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -502,8 +502,9 @@ export class Tabs extends Ion implements AfterViewInit, RootNode, ITabs, Navigat
502502
/**
503503
* @internal
504504
*/
505-
getActiveChildNav(): Tab {
506-
return this.getSelected();
505+
getActiveChildNavs(): Tab[] {
506+
const selected = this.getSelected();
507+
return selected ? [selected] : [];
507508
}
508509

509510
/**
@@ -624,9 +625,9 @@ export class Tabs extends Ion implements AfterViewInit, RootNode, ITabs, Navigat
624625
* @private
625626
*/
626627
getSecondaryIdentifier(): string {
627-
const tab = this.getActiveChildNav();
628-
if (tab) {
629-
return this._linker._getTabSelector(tab);
628+
const tabs = this.getActiveChildNavs();
629+
if (tabs && tabs.length) {
630+
return this._linker._getTabSelector(tabs[0]);
630631
}
631632
return '';
632633
}

src/navigation/deep-linker.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,16 @@ export class DeepLinker {
182182
getSegmentFromTab(navContainer: NavigationContainer, component?: any, data?: any): NavSegment {
183183
if (navContainer && navContainer.parent) {
184184
const tabsNavContainer = navContainer.parent as NavigationContainer;
185-
const activeChildNav = tabsNavContainer.getActiveChildNav();
186-
// since it's a tabs, we know that the activeChildNav is a tab
187-
const viewController = (activeChildNav as NavController).getActive(true);
188-
if (viewController) {
189-
component = viewController.component;
190-
data = viewController.data;
185+
const activeChildNavs = tabsNavContainer.getActiveChildNavs();
186+
if (activeChildNavs && activeChildNavs.length) {
187+
const activeChildNav = activeChildNavs[0];
188+
const viewController = (activeChildNav as NavController).getActive(true);
189+
if (viewController) {
190+
component = viewController.component;
191+
data = viewController.data;
192+
}
193+
return this._serializer.serializeComponent({ navId: tabsNavContainer.name || tabsNavContainer.id, secondaryId: tabsNavContainer.getSecondaryIdentifier(), type: 'tabs'}, component, data);
191194
}
192-
return this._serializer.serializeComponent({ navId: tabsNavContainer.name || tabsNavContainer.id, secondaryId: tabsNavContainer.getSecondaryIdentifier(), type: 'tabs'}, component, data);
193195
}
194196
}
195197

src/navigation/nav-controller-base.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { TransitionController } from '../transitions/transition-controller';
2626
*/
2727
export class NavControllerBase extends Ion implements NavController {
2828

29-
_child: NavigationContainer;
29+
_children: NavigationContainer[];
3030
_ids: number = -1;
3131
_init = false;
3232
_isPortal: boolean;
@@ -77,7 +77,7 @@ export class NavControllerBase extends Ion implements NavController {
7777
super(config, elementRef, renderer);
7878

7979
this._sbEnabled = config.getBoolean('swipeBackEnabled');
80-
80+
this._children = [];
8181
this.id = 'n' + (++ctrlIds);
8282
}
8383

@@ -755,7 +755,7 @@ export class NavControllerBase extends Ion implements NavController {
755755
// TODO - probably could be resolved in a better way
756756
this.setTransitioning(false);
757757

758-
if (!this.hasChild() && opts.updateUrl !== false) {
758+
if (!this.hasChildren() && opts.updateUrl !== false) {
759759
// notify deep linker of the nav change
760760
// if a direction was provided and should update url
761761
this._linker.navChange(this.id, opts.direction);
@@ -960,20 +960,20 @@ export class NavControllerBase extends Ion implements NavController {
960960
}
961961
}
962962

963-
hasChild(): boolean {
964-
return !!this._child;
963+
hasChildren(): boolean {
964+
return this._children && this._children.length > 0;
965965
}
966966

967-
getActiveChildNav(): any {
968-
return this._child;
967+
getActiveChildNavs(): any[] {
968+
return this._children;
969969
}
970970

971971
registerChildNav(container: NavigationContainer) {
972-
this._child = container;
972+
this._children.push(container);
973973
}
974974

975975
unregisterChildNav(nav: any) {
976-
this._child = null;
976+
this._children = this._children.filter(child => child !== nav);
977977
}
978978

979979
destroy() {
@@ -1048,7 +1048,7 @@ export class NavControllerBase extends Ion implements NavController {
10481048
canSwipeBack(): boolean {
10491049
return (this._sbEnabled &&
10501050
!this._isPortal &&
1051-
!this._child &&
1051+
!this._children.length &&
10521052
!this.isTransitioning() &&
10531053
this._app.isEnabled() &&
10541054
this.canGoBack());

src/navigation/nav-controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ export abstract class NavController implements NavigationContainer {
592592
/**
593593
* Returns the active child navigation.
594594
*/
595-
abstract getActiveChildNav(): any;
595+
abstract getActiveChildNavs(): any[];
596596

597597
/**
598598
* Returns if the nav controller is actively transitioning or not.

src/navigation/navigation-container.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export interface NavigationContainer {
44
id: string;
55
name: string;
66
parent: NavController;
7-
getActiveChildNav(): NavigationContainer;
7+
getActiveChildNavs(): NavigationContainer[];
88
getType(): string;
99
getSecondaryIdentifier(): string;
1010
}

src/navigation/test/nav-controller.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1128,7 +1128,7 @@ describe('NavController', () => {
11281128

11291129
it('should not swipe back if it has a child nav', () => {
11301130
nav._sbEnabled = true;
1131-
nav._child = mockNavController();
1131+
nav.registerChildNav(mockNavController());
11321132

11331133
const view1 = mockView();
11341134
const view2 = mockView();

0 commit comments

Comments
 (0)