diff --git a/src/components/app/app.ts b/src/components/app/app.ts
index 733e8cf1e8..9cdfddd293 100644
--- a/src/components/app/app.ts
+++ b/src/components/app/app.ts
@@ -419,8 +419,31 @@ export class App {
}
}
+ getNavByIdOrName(id: string) {
+ const navs = Array.from(this._rootNavs.values());
+ for (const navContainer of navs) {
+ const match = getNavByIdOrName(navContainer, id);
+ if (match) {
+ return match;
+ }
+ }
+ return null;
+ }
+
}
+export function getNavByIdOrName(nav: NavigationContainer, id: string): NavigationContainer {
+ if (nav.id === id || nav.name === id) {
+ return nav;
+ }
+ for (const child of nav.getAllChildNavs()) {
+ const tmp = getNavByIdOrName(child, id);
+ if (tmp) {
+ return tmp;
+ }
+ }
+ return null;
+}
function getPoppableNav(nav: NavControllerBase): NavControllerBase {
if (!nav) {
@@ -455,6 +478,7 @@ export function findTopNavs(nav: NavigationContainer): NavigationContainer[] {
return containers;
}
+
const SKIP_BLURRING = ['INPUT', 'TEXTAREA', 'ION-INPUT', 'ION-TEXTAREA'];
const ACTIVE_SCROLLING_TIME = 100;
const CLICK_BLOCK_BUFFER_IN_MILLIS = 64;
diff --git a/src/components/app/test/app.spec.ts b/src/components/app/test/app.spec.ts
index 02d5116d52..c84afbc404 100644
--- a/src/components/app/test/app.spec.ts
+++ b/src/components/app/test/app.spec.ts
@@ -767,6 +767,149 @@ describe('App', () => {
});
});
+ describe('getNavByIdOrName', () => {
+ it('should return a basic root nav', () => {
+ const nav = mockNavController();
+ app.registerRootNav(nav);
+ const result = app.getNavByIdOrName(nav.id);
+ expect(result).toEqual(nav);
+ });
+
+ it('should return a child nav', () => {
+ const rootNav = mockNavController();
+ app.registerRootNav(rootNav);
+
+ const childNav = mockNavController();
+ childNav.parent = rootNav;
+ rootNav.registerChildNav(childNav);
+
+ const childChildNav = mockNavController();
+ childChildNav.parent = childNav;
+ childNav.registerChildNav(childChildNav);
+
+
+ const expectedChildNav = app.getNavByIdOrName(childNav.id);
+ expect(expectedChildNav).toEqual(childNav);
+
+ const expectedChildChildNav = app.getNavByIdOrName(childChildNav.id);
+ expect(expectedChildChildNav).toEqual(childChildNav);
+ });
+
+ it('should return a child nav when there is a tabs in there', () => {
+ const rootNav = mockNavController();
+ app.registerRootNav(rootNav);
+
+ const tabs = mockTabs();
+ tabs.parent = rootNav;
+ rootNav.registerChildNav(tabs);
+
+ const tab1 = mockTab(tabs);
+ const tab2 = mockTab(tabs);
+ const tab3 = mockTab(tabs);
+
+ const tabChildNav = mockNavController();
+ tabChildNav.parent = tab2;
+ tab2.registerChildNav(tabChildNav);
+
+ const tabChildChildNav = mockNavController();
+ tabChildChildNav.parent = tabChildNav;
+ tabChildNav.registerChildNav(tabChildChildNav);
+
+ const expectedTab1 = app.getNavByIdOrName(tab1.id);
+ expect(expectedTab1).toEqual(tab1);
+
+ const expectedTab2 = app.getNavByIdOrName(tab2.id);
+ expect(expectedTab2).toEqual(tab2);
+
+ const expectedTab3 = app.getNavByIdOrName(tab3.id);
+ expect(expectedTab3).toEqual(tab3);
+
+ const expectedTabChildNav = app.getNavByIdOrName(tabChildNav.id);
+ expect(expectedTabChildNav).toEqual(tabChildNav);
+
+ const expectedTabChildChildNav = app.getNavByIdOrName(tabChildChildNav.id);
+ expect(expectedTabChildChildNav).toEqual(tabChildChildNav);
+ });
+
+ it('should return a basic root nav when the are multiple root navs', () => {
+ const rootNavOne = mockNavController();
+ const rootNavTwo = mockNavController();
+ const rootNavThree = mockNavController();
+ app.registerRootNav(rootNavOne);
+ app.registerRootNav(rootNavTwo);
+ app.registerRootNav(rootNavThree);
+
+ const expectedRootNavOne = app.getNavByIdOrName(rootNavOne.id);
+ expect(expectedRootNavOne).toEqual(rootNavOne);
+
+ const expectedRootNavTwo = app.getNavByIdOrName(rootNavTwo.id);
+ expect(expectedRootNavTwo).toEqual(rootNavTwo);
+
+ const expectedRootNavThree = app.getNavByIdOrName(rootNavThree.id);
+ expect(expectedRootNavThree).toEqual(rootNavThree);
+ });
+
+ it('should return a proper navs when there are multiple root navs with nested navs', () => {
+ const rootNavOne = mockNavController();
+ const rootNavTwo = mockNavController();
+ const rootNavThree = mockNavController();
+ app.registerRootNav(rootNavOne);
+ app.registerRootNav(rootNavTwo);
+ app.registerRootNav(rootNavThree);
+
+ const childNavOne = mockNavController();
+ childNavOne.parent = rootNavOne;
+ rootNavOne.registerChildNav(childNavOne);
+
+ const childChildNavOne = mockNavController();
+ childChildNavOne.parent = childNavOne;
+ childNavOne.registerChildNav(childChildNavOne);
+
+ const childNavTwo = mockNavController();
+ childNavOne.parent = rootNavTwo;
+ rootNavTwo.registerChildNav(childNavTwo);
+
+ const childChildNavTwo = mockNavController();
+ childChildNavTwo.parent = childNavTwo;
+ childNavTwo.registerChildNav(childChildNavTwo);
+
+ const childNavThree = mockNavController();
+ childNavThree.parent = rootNavThree;
+ rootNavThree.registerChildNav(childNavThree);
+
+ const childChildNavThree = mockNavController();
+ childChildNavThree.parent = childNavThree;
+ childNavThree.registerChildNav(childChildNavThree);
+
+ const expectedRootNavOne = app.getNavByIdOrName(rootNavOne.id);
+ expect(expectedRootNavOne).toEqual(rootNavOne);
+
+ const expectedChildNavOne = app.getNavByIdOrName(childNavOne.id);
+ expect(expectedChildNavOne).toEqual(childNavOne);
+
+ const expectedChildChildNavOne = app.getNavByIdOrName(childChildNavOne.id);
+ expect(expectedChildChildNavOne).toEqual(childChildNavOne);
+
+ const expectedRootNavTwo = app.getNavByIdOrName(rootNavTwo.id);
+ expect(expectedRootNavTwo).toEqual(rootNavTwo);
+
+ const expectedChildNavTwo = app.getNavByIdOrName(childNavTwo.id);
+ expect(expectedChildNavTwo).toEqual(childNavTwo);
+
+ const expectedChildChildNavTwo = app.getNavByIdOrName(childChildNavTwo.id);
+ expect(expectedChildChildNavTwo).toEqual(childChildNavTwo);
+
+ const expectedRootNavThree = app.getNavByIdOrName(rootNavThree.id);
+ expect(expectedRootNavThree).toEqual(rootNavThree);
+
+ const expectedChildNavThree = app.getNavByIdOrName(childNavThree.id);
+ expect(expectedChildNavThree).toEqual(childNavThree);
+
+ const expectedChildChildNavThree = app.getNavByIdOrName(childChildNavThree.id);
+ expect(expectedChildChildNavThree).toEqual(childChildNavThree);
+ });
+ });
+
let app: App;
let config: Config;
let plt: MockPlatform;
diff --git a/src/components/nav/test/advanced-nav-then-tabs/app/app.component.ts b/src/components/nav/test/advanced-nav-then-tabs/app/app.component.ts
index 558e9538fb..f18c526c21 100644
--- a/src/components/nav/test/advanced-nav-then-tabs/app/app.component.ts
+++ b/src/components/nav/test/advanced-nav-then-tabs/app/app.component.ts
@@ -3,8 +3,8 @@ import { Component } from '@angular/core';
@Component({
template: `
-
-
+
+
`
diff --git a/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-one-page-three/tab-one-page-three.ts b/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-one-page-three/tab-one-page-three.ts
index ddc3d0dc36..5deb7fc068 100644
--- a/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-one-page-three/tab-one-page-three.ts
+++ b/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-one-page-three/tab-one-page-three.ts
@@ -19,7 +19,6 @@ import { IonicPage, NavController, NavParams } from '../../../../../../..';
Name: {{paramTwo}}
-
`
})
diff --git a/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-two-page-three/tab-two-page-three.ts b/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-two-page-three/tab-two-page-three.ts
index 1095975b5c..74c07a8f70 100644
--- a/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-two-page-three/tab-two-page-three.ts
+++ b/src/components/nav/test/advanced-tabs/pages/tabs-one/tab-two-page-three/tab-two-page-three.ts
@@ -14,10 +14,10 @@ import { IonicPage, NavController, NavParams } from '../../../../../../..';
Tabs 1 Tab 2 Page 3
- Param One: {{userId}}
+ Param One: {{paramOne}}
- Param Two: {{name}}
+ Param Two: {{paramTwo}}
`
diff --git a/src/components/nav/test/simple-nested-peer-navs/app/app.component.ts b/src/components/nav/test/simple-nested-peer-navs/app/app.component.ts
new file mode 100644
index 0000000000..3337574380
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/app/app.component.ts
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+ template: ``
+})
+export class AppComponent {
+ root = 'LoginPage';
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/app/app.module.ts b/src/components/nav/test/simple-nested-peer-navs/app/app.module.ts
new file mode 100644
index 0000000000..55310fa215
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { IonicApp, IonicModule } from '../../../../..';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ declarations: [
+ AppComponent
+ ],
+ imports: [
+ BrowserModule,
+ IonicModule.forRoot(AppComponent, { swipeBackEnabled: true, preloadModules: true }),
+ ],
+ bootstrap: [IonicApp]
+})
+export class AppModule {}
diff --git a/src/components/nav/test/simple-nested-peer-navs/app/main.ts b/src/components/nav/test/simple-nested-peer-navs/app/main.ts
new file mode 100644
index 0000000000..6af7a5b2ae
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/app/main.ts
@@ -0,0 +1,5 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/landing-page/landing-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/landing-page/landing-page.module.ts
new file mode 100644
index 0000000000..ba7d5165e8
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/landing-page/landing-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../..';
+import { LandingPage } from './landing-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(LandingPage)
+ ],
+ declarations: [
+ LandingPage
+ ]
+})
+export class LandingPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/landing-page/landing-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/landing-page/landing-page.ts
new file mode 100644
index 0000000000..da2c93925c
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/landing-page/landing-page.ts
@@ -0,0 +1,30 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, } from '../../../../../..';
+
+@IonicPage()
+@Component({
+ template: `
+
+
+ My Super Cool, multi-pane App
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class LandingPage {
+ constructor(public nav: NavController) {
+ }
+
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.html b/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.html
new file mode 100644
index 0000000000..e82b2f2388
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.html
@@ -0,0 +1,9 @@
+
+
+ Page One
+
+
+
+ Page One
+
+
\ No newline at end of file
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.module.ts
new file mode 100644
index 0000000000..c17e0dde59
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../../..';
+import { FirstPage } from './first-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(FirstPage)
+ ],
+ declarations: [
+ FirstPage
+ ]
+})
+export class FirstPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.ts
new file mode 100644
index 0000000000..027607bc6d
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/first-page/first-page.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, } from '../../../../../../..';
+
+@IonicPage()
+@Component({
+ templateUrl: 'first-page.html'
+})
+export class FirstPage {
+ constructor(public nav: NavController) {
+ }
+
+ goToPageTwo() {
+ this.nav.push('SecondPage', { userId: '123', name: 'Michael Scott'});
+ }
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.html b/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.html
new file mode 100644
index 0000000000..39d1124a07
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.html
@@ -0,0 +1,15 @@
+
+
+ Page Two
+
+
+
+ Page Two
+
+ User ID: {{userId}}
+
+
+ Name {{name}}
+
+
+
\ No newline at end of file
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.module.ts
new file mode 100644
index 0000000000..ce824037f8
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../../..';
+import { SecondPage } from './second-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(SecondPage)
+ ],
+ declarations: [
+ SecondPage
+ ]
+})
+export class SecondPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.ts
new file mode 100644
index 0000000000..44abf580fe
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/second-page/second-page.ts
@@ -0,0 +1,22 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, NavParams } from '../../../../../../..';
+
+@IonicPage({
+ segment: 'pageTwo/user/:userId/name/:name'
+})
+@Component({
+ templateUrl: 'second-page.html'
+})
+export class SecondPage {
+
+ userId: string;
+ name: string;
+ constructor(public nav: NavController, public params: NavParams) {
+ this.userId = this.params.data.userId;
+ this.name = this.params.data.name;
+ }
+
+ goToNextPage() {
+ this.nav.push('ThirdPage', { paramOne: 'mono', paramTwo: 'stereo'});
+ }
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.html b/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.html
new file mode 100644
index 0000000000..af317e8de8
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.html
@@ -0,0 +1,14 @@
+
+
+ Page Three
+
+
+
+ Page Three
+
+ Param One: {{paramOne}}
+
+
+ Param Two: {{paramTwo}}
+
+
\ No newline at end of file
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.module.ts
new file mode 100644
index 0000000000..fab76aa598
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../../..';
+import { ThirdPage } from './third-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(ThirdPage)
+ ],
+ declarations: [
+ ThirdPage
+ ]
+})
+export class ThirdPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.ts
new file mode 100644
index 0000000000..9cbbc0413f
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/left/third-page/third-page.ts
@@ -0,0 +1,17 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, NavParams} from '../../../../../../..';
+
+@IonicPage({
+ segment: 'thirdPage/paramOne/:paramOne/paramTwo/:paramTwo'
+})
+@Component({
+ templateUrl: 'third-page.html'
+})
+export class ThirdPage {
+ paramOne: string;
+ paramTwo: string;
+ constructor(public nav: NavController, public params: NavParams) {
+ this.paramOne = params.data.paramOne;
+ this.paramTwo = params.data.paramTwo;
+ }
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/login-page/login-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/login-page/login-page.module.ts
new file mode 100644
index 0000000000..e3cbf3d916
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/login-page/login-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../..';
+import { LoginPage } from './login-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(LoginPage)
+ ],
+ declarations: [
+ LoginPage
+ ]
+})
+export class LoginPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/login-page/login-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/login-page/login-page.ts
new file mode 100644
index 0000000000..3b781c7d4c
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/login-page/login-page.ts
@@ -0,0 +1,24 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, } from '../../../../../..';
+
+@IonicPage()
+@Component({
+ template: `
+
+
+ Login
+
+
+
+
+
+ `
+})
+export class LoginPage {
+ constructor(public nav: NavController) {
+ }
+
+ clickMe() {
+ this.nav.push('LandingPage');
+ }
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.html b/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.html
new file mode 100644
index 0000000000..3a700e617f
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.html
@@ -0,0 +1,15 @@
+
+
+ Page Five
+
+
+
+ Page Five
+
+ User ID: {{userId}}
+
+
+ Name {{name}}
+
+
+
\ No newline at end of file
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.module.ts
new file mode 100644
index 0000000000..1499d707cf
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../../..';
+import { FifthPage } from './fifth-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(FifthPage)
+ ],
+ declarations: [
+ FifthPage
+ ]
+})
+export class FifthPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.ts
new file mode 100644
index 0000000000..fd3023b1f5
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/fifth-page/fifth-page.ts
@@ -0,0 +1,22 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, NavParams } from '../../../../../../..';
+
+@IonicPage({
+ segment: 'pageFive/user/:userId/name/:name'
+})
+@Component({
+ templateUrl: 'fifth-page.html'
+})
+export class FifthPage {
+
+ userId: string;
+ name: string;
+ constructor(public nav: NavController, public params: NavParams) {
+ this.userId = this.params.data.userId;
+ this.name = this.params.data.name;
+ }
+
+ goToNextPage() {
+ this.nav.push('SixthPage', { paramOne: 'Tobey', paramTwo: 'Holly'});
+ }
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.html b/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.html
new file mode 100644
index 0000000000..a89fef0a4e
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.html
@@ -0,0 +1,9 @@
+
+
+ Page Four
+
+
+
+ Page Four
+
+
\ No newline at end of file
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.module.ts
new file mode 100644
index 0000000000..d041e3bcc8
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../../..';
+import { FourthPage } from './fourth-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(FourthPage)
+ ],
+ declarations: [
+ FourthPage
+ ]
+})
+export class FourthPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.ts
new file mode 100644
index 0000000000..6ad06442e0
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/fouth-page/fourth-page.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, } from '../../../../../../..';
+
+@IonicPage()
+@Component({
+ templateUrl: 'fourth-page.html'
+})
+export class FourthPage {
+ constructor(public nav: NavController) {
+ }
+
+ goToPageTwo() {
+ this.nav.push('FifthPage', { userId: '567', name: 'Pamela Beasley'});
+ }
+}
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.html b/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.html
new file mode 100644
index 0000000000..8732947e36
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.html
@@ -0,0 +1,14 @@
+
+
+ Page Six
+
+
+
+ Page Six
+
+ Param One: {{paramOne}}
+
+
+ Param Two: {{paramTwo}}
+
+
\ No newline at end of file
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.module.ts b/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.module.ts
new file mode 100644
index 0000000000..212bd5c889
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { IonicPageModule } from '../../../../../../..';
+import { SixthPage } from './sixth-page';
+
+@NgModule({
+ imports: [
+ IonicPageModule.forChild(SixthPage)
+ ],
+ declarations: [
+ SixthPage
+ ]
+})
+export class SixthPageModule { }
diff --git a/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.ts b/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.ts
new file mode 100644
index 0000000000..6f075c5fa4
--- /dev/null
+++ b/src/components/nav/test/simple-nested-peer-navs/pages/right/sixth-page/sixth-page.ts
@@ -0,0 +1,18 @@
+import { Component } from '@angular/core';
+import { IonicPage, NavController, NavParams} from '../../../../../../..';
+
+@IonicPage({
+ segment: 'sixthPage/paramOne/:paramOne/paramTwo/:paramTwo'
+})
+@Component({
+ templateUrl: 'sixth-page.html'
+})
+export class SixthPage {
+
+ paramOne: string;
+ paramTwo: string;
+ constructor(public nav: NavController, public params: NavParams) {
+ this.paramOne = params.data.paramOne;
+ this.paramTwo = params.data.paramTwo;
+ }
+}
diff --git a/src/components/tabs/tabs.ts b/src/components/tabs/tabs.ts
index 15fd28a2a2..302432d1e6 100644
--- a/src/components/tabs/tabs.ts
+++ b/src/components/tabs/tabs.ts
@@ -507,6 +507,13 @@ export class Tabs extends Ion implements AfterViewInit, RootNode, ITabs, Navigat
return selected ? [selected] : [];
}
+ /**
+ * @internal
+ */
+ getAllChildNavs(): any[] {
+ return this._tabs;
+ }
+
/**
* @internal
*/
diff --git a/src/module.ts b/src/module.ts
index 6ac1054412..e9b48d7980 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -445,7 +445,7 @@ export class IonicModule {
{ provide: ModuleLoader, useFactory: provideModuleLoader, deps: [NgModuleLoader, Injector]},
{ provide: LocationStrategy, useFactory: provideLocationStrategy, deps: [ PlatformLocation, [new Inject(APP_BASE_HREF), new Optional()], Config ] },
- { provide: UrlSerializer, useFactory: setupUrlSerializer, deps: [ DeepLinkConfigToken ] },
+ { provide: UrlSerializer, useFactory: setupUrlSerializer, deps: [ App, DeepLinkConfigToken ] },
{ provide: DeepLinker, useFactory: setupDeepLinker, deps: [ App, UrlSerializer, Location, ModuleLoader, ComponentFactoryResolver ] },
]
};
diff --git a/src/navigation/deep-linker.ts b/src/navigation/deep-linker.ts
index 4fff116f0c..3c5d06c748 100644
--- a/src/navigation/deep-linker.ts
+++ b/src/navigation/deep-linker.ts
@@ -131,18 +131,23 @@ export class DeepLinker {
*/
navChange(direction: string) {
if (direction) {
- const rootNavContainers = this._app.getActiveNavContainers();
+ const activeNavContainers = this._app.getActiveNavContainers();
// the only time you'll ever get a TABS here is when loading directly from a URL
// this method will be called again when the TAB is loaded
// so just don't worry about the TABS for now
// if you encounter a TABS, just return
- let segments: NavSegment[] = [];
- for (const rootNavContainer of rootNavContainers) {
- if (isTabs(rootNavContainer) || (rootNavContainer as NavController).isTransitioning()) {
+ for (const activeNavContainer of activeNavContainers) {
+ if (isTabs(activeNavContainer) || (activeNavContainer as NavController).isTransitioning()) {
return;
}
- const segmentsForNav = this.getSegmentsFromNav(rootNavContainer);
- segments = segments.concat(segmentsForNav);
+ }
+
+ // okay, get the root navs and build the segments up
+ let segments: NavSegment[] = [];
+ const navContainers: NavigationContainer[] = this._app.getRootNavs();
+ for (const navContainer of navContainers) {
+ const segmentsForNav = this.getSegmentsFromNav(navContainer);
+ segments = segments.concat(segmentsForNav);
}
segments = segments.filter(segment => !!segment);
if (segments.length) {
@@ -153,19 +158,16 @@ export class DeepLinker {
}
getSegmentsFromNav(nav: NavigationContainer): NavSegment[] {
- const segments: NavSegment[] = [];
- while (nav) {
- if (isNav(nav)) {
- segments.push(this.getSegmentFromNav(nav as NavController));
- nav = nav.parent;
- } else if (isTab(nav)) {
- segments.push(this.getSegmentFromTab(nav));
- nav = nav.parent && nav.parent.parent;
- } else {
- nav = nav.parent;
- }
+ let segments: NavSegment[] = [];
+ if (isNav(nav)) {
+ segments.push(this.getSegmentFromNav(nav as NavController));
+ } else if (isTab(nav)) {
+ segments.push(this.getSegmentFromTab(nav));
}
- return segments.reverse();
+ nav.getActiveChildNavs().forEach(child => {
+ segments = segments.concat(this.getSegmentsFromNav(child));
+ });
+ return segments;
}
getSegmentFromNav(nav: NavController, component?: any, data?: any): NavSegment {
@@ -176,7 +178,7 @@ export class DeepLinker {
data = viewController.data;
}
}
- return this._serializer.serializeComponent({ navId: nav.name && nav.name.length ? nav.name : nav.id, secondaryId: null, type: 'nav'}, component, data);
+ return this._serializer.serializeComponent(nav, component, data);
}
getSegmentFromTab(navContainer: NavigationContainer, component?: any, data?: any): NavSegment {
@@ -190,7 +192,7 @@ export class DeepLinker {
component = viewController.component;
data = viewController.data;
}
- return this._serializer.serializeComponent({ navId: tabsNavContainer.name || tabsNavContainer.id, secondaryId: tabsNavContainer.getSecondaryIdentifier(), type: 'tabs'}, component, data);
+ return this._serializer.serializeComponent(tabsNavContainer, component, data);
}
}
}
@@ -415,7 +417,7 @@ export class DeepLinker {
return navController.popTo(viewController, {
animate: false,
updateUrl: false,
- }, done);
+ }, {}, done);
}
}
}
diff --git a/src/navigation/nav-controller-base.ts b/src/navigation/nav-controller-base.ts
index e921c52ce7..b1dae7203c 100644
--- a/src/navigation/nav-controller-base.ts
+++ b/src/navigation/nav-controller-base.ts
@@ -991,6 +991,10 @@ export class NavControllerBase extends Ion implements NavController {
return this._children;
}
+ getAllChildNavs(): any[] {
+ return this._children;
+ }
+
registerChildNav(container: NavigationContainer) {
this._children.push(container);
}
diff --git a/src/navigation/nav-controller.ts b/src/navigation/nav-controller.ts
index 0f02e0f0eb..58be7a19c3 100644
--- a/src/navigation/nav-controller.ts
+++ b/src/navigation/nav-controller.ts
@@ -599,6 +599,11 @@ export abstract class NavController implements NavigationContainer {
*/
abstract getActiveChildNav(): any;
+ /**
+ * Returns a list of all child navigation containers
+ */
+ abstract getAllChildNavs(): any[];
+
/**
* Returns if the nav controller is actively transitioning or not.
diff --git a/src/navigation/nav-util.ts b/src/navigation/nav-util.ts
index 772978a5fa..1c947cacbe 100644
--- a/src/navigation/nav-util.ts
+++ b/src/navigation/nav-util.ts
@@ -163,16 +163,26 @@ export interface NavResult {
direction?: string;
}
-export interface NavSegment {
+export interface NavSegment extends DehydratedSegment {
+ type: string;
+ navId: string;
+ secondaryId: string;
+ requiresExplicitNavPrefix?: boolean;
+}
+
+export interface DehydratedSegment {
id: string;
name: string;
component?: any;
loadChildren?: string;
data: any;
- type: string;
- navId: string;
- secondaryId: string;
defaultHistory?: NavSegment[];
+ secondaryId?: string;
+}
+
+export interface DehydratedSegmentPair {
+ segments: DehydratedSegment[];
+ navGroup: NavGroup;
}
export interface NavGroup {
diff --git a/src/navigation/navigation-container.ts b/src/navigation/navigation-container.ts
index 8cb8daf8a3..a5bbb93290 100644
--- a/src/navigation/navigation-container.ts
+++ b/src/navigation/navigation-container.ts
@@ -1,10 +1,14 @@
import { NavController } from './nav-controller';
+/**
+ * @hidden
+ */
export interface NavigationContainer {
id: string;
name: string;
parent: NavController;
getActiveChildNavs(): NavigationContainer[];
+ getAllChildNavs?(): NavigationContainer[];
getType(): string;
getSecondaryIdentifier(): string;
}
diff --git a/src/navigation/test/deep-linker.spec.ts b/src/navigation/test/deep-linker.spec.ts
index ea7b50736a..5f23f4a633 100644
--- a/src/navigation/test/deep-linker.spec.ts
+++ b/src/navigation/test/deep-linker.spec.ts
@@ -457,9 +457,7 @@ describe('DeepLinker', () => {
linker.getSegmentFromNav(mockNav, null, null);
- expect(spy.calls.first().args[0].navId).toEqual(mockNav.name);
- expect(spy.calls.first().args[0].secondaryId).toBeFalsy();
- expect(spy.calls.first().args[0].type).toEqual('nav');
+ expect(spy.calls.first().args[0]).toEqual(mockNav);
});
it('should use the id of the nav when name doesnt exists', () => {
@@ -472,9 +470,7 @@ describe('DeepLinker', () => {
linker.getSegmentFromNav(mockNav, null, null);
- expect(spy.calls.first().args[0].navId).toEqual(mockNav.id);
- expect(spy.calls.first().args[0].secondaryId).toBeFalsy();
- expect(spy.calls.first().args[0].type).toEqual('nav');
+ expect(spy.calls.first().args[0]).toEqual(mockNav);
});
});
@@ -493,7 +489,7 @@ describe('DeepLinker', () => {
linker.getSegmentFromTab(tabOne, null, null);
expect(spy).toHaveBeenCalled();
- expect(spy.calls.first().args[0].navId).toEqual(tabs.name);
+ expect(spy.calls.first().args[0]).toEqual(tabs);
});
@@ -511,7 +507,7 @@ describe('DeepLinker', () => {
linker.getSegmentFromTab(tabOne, null, null);
expect(spy).toHaveBeenCalled();
- expect(spy.calls.first().args[0].navId).toEqual(tabs.id);
+ expect(spy.calls.first().args[0]).toEqual(tabs);
});
});
@@ -685,7 +681,7 @@ describe('DeepLinker', () => {
beforeEach(() => {
let linkConfig = mockDeepLinkConfig();
- serializer = new UrlSerializer(linkConfig);
+ serializer = new UrlSerializer(mockApp(), linkConfig);
let moduleLoader = mockModuleLoader();
let baseCfr: any = null;
diff --git a/src/navigation/test/url-serializer.spec.ts b/src/navigation/test/url-serializer.spec.ts
index ed3000a73b..40a4f29d08 100644
--- a/src/navigation/test/url-serializer.spec.ts
+++ b/src/navigation/test/url-serializer.spec.ts
@@ -1,17 +1,18 @@
import { NavLink, NavSegment } from '../nav-util';
import {
- NavGroup,
UrlSerializer,
+ convertUrlToDehydratedSegments,
+ convertUrlToSegments,
createMatchedData,
findLinkByComponentData,
formatUrlPart,
isPartMatch,
navGroupStringtoObjects,
normalizeLinks,
- parseUrlParts,
urlToNavGroupStrings,
+ urlToNavGroupStringsTwo,
} from '../url-serializer';
-import { MockView1, MockView2, MockView3, mockDeepLinkConfig, mockNavController, noop } from '../../util/mock-providers';
+import { MockView1, MockView2, MockView3, mockApp, mockDeepLinkConfig, mockNavController, mockTab, mockTabs, noop } from '../../util/mock-providers';
describe('UrlSerializer', () => {
@@ -22,32 +23,37 @@ describe('UrlSerializer', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView1, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView1, name: 'viewthree', segment: 'view/:param1/:param2' };
- const navGroup: NavGroup = { type: 'nav', navId: 'n1', secondaryId: null, segmentPieces: ['view']};
serializer = mockSerializer([link1, link2, link3]);
serializer._createSegment = noop;
spyOn(serializer, '_createSegment');
- serializer.serializeComponent(navGroup, MockView1, null);
- expect(serializer._createSegment).toHaveBeenCalledWith(navGroup, link1, null);
+ const nav = mockNavController();
+ serializer.serializeComponent(nav, MockView1, null);
+ expect(serializer._createSegment).toHaveBeenCalledWith(serializer._app, nav, link1, null);
});
it('should create segment if component found in links', () => {
serializer._createSegment = noop;
spyOn(serializer, '_createSegment');
- serializer.serializeComponent({ type: 'nav', navId: 'n1', secondaryId: null, segmentPieces: ['view']}, MockView1, null);
+ const nav = mockNavController();
+ serializer.serializeComponent(nav, MockView1, null);
expect(serializer._createSegment).toHaveBeenCalled();
});
it('should return null if component not found in links', () => {
serializer._createSegment = noop;
spyOn(serializer, '_createSegment');
- serializer.serializeComponent({ type: 'nav', navId: 'n1', secondaryId: null, segmentPieces: ['view']}, NotFound, null);
+ const nav = mockNavController();
+ serializer.serializeComponent(nav, NotFound, null);
expect(serializer._createSegment).not.toHaveBeenCalled();
});
it('should create tab segment if component found in deep links', () => {
serializer._createSegment = noop;
spyOn(serializer, '_createSegment');
- serializer.serializeComponent({ type: 'nav', navId: 'n1', secondaryId: null, segmentPieces: ['view']}, MockView1, null);
+
+ const tabs = mockTabs();
+ const tab = mockTab(tabs);
+ serializer.serializeComponent(tab, MockView1, null);
expect(serializer._createSegment).toHaveBeenCalled();
});
@@ -65,7 +71,8 @@ describe('UrlSerializer', () => {
name: 'jenny'
};
- const segment = serializer._createSegment({ navId: '1', type: 'nav', secondaryId: null}, link, data);
+ const nav = mockNavController();
+ const segment = serializer._createSegment(serializer._app, nav, link, data);
expect(segment.id).toEqual('userId/8675309/name/jenny');
expect(segment.component).toEqual(link.component);
expect(segment.data.id).toEqual(data.id);
@@ -84,7 +91,8 @@ describe('UrlSerializer', () => {
id: char,
name: 'jenny'
};
- const segment = serializer._createSegment({ navId: '1', type: 'nav', secondaryId: null}, link, data);
+ const nav = mockNavController();
+ const segment = serializer._createSegment(serializer._app, nav, link, data);
expect(segment.id).toEqual(`userId/${encoded}/name/${data.name}`);
expect(segment.component).toEqual(MockView1);
expect(segment.data.id).toEqual(char);
@@ -95,7 +103,8 @@ describe('UrlSerializer', () => {
segmentParts: ['settings-view'],
component: MockView1
};
- const segment = serializer._createSegment({ navId: '1', type: 'nav', secondaryId: null}, link, null);
+ const nav = mockNavController();
+ const segment = serializer._createSegment(serializer._app, nav, link, null);
expect(segment.id).toEqual('settings-view');
expect(segment.component).toEqual(MockView1);
expect(segment.data).toEqual(null);
@@ -179,106 +188,51 @@ describe('UrlSerializer', () => {
describe('navGroupStringtoObjects', () => {
it('should convert the nav group strings to objects', () => {
- const urlChunks = ['nav/1/chunk/of/segment', 'tabs/1/tab-one/chunk/of/segment'];
+ const urlChunks = ['taco/burrito/pizza/nachos', 'nav/1/chunk/of/segment', 'tabs/1/tab-one/chunk/of/segment'];
const objects = navGroupStringtoObjects(urlChunks);
- expect(objects.length).toEqual(2);
- expect(objects[0].type).toEqual('nav');
- expect(objects[0].navId).toEqual('1');
+ expect(objects.length).toEqual(3);
+ expect(objects[0].type).toEqual(null);
+ expect(objects[0].navId).toEqual(null);
expect(objects[0].secondaryId).toEqual(null);
- expect(objects[0].segmentPieces.length).toEqual(3);
- expect(objects[0].segmentPieces[0]).toEqual('chunk');
- expect(objects[0].segmentPieces[1]).toEqual('of');
- expect(objects[0].segmentPieces[2]).toEqual('segment');
- expect(objects[1].type).toEqual('tabs');
+ expect(objects[0].segmentPieces.length).toEqual(4);
+ expect(objects[0].segmentPieces[0]).toEqual('taco');
+ expect(objects[0].segmentPieces[1]).toEqual('burrito');
+ expect(objects[0].segmentPieces[2]).toEqual('pizza');
+ expect(objects[0].segmentPieces[3]).toEqual('nachos');
+ expect(objects[1].type).toEqual('nav');
expect(objects[1].navId).toEqual('1');
- expect(objects[1].secondaryId).toEqual('tab-one');
+ expect(objects[1].secondaryId).toEqual(null);
expect(objects[1].segmentPieces.length).toEqual(3);
expect(objects[1].segmentPieces[0]).toEqual('chunk');
expect(objects[1].segmentPieces[1]).toEqual('of');
expect(objects[1].segmentPieces[2]).toEqual('segment');
- });
- });
-
- describe('parse', () => {
-
- it('should return empty list of segments for bogus url', () => {
- serializer = mockSerializer([]);
-
- const segments = serializer.parse('/some/bogus/url');
- expect(segments.length).toEqual(0);
- });
-
- it('should return empty list of segments when there isnt a match', () => {
- serializer = mockSerializer([
- { segment: 'some/chunk/of/url', name: 'viewone', component: MockView1 },
- { segment: 'another/section/of/url', name: 'viewtwo', component: MockView2 }
- ]);
- const segments = serializer.parse('/nav/n1/not/a/matching/url');
- expect(segments.length).toEqual(0);
- });
-
- it('should return the segments from the url with multiple navs', () => {
- serializer = mockSerializer([
- { segment: 'userId/:id/name/:name', name: 'viewone', component: MockView1 },
- { segment: 'selectedId/:id/food/:food', name: 'viewtwo', component: MockView2 }
- ]);
- const segments = serializer.parse('/nav/n1/userId/123/name/Stanley/nav/n2/selectedId/456/food/tacos');
- expect(segments.length).toEqual(2);
- expect(segments[0].name).toEqual('viewone');
- expect(segments[0].data.id).toEqual('123');
- expect(segments[0].data.name).toEqual('Stanley');
- expect(segments[1].name).toEqual('viewtwo');
- expect(segments[1].data.id).toEqual('456');
- expect(segments[1].data.food).toEqual('tacos');
- });
-
- it('should return the segments from the url with multiple tabs', () => {
- serializer = mockSerializer([
- { segment: 'userId/:id/name/:name', name: 'viewone', component: MockView1 },
- { segment: 'selectedId/:id/food/:food', name: 'viewtwo', component: MockView2 }
- ]);
- const segments = serializer.parse('/tabs/t1/tab-one/userId/123/name/Stanley/tabs/t2/tab-three/selectedId/456/food/tacos');
- expect(segments.length).toEqual(2);
- expect(segments[0].name).toEqual('viewone');
- expect(segments[0].navId).toEqual('t1');
- expect(segments[0].data.id).toEqual('123');
- expect(segments[0].data.name).toEqual('Stanley');
- expect(segments[0].secondaryId).toEqual('tab-one');
- expect(segments[1].name).toEqual('viewtwo');
- expect(segments[1].navId).toEqual('t2');
- expect(segments[1].data.id).toEqual('456');
- expect(segments[1].data.food).toEqual('tacos');
- expect(segments[1].secondaryId).toEqual('tab-three');
- });
-
- it('should return the segments from a mixed nav/tabs url', () => {
- serializer = mockSerializer([
- { segment: 'userId/:id/name/:name', name: 'viewone', component: MockView1 },
- { segment: 'selectedId/:id/food/:food', name: 'viewtwo', component: MockView2 }
- ]);
- const segments = serializer.parse('/tabs/t1/tab-one/userId/123/name/Stanley/nav/n1/selectedId/456/food/tacos');
- expect(segments.length).toEqual(2);
- expect(segments[0].name).toEqual('viewone');
- expect(segments[0].navId).toEqual('t1');
- expect(segments[0].data.id).toEqual('123');
- expect(segments[0].data.name).toEqual('Stanley');
- expect(segments[0].secondaryId).toEqual('tab-one');
- expect(segments[1].name).toEqual('viewtwo');
- expect(segments[1].navId).toEqual('n1');
- expect(segments[1].data.id).toEqual('456');
- expect(segments[1].data.food).toEqual('tacos');
- expect(segments[1].secondaryId).toEqual(null);
+ expect(objects[2].type).toEqual('tabs');
+ expect(objects[2].navId).toEqual('1');
+ expect(objects[2].secondaryId).toEqual('tab-one');
+ expect(objects[2].segmentPieces.length).toEqual(3);
+ expect(objects[2].segmentPieces[0]).toEqual('chunk');
+ expect(objects[2].segmentPieces[1]).toEqual('of');
+ expect(objects[2].segmentPieces[2]).toEqual('segment');
});
});
describe('serialize', () => {
+ it('should serialize multiple segments into a url with explicit prefixs', () => {
+ let paths: NavSegment[] = [
+ { type: 'nav', navId: 'whatup', secondaryId: null, id: 'some/url/chunks', name: 'viewOne', component: MockView1, data: null, requiresExplicitNavPrefix: true},
+ { type: 'tabs', navId: 't1', secondaryId: 'tab-one', id: 'some/more/url/chunks', name: 'viewTwo', component: MockView1, data: null, requiresExplicitNavPrefix: true }
+ ];
+ const result = serializer.serialize(paths);
+ expect(result).toEqual('/nav/whatup/some/url/chunks/tabs/t1/tab-one/some/more/url/chunks');
+ });
+
it('should serialize multiple segments into a url', () => {
let paths: NavSegment[] = [
{ type: 'nav', navId: 'whatup', secondaryId: null, id: 'some/url/chunks', name: 'viewOne', component: MockView1, data: null },
{ type: 'tabs', navId: 't1', secondaryId: 'tab-one', id: 'some/more/url/chunks', name: 'viewTwo', component: MockView1, data: null }
];
const result = serializer.serialize(paths);
- expect(result).toEqual('/nav/whatup/some/url/chunks/tabs/t1/tab-one/some/more/url/chunks');
+ expect(result).toEqual('/some/url/chunks/tab-one/some/more/url/chunks');
});
it('should return default url when given empty list of segments', () => {
@@ -342,194 +296,6 @@ describe('UrlSerializer', () => {
});
- describe('parseUrlParts', () => {
-
- it('should return a single matching segment', () => {
- // arrange
- const navGroups = [];
- const configLinks = [];
- const segmentPieces = ['some', 'part', 'of', 'url'];
-
- const navGroup = { type: 'nav', navId: '1', secondaryId: '', segmentPieces: segmentPieces };
- navGroups.push(navGroup);
-
- const configLink = {
- segmentParts: segmentPieces,
- segmentPartsLen: 4,
- component: {},
- name: 'someName',
- loadChildren: 'someValue',
- };
- configLinks.push(configLink);
-
- // act
- const segments = parseUrlParts(navGroups, configLinks);
-
- // assert
- expect(segments.length).toEqual(1);
- expect(segments[0].id).toEqual(configLink.segmentParts.join('/'));
- expect(segments[0].component).toEqual(configLink.component);
- expect(segments[0].name).toEqual(configLink.name);
- expect(segments[0].loadChildren).toEqual(configLink.loadChildren);
- expect(segments[0].type).toEqual(navGroup.type);
- expect(segments[0].navId).toEqual(navGroup.navId);
- expect(segments[0].secondaryId).toEqual(navGroup.secondaryId);
- });
-
- it('should return single matching segment for tabs', () => {
- // arrange
- const navGroups = [];
- const configLinks = [];
- const segmentPieces = ['some', 'part', 'of', 'url'];
-
- const navGroup = { type: 'tabs', navId: '1', secondaryId: 'tab-one', segmentPieces: segmentPieces };
- navGroups.push(navGroup);
-
- const configLink = {
- segmentParts: ['some', ':someVariable', 'of', ':someVariable2'],
- segmentPartsLen: 4,
- component: {},
- name: 'someName',
- loadChildren: 'someValue',
- };
- configLinks.push(configLink);
-
- // act
- const segments = parseUrlParts(navGroups, configLinks);
-
- // assert
- expect(segments.length).toEqual(1);
- expect(segments[0].id).toEqual(configLink.segmentParts.join('/'));
- expect(segments[0].component).toEqual(configLink.component);
- expect(segments[0].name).toEqual(configLink.name);
- expect(segments[0].loadChildren).toEqual(configLink.loadChildren);
- expect(segments[0].type).toEqual(navGroup.type);
- expect(segments[0].navId).toEqual(navGroup.navId);
- expect(segments[0].secondaryId).toEqual(navGroup.secondaryId);
- expect(segments[0].data.someVariable).toEqual('part');
- expect(segments[0].data.someVariable2).toEqual('url');
- });
-
- it('should return an empty list of segments when there isnt a nav group', () => {
- // arrange
- const configLinks = [];
-
- const configLink = {
- segmentParts: ['some', ':someVariable', 'of', ':someVariable2'],
- segmentPartsLen: 4,
- component: {},
- name: 'someName',
- loadChildren: 'someValue',
- };
- configLinks.push(configLink);
-
- // act
- const segments = parseUrlParts([], configLinks);
-
- // assert
- expect(segments.length).toEqual(0);
- });
-
- it('should return a list of segments', () => {
- // arrange
- const navGroups = [];
- const configLinks = [];
- const segmentPiecesOne = ['some', 'part', 'of', 'url'];
- const navGroup = { type: 'tabs', navId: '1', secondaryId: 'tab-one', segmentPieces: segmentPiecesOne };
- const segmentPiecesTwo = ['userId', '123', 'name', 'Stanley Hudson'];
- const navGroupTwo = { type: 'nav', navId: '2', secondaryId: '', segmentPieces: segmentPiecesTwo };
-
- navGroups.push(navGroup);
- navGroups.push(navGroupTwo);
-
- const configLink = {
- segmentParts: ['some', ':someVariable', 'of', ':someVariable2'],
- segmentPartsLen: 4,
- component: {},
- name: 'someName',
- loadChildren: 'someValue',
- };
- const configLinkTwo = {
- segmentParts: ['userId', ':userId', 'name', ':name'],
- segmentPartsLen: 4,
- component: {},
- name: 'nameTwo',
- loadChildren: 'valueTwo',
- };
- configLinks.push(configLink);
- configLinks.push(configLinkTwo);
-
- // act
- const segments = parseUrlParts(navGroups, configLinks);
-
- // assert
- expect(segments.length).toEqual(2);
- expect(segments[0].id).toEqual(configLink.segmentParts.join('/'));
- expect(segments[0].component).toEqual(configLink.component);
- expect(segments[0].name).toEqual(configLink.name);
- expect(segments[0].loadChildren).toEqual(configLink.loadChildren);
- expect(segments[0].type).toEqual(navGroup.type);
- expect(segments[0].navId).toEqual(navGroup.navId);
- expect(segments[0].secondaryId).toEqual(navGroup.secondaryId);
- expect(segments[0].data.someVariable).toEqual('part');
- expect(segments[0].data.someVariable2).toEqual('url');
-
- expect(segments[1].id).toEqual(configLinkTwo.segmentParts.join('/'));
- expect(segments[1].component).toEqual(configLinkTwo.component);
- expect(segments[1].name).toEqual(configLinkTwo.name);
- expect(segments[1].loadChildren).toEqual(configLinkTwo.loadChildren);
- expect(segments[1].type).toEqual(navGroupTwo.type);
- expect(segments[1].navId).toEqual(navGroupTwo.navId);
- expect(segments[1].secondaryId).toEqual(navGroupTwo.secondaryId);
- expect(segments[1].data.userId).toEqual('123');
- expect(segments[1].data.name).toEqual('Stanley Hudson');
- });
-
- it('should return only matching segments for the nav groups', () => {
- // arrange
- const navGroups = [];
- const configLinks = [];
- const segmentPiecesOne = ['some', 'part', 'of', 'url'];
- const navGroup = { type: 'tabs', navId: '1', secondaryId: 'tab-one', segmentPieces: segmentPiecesOne };
- const segmentPiecesTwo = ['userId', '123', 'name', 'Stanley Hudson'];
- const navGroupTwo = { type: 'nav', navId: '2', secondaryId: '', segmentPieces: segmentPiecesTwo };
-
- navGroups.push(navGroup);
- navGroups.push(navGroupTwo);
-
- const configLink = {
- segmentParts: ['some', ':someVariable', 'of', ':someVariable2'],
- segmentPartsLen: 4,
- component: {},
- name: 'someName',
- loadChildren: 'someValue',
- };
- const configLinkTwo = {
- segmentParts: ['some', 'bogus', 'content', 'wewontmatch'],
- segmentPartsLen: 4,
- component: {},
- name: 'nameTwo',
- loadChildren: 'valueTwo',
- };
- const configLinkThree = {
- segmentParts: ['hi'],
- segmentPartsLen: 1,
- component: {},
- name: 'nameThree',
- loadChildren: 'valueThree',
- };
- configLinks.push(configLink);
- configLinks.push(configLinkTwo);
- configLinks.push(configLinkThree);
-
- const segments = parseUrlParts(navGroups, configLinks);
-
- expect(segments.length).toEqual(1);
- expect(segments[0].name).toEqual(configLink.name);
- });
-
- });
-
describe('isPartMatch', () => {
it('should match if parts are equal', () => {
@@ -764,6 +530,289 @@ describe('UrlSerializer', () => {
});
+ describe('urlToNavGroupStringsTwo', () => {
+ it('should get an array with a single piece url back', () => {
+ const url = 'test';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(1);
+ expect(result[0]).toEqual('test');
+ });
+
+ it('should get an array with multiple pieces back', () => {
+ const url = 'the/dog/jumps/high';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(1);
+ expect(result[0]).toEqual('the/dog/jumps/high');
+ });
+
+ it('should return a single entry with the nav prefix', () => {
+ const url = 'nav/myApp/the/dog/jumps/high';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(1);
+ expect(result[0]).toEqual('nav/myApp/the/dog/jumps/high');
+ });
+
+ it('should return a single entry with the tabs prefix', () => {
+ const url = 'tabs/myApp/tab-one/the/dog/jumps/high';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(1);
+ expect(result[0]).toEqual('tabs/myApp/tab-one/the/dog/jumps/high');
+ });
+
+ it('should return multiple entries with the nav prefix', () => {
+ const url = 'nav/myApp/the/dog/jumps/high/nav/someSubNav/taco/burrito/nav/thirdNav/banana/apple/orange';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(3);
+ expect(result[0]).toEqual('nav/myApp/the/dog/jumps/high');
+ expect(result[1]).toEqual('nav/someSubNav/taco/burrito');
+ expect(result[2]).toEqual('nav/thirdNav/banana/apple/orange');
+ });
+
+ it('should return multiple entries with the tabs prefix', () => {
+ const url = 'tabs/myApp/tab-one/the/dog/jumps/high/tabs/someSubNav/tab-two/taco/burrito/tabs/thirdNav/tab-three/banana/apple/orange';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(3);
+ expect(result[0]).toEqual('tabs/myApp/tab-one/the/dog/jumps/high');
+ expect(result[1]).toEqual('tabs/someSubNav/tab-two/taco/burrito');
+ expect(result[2]).toEqual('tabs/thirdNav/tab-three/banana/apple/orange');
+ });
+
+ it('should handle a nav in the middle of the url', () => {
+ const url = 'the/dog/jumps/high/nav/someSubNav/taco/burrito/nav/thirdNav/banana/apple/orange';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(3);
+ expect(result[0]).toEqual('the/dog/jumps/high');
+ expect(result[1]).toEqual('nav/someSubNav/taco/burrito');
+ expect(result[2]).toEqual('nav/thirdNav/banana/apple/orange');
+ });
+
+ it('should handle a tabs in the middle of the url', () => {
+ const url = 'the/dog/jumps/high/tabs/someSubNav/tab-two/taco/burrito/tabs/thirdNav/tab-three/banana/apple/orange';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(3);
+ expect(result[0]).toEqual('the/dog/jumps/high');
+ expect(result[1]).toEqual('tabs/someSubNav/tab-two/taco/burrito');
+ expect(result[2]).toEqual('tabs/thirdNav/tab-three/banana/apple/orange');
+ });
+
+ it('should handle a mixed url', () => {
+ const url = 'the/dog/jumps/high/tabs/someSubNav/tab-two/taco/burrito/nav/thirdNav/banana/apple/orange';
+ const result = urlToNavGroupStringsTwo(url);
+ expect(result.length).toEqual(3);
+ expect(result[0]).toEqual('the/dog/jumps/high');
+ expect(result[1]).toEqual('tabs/someSubNav/tab-two/taco/burrito');
+ expect(result[2]).toEqual('nav/thirdNav/banana/apple/orange');
+ });
+ });
+
+ describe('convertUrlToDehydratedSegments', () => {
+ it('it should return a vanilla single segment', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three' };
+
+ const links = normalizeLinks([link1, link2, link3]);
+ const url = 'view-two';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(1);
+ expect(segmentPairs[0].segments.length).toEqual(1);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two');
+ });
+
+ it('it should return a data-driven single segment', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three' };
+
+ const links = normalizeLinks([link1, link2, link3]);
+ const url = 'view-two/paramOne/taco/paramTwo/burrito';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(1);
+ expect(segmentPairs[0].segments.length).toEqual(1);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[0].segments[0].data.paramOne).toEqual('taco');
+ expect(segmentPairs[0].segments[0].data.paramTwo).toEqual('burrito');
+ });
+
+ it('it should return a vanilla set of segments', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'view-two/view-one/view-three';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(1);
+ expect(segmentPairs[0].segments.length).toEqual(3);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two');
+ expect(segmentPairs[0].segments[1].id).toEqual('view-one');
+ expect(segmentPairs[0].segments[2].id).toEqual('view-three');
+ });
+
+ it('it should return a data-driven set of segments', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/user/:userId' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three/:itemId' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'view-two/user/fred/view-one/paramOne/taco/paramTwo/burrito/view-three/12345';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(1);
+ expect(segmentPairs[0].segments.length).toEqual(3);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/user/fred');
+ expect(segmentPairs[0].segments[0].data.userId).toEqual('fred');
+ expect(segmentPairs[0].segments[1].id).toEqual('view-one/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[0].segments[1].data.paramOne).toEqual('taco');
+ expect(segmentPairs[0].segments[1].data.paramTwo).toEqual('burrito');
+ expect(segmentPairs[0].segments[2].id).toEqual('view-three/12345');
+ expect(segmentPairs[0].segments[2].data.itemId).toEqual('12345');
+ });
+
+ it('it should return a data-driven set of segments with a root nav prefix', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/user/:userId' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three/:itemId' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'nav/123/view-two/user/fred/view-one/paramOne/taco/paramTwo/burrito/view-three/12345';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(1);
+ expect(segmentPairs[0].segments.length).toEqual(3);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/user/fred');
+ expect(segmentPairs[0].segments[0].data.userId).toEqual('fred');
+ expect(segmentPairs[0].segments[1].id).toEqual('view-one/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[0].segments[1].data.paramOne).toEqual('taco');
+ expect(segmentPairs[0].segments[1].data.paramTwo).toEqual('burrito');
+ expect(segmentPairs[0].segments[2].id).toEqual('view-three/12345');
+ expect(segmentPairs[0].segments[2].data.itemId).toEqual('12345');
+ });
+
+ it('it should return a data-driven set of segments with multiple nav segments in the middle', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/user/:userId' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three/:itemId' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'view-two/user/fred/nav/123/view-one/paramOne/taco/paramTwo/burrito/nav/456/view-three/12345';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(3);
+ expect(segmentPairs[0].segments.length).toEqual(1);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/user/fred');
+ expect(segmentPairs[0].segments[0].data.userId).toEqual('fred');
+
+ expect(segmentPairs[1].segments.length).toEqual(1);
+ expect(segmentPairs[1].segments[0].id).toEqual('view-one/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[1].segments[0].data.paramOne).toEqual('taco');
+ expect(segmentPairs[1].segments[0].data.paramTwo).toEqual('burrito');
+
+ expect(segmentPairs[2].segments.length).toEqual(1);
+ expect(segmentPairs[2].segments[0].id).toEqual('view-three/12345');
+ expect(segmentPairs[2].segments[0].data.itemId).toEqual('12345');
+ });
+
+ it('it should return a data-driven set of segments with a root nav and multiple nav segments in the middle', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/user/:userId' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three/:itemId' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'nav/app/view-two/user/fred/nav/123/view-one/paramOne/taco/paramTwo/burrito/nav/456/view-three/12345';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(3);
+ expect(segmentPairs[0].segments.length).toEqual(1);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/user/fred');
+ expect(segmentPairs[0].segments[0].data.userId).toEqual('fred');
+
+ expect(segmentPairs[1].segments.length).toEqual(1);
+ expect(segmentPairs[1].segments[0].id).toEqual('view-one/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[1].segments[0].data.paramOne).toEqual('taco');
+ expect(segmentPairs[1].segments[0].data.paramTwo).toEqual('burrito');
+
+ expect(segmentPairs[2].segments.length).toEqual(1);
+ expect(segmentPairs[2].segments[0].id).toEqual('view-three/12345');
+ expect(segmentPairs[2].segments[0].data.itemId).toEqual('12345');
+ });
+
+ it('it should return a data-driven set of segments with a root tabs and multiple tabs segments in the middle', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/user/:userId' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three/:itemId' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'tabs/app/tab-two/view-two/user/fred/tabs/123/tab-three/view-one/paramOne/taco/paramTwo/burrito/tabs/456/tab-four/view-three/12345';
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(3);
+ expect(segmentPairs[0].segments.length).toEqual(1);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/user/fred');
+ expect(segmentPairs[0].segments[0].data.userId).toEqual('fred');
+
+ expect(segmentPairs[1].segments.length).toEqual(1);
+ expect(segmentPairs[1].segments[0].id).toEqual('view-one/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[1].segments[0].data.paramOne).toEqual('taco');
+ expect(segmentPairs[1].segments[0].data.paramTwo).toEqual('burrito');
+
+ expect(segmentPairs[2].segments.length).toEqual(1);
+ expect(segmentPairs[2].segments[0].id).toEqual('view-three/12345');
+ expect(segmentPairs[2].segments[0].data.itemId).toEqual('12345');
+ });
+
+ it('should return a data-driven set of segments where the root tabs is implied but the secondary identifier is actually grabed', () => {
+ const link1 = { component: MockView1, name: 'viewone', segment: 'view-one/paramOne/:paramOne/paramTwo/:paramTwo' };
+ const link2 = { component: MockView1, name: 'viewtwo', segment: 'view-two/user/:userId' };
+ const link3 = { component: MockView1, name: 'viewthree', segment: 'view-three/:itemId' };
+ const link4 = { component: MockView1, name: 'viewfour', segment: 'view-four' };
+
+ const links = normalizeLinks([link1, link2, link3, link4]);
+ const url = 'tab-two/view-two/user/fred/tab-three/view-one/paramOne/taco/paramTwo/burrito/tab-four/view-three/12345';
+
+ const segmentPairs = convertUrlToDehydratedSegments(url, links);
+ expect(segmentPairs.length).toEqual(1);
+ expect(segmentPairs[0].segments.length).toEqual(3);
+ expect(segmentPairs[0].segments[0].id).toEqual('view-two/user/fred');
+ expect(segmentPairs[0].segments[0].name).toEqual('viewtwo');
+ expect(segmentPairs[0].segments[0].secondaryId).toEqual('tab-two');
+ expect(segmentPairs[0].segments[0].data.userId).toEqual('fred');
+
+ expect(segmentPairs[0].segments[1].id).toEqual('view-one/paramOne/taco/paramTwo/burrito');
+ expect(segmentPairs[0].segments[1].name).toEqual('viewone');
+ expect(segmentPairs[0].segments[1].data.paramOne).toEqual('taco');
+ expect(segmentPairs[0].segments[1].data.paramTwo).toEqual('burrito');
+ expect(segmentPairs[0].segments[1].secondaryId).toEqual('tab-three');
+
+ expect(segmentPairs[0].segments[2].id).toEqual('view-three/12345');
+ expect(segmentPairs[0].segments[2].name).toEqual('viewthree');
+ expect(segmentPairs[0].segments[2].data.itemId).toEqual('12345');
+ expect(segmentPairs[0].segments[2].secondaryId).toEqual('tab-four');
+ });
+
+ describe('convertUrlToSegments', () => {
+ it('it should return a vanilla single segment', () => {
+
+ const link1 = { component: MockView1, name: 'login-page', segment: 'login-page' };
+ const link2 = { component: MockView1, name: 'settings-page', segment: 'settings-page' };
+ const link3 = { component: MockView1, name: 'details-page', segment: 'details-page' };
+
+ const mockNav = mockNavController();
+ serializer._app.registerRootNav(mockNav);
+
+ const links = normalizeLinks([link1, link2, link3]);
+ const url = 'settings-page';
+
+
+ const segments = convertUrlToSegments(serializer._app, url, links);
+ expect(segments.length).toEqual(1);
+ expect(segments[0].type).toEqual('nav');
+ expect(segments[0].navId).toEqual(mockNav.id);
+ });
+ });
+ });
+
var serializer: UrlSerializer;
beforeEach(() => {
@@ -778,5 +827,5 @@ class NotFound {}
function mockSerializer(navLinks?: NavLink[]) {
let deepLinkConfig = mockDeepLinkConfig(navLinks);
- return new UrlSerializer(deepLinkConfig);
+ return new UrlSerializer(mockApp(), deepLinkConfig);
}
diff --git a/src/navigation/url-serializer.ts b/src/navigation/url-serializer.ts
index 4addf2617a..e5dd9febfa 100644
--- a/src/navigation/url-serializer.ts
+++ b/src/navigation/url-serializer.ts
@@ -1,7 +1,8 @@
import { OpaqueToken } from '@angular/core';
+import { App } from '../components/app/app';
import { NavigationContainer } from './navigation-container';
-import { DeepLinkConfig, NavLink, NavSegment } from './nav-util';
+import { DeepLinkConfig, DehydratedSegment, DehydratedSegmentPair, NavGroup, NavLink, NavSegment } from './nav-util';
import { isArray, isBlank, isPresent } from '../util/util';
@@ -11,7 +12,7 @@ import { isArray, isBlank, isPresent } from '../util/util';
export class UrlSerializer {
links: NavLink[];
- constructor(config: DeepLinkConfig) {
+ constructor(public _app: App, config: DeepLinkConfig) {
if (config && isArray(config.links)) {
this.links = normalizeLinks(config.links);
@@ -31,9 +32,7 @@ export class UrlSerializer {
// trim off data after ? and #
browserUrl = browserUrl.split('?')[0].split('#')[0];
- const navGroupStrings = urlToNavGroupStrings(browserUrl);
- const navGroups = navGroupStringtoObjects(navGroupStrings);
- return parseUrlParts(navGroups, this.links);
+ return convertUrlToSegments(this._app, browserUrl, this.links);
}
@@ -41,7 +40,7 @@ export class UrlSerializer {
createSegmentFromName(navContainer: NavigationContainer, nameOrComponent: any): NavSegment {
const configLink = this.getLinkFromName(nameOrComponent);
if (configLink) {
- return this._createSegment({ navId: navContainer.id, secondaryId: navContainer.getSecondaryIdentifier(), type: 'tabs'}, configLink, null);
+ return this._createSegment(this._app, navContainer, configLink, null);
}
return null;
}
@@ -63,9 +62,16 @@ export class UrlSerializer {
}
const sections = segments.map(segment => {
if (segment.type === 'tabs') {
- return `/${segment.type}/${segment.navId}/${segment.secondaryId}/${segment.id}`;
+ if (segment.requiresExplicitNavPrefix) {
+ return `/${segment.type}/${segment.navId}/${segment.secondaryId}/${segment.id}`;
+ }
+ return `/${segment.secondaryId}/${segment.id}`;
}
- return `/${segment.type}/${segment.navId}/${segment.id}`;
+ // it's a nav
+ if (segment.requiresExplicitNavPrefix) {
+ return `/${segment.type}/${segment.navId}/${segment.id}`;
+ }
+ return `/${segment.id}`;
});
return sections.join('');
}
@@ -73,18 +79,20 @@ export class UrlSerializer {
/**
* Serializes a component and its data into a NavSegment.
*/
- serializeComponent(navGroup: NavGroup, component: any, data: any): NavSegment {
+ serializeComponent(navContainer: NavigationContainer, component: any, data: any): NavSegment {
if (component) {
const link = findLinkByComponentData(this.links, component, data);
if (link) {
- return this._createSegment(navGroup, link, data);
+ return this._createSegment(this._app, navContainer, link, data);
}
}
return null;
}
- /** @internal */
- _createSegment(navGroup: NavGroup, configLink: NavLink, data: any): NavSegment {
+ /**
+ * @internal
+ */
+ _createSegment(app: App, navContainer: NavigationContainer, configLink: NavLink, data: any): NavSegment {
let urlParts = configLink.segmentParts;
if (isPresent(data)) {
@@ -110,6 +118,14 @@ export class UrlSerializer {
}
}
+ let requiresExplicitPrefix = true;
+ if (navContainer.parent) {
+ requiresExplicitPrefix = navContainer.parent && navContainer.parent.getAllChildNavs().length > 1;
+ } else {
+ // if it's a root nav, and there are multiple root navs, we need an explicit prefix
+ requiresExplicitPrefix = app.getRootNavById(navContainer.id) && app.getRootNavs().length > 1;
+ }
+
return {
id: urlParts.join('/'),
name: configLink.name,
@@ -117,9 +133,10 @@ export class UrlSerializer {
loadChildren: configLink.loadChildren,
data: data,
defaultHistory: configLink.defaultHistory,
- navId: navGroup.navId,
- type: navGroup.type,
- secondaryId: navGroup.secondaryId
+ navId: navContainer.name || navContainer.id,
+ type: navContainer.getType(),
+ secondaryId: navContainer.getSecondaryIdentifier(),
+ requiresExplicitNavPrefix: requiresExplicitPrefix
};
}
}
@@ -141,39 +158,6 @@ export function formatUrlPart(name: string): string {
return encodeURIComponent(name);
}
-export const parseUrlParts = (navGroups: NavGroup[], configLinks: NavLink[]): NavSegment[] => {
- const segments: NavSegment[] = [];
- for (const link of configLinks) {
- for (const navGroup of navGroups) {
- if (link.segmentPartsLen === navGroup.segmentPieces.length) {
- // check if the segment pieces are a match
- let allSegmentsMatch = true;
- for (let i = 0; i < navGroup.segmentPieces.length; i++) {
- if (!isPartMatch(navGroup.segmentPieces[i], link.segmentParts[i])) {
- allSegmentsMatch = false;
- break;
- }
- }
- // sweet, we found a match!
- if (allSegmentsMatch) {
- segments.push({
- id: link.segmentParts.join('/'),
- name: link.name,
- component: link.component,
- loadChildren: link.loadChildren,
- data: createMatchedData(navGroup.segmentPieces, link),
- defaultHistory: link.defaultHistory,
- navId: navGroup.navId,
- type: navGroup.type,
- secondaryId: navGroup.secondaryId
- });
- }
- }
- }
- }
- return segments;
-};
-
export const isPartMatch = (urlPart: string, configLinkPart: string) => {
if (isPresent(urlPart) && isPresent(configLinkPart)) {
if (configLinkPart.charAt(0) === ':') {
@@ -300,8 +284,8 @@ const URL_REPLACE_REG = /\s+|\?|\!|\$|\,|\.|\+|\"|\'|\*|\^|\||\/|\\|\[|\]|#|%|`|
*/
export const DeepLinkConfigToken = new OpaqueToken('USERLINKS');
-export function setupUrlSerializer(userDeepLinkConfig: any): UrlSerializer {
- return new UrlSerializer(userDeepLinkConfig);
+export function setupUrlSerializer(app: App, userDeepLinkConfig: any): UrlSerializer {
+ return new UrlSerializer(app, userDeepLinkConfig);
}
export function urlToNavGroupStrings(url: string): string[] {
@@ -334,20 +318,227 @@ export function navGroupStringtoObjects(navGroupStrings: string[]): NavGroup[] {
secondaryId: null,
segmentPieces: sections.splice(2)
};
+ } else if (sections[0] === 'tabs') {
+ return {
+ type: 'tabs',
+ navId: sections[1],
+ niceId: sections[1],
+ secondaryId: sections[2],
+ segmentPieces: sections.splice(3)
+ };
}
return {
- type: 'tabs',
- navId: sections[1],
- niceId: sections[1],
- secondaryId: sections[2],
- segmentPieces: sections.splice(3)
+ type: null,
+ navId: null,
+ niceId: null,
+ secondaryId: null,
+ segmentPieces: sections
};
});
}
-export interface NavGroup {
- type: string;
- navId: string;
- secondaryId: string;
- segmentPieces?: string[];
+export function urlToNavGroupStringsTwo(url: string) {
+ const tokens = url.split('/');
+ const keywordIndexes = [];
+ for (let i = 0; i < tokens.length; i++) {
+ if (i !== 0 && (tokens[i] === 'nav' || tokens[i] === 'tabs')) {
+ keywordIndexes.push(i);
+ }
+ }
+ // append the last index + 1 to the list no matter what
+ keywordIndexes.push(tokens.length);
+ const groupings: string[] = [];
+ let activeKeywordIndex = 0;
+ let tmpArray: string[] = [];
+ for (let i = 0; i < tokens.length; i++) {
+ if (i >= keywordIndexes[activeKeywordIndex]) {
+ groupings.push(tmpArray.join('/'));
+ tmpArray = [];
+ activeKeywordIndex++;
+ }
+ tmpArray.push(tokens[i]);
+ }
+ // okay, after the loop we've gotta push one more time just to be safe
+ groupings.push(tmpArray.join('/'));
+ return groupings;
+}
+
+export function convertUrlToSegments(app: App, url: string, navLinks: NavLink[]): NavSegment[] {
+ const pairs = convertUrlToDehydratedSegments(url, navLinks);
+ return hydrateSegmentsWithNav(app, pairs);
+}
+
+export function convertUrlToDehydratedSegments(url: string, navLinks: NavLink[]): DehydratedSegmentPair[] {
+ const navGroupStrings = urlToNavGroupStringsTwo(url);
+ const navGroups = navGroupStringtoObjects(navGroupStrings);
+ return getSegmentsFromNavGroups(navGroups, navLinks);
+}
+
+export function hydrateSegmentsWithNav(app: App, dehydratedSegmentPairs: DehydratedSegmentPair[]) {
+ const segments: NavSegment[] = [];
+ for (let i = 0; i < dehydratedSegmentPairs.length; i++) {
+ let navs = getNavFromNavGroup(dehydratedSegmentPairs[i].navGroup, app);
+ // okay, cool, let's walk through the segments and hydrate them
+ for (const dehydratedSegment of dehydratedSegmentPairs[i].segments) {
+ if (navs.length === 1) {
+ segments.push(hydrateSegment(dehydratedSegment, navs[0]));
+ } else if (navs.length > 1 || navs.length <= 0) {
+ break;
+ // throw new Error('Invalid URL - could not determine which nav to use');
+ }
+ navs = navs[0].getActiveChildNavs();
+ }
+ }
+ return segments;
+}
+
+export function getNavFromNavGroup(navGroup: NavGroup, app: App): NavigationContainer[] {
+ if (navGroup.navId) {
+ const rootNav = app.getNavByIdOrName(navGroup.navId);
+ if (rootNav) {
+ return [rootNav];
+ }
+ return [];
+ }
+ // we don't know what nav to use, so just use the root nav.
+ // if there is more than one root nav, throw an error
+ return app.getRootNavs();
+}
+
+/*
+ * Let's face the facts: Getting a dehydrated segment from the url is really hard
+ * because we need to do a ton of crazy looping
+ * the are chunks of a url that are totally irrelevant at this stage, such as the secondary identifier
+ * stating which tab is selected, etc.
+ * but is necessary. Ultimately, let's say that we have a url that looks like this
+ * /the/dog/jumps/high
+ * We need to check for a segment match this looping pattern
+ * the
+ * the dog
+ * the dog jumps
+ * the dog jumps high
+ * dog
+ * dog jumps
+ * dog jumps high
+ * jumps
+ * jumps high
+ * high
+ **/
+export function getSegmentsFromNavGroups(navGroups: NavGroup[], navLinks: NavLink[]) {
+ const pairs: DehydratedSegmentPair[] = [];
+ for (const navGroup of navGroups) {
+ const segments: DehydratedSegment[] = [];
+ // loop over each nav segment piece
+ for (let i = 0; i < navGroup.segmentPieces.length; i++) {
+ // do a second loop the length - i times
+ let created = false;
+ for (let j = 0; j < navGroup.segmentPieces.length - i; j++) {
+ // grab the subsection of the url to match the pattern above
+ const endIndex = 1 + i + j;
+ const subsetOfUrl = navGroup.segmentPieces.slice(i, endIndex);
+ for (const navLink of navLinks) {
+ const segment = getSegmentsFromUrlPieces(subsetOfUrl, navLink);
+ if (segment) {
+ created = true;
+ // sweet, we found a segment
+ segments.push(segment);
+ // now we want to null out the url subsection in the segmentPieces
+ for (let k = i; k < endIndex; k++) {
+ navGroup.segmentPieces[k] = null;
+ }
+ break;
+ }
+ }
+ }
+ if (!created && navGroup.segmentPieces[i]) {
+ // this is very likely a tab's secondary identifier
+ segments.push({
+ id: null,
+ name: null,
+ secondaryId: navGroup.segmentPieces[i],
+ component: null,
+ loadChildren: null,
+ data: null,
+ defaultHistory: null
+ });
+ }
+ }
+
+ segments.forEach(segment => console.log('segment: ', segment));
+
+ // okay, this is the lazy persons approach here.
+ // so here's the deal! Right now if section of the url is not a part of a segment
+ // it is almost certainly the secondaryId for a tabs component
+ // basically, knowing the segment for the `tab` itself is good, but we also need to know
+ // which tab is selected, so we have an identifer in the url that is associated with the tabs component
+ // telling us which tab is selected. With that in mind, we are going to go through and find the segments with only secondary identifiers,
+ // and simply add the secondaryId to the next segment, and then remove the empty segment from the list
+ for (let i = 0; i < segments.length; i++) {
+ if (segments[i].secondaryId && !segments[i].id && ((i + 1) <= segments.length - 1)) {
+ segments[i + 1].secondaryId = segments[i].secondaryId;
+ segments[i] = null;
+ }
+ }
+ const cleanedSegments = segments.filter(segment => !!segment);
+ // if the nav group has a secondary id, make sure the first segment also has it set
+ if (navGroup.secondaryId && segments.length) {
+ cleanedSegments[0].secondaryId = navGroup.secondaryId;
+ }
+
+ pairs.push({
+ navGroup: navGroup,
+ segments: cleanedSegments
+ });
+ }
+ return pairs;
+}
+
+export function getSegmentsFromUrlPieces(urlSections: string[], navLink: NavLink): DehydratedSegment {
+ if (navLink.segmentPartsLen !== urlSections.length) {
+ return null;
+ }
+ for (let i = 0; i < urlSections.length; i++) {
+ if (!isPartMatch(urlSections[i], navLink.segmentParts[i])) {
+ // just return an empty array if the part doesn't match
+ return null;
+ }
+ }
+ return {
+ id: urlSections.join('/'),
+ name: navLink.name,
+ component: navLink.component,
+ loadChildren: navLink.loadChildren,
+ data: createMatchedData(urlSections, navLink),
+ defaultHistory: navLink.defaultHistory
+ };
+}
+
+export function hydrateSegment(segment: DehydratedSegment, nav: NavigationContainer) {
+ const hydratedSegment = Object.assign({}, segment) as NavSegment;
+ hydratedSegment.type = nav.getType();
+ hydratedSegment.navId = nav.id;
+ // secondaryId is set on an empty dehydrated segment in the case of tabs to identify which tab is selected
+ hydratedSegment.secondaryId = segment.secondaryId;
+ return hydratedSegment;
+}
+
+export function getNonHydratedSegmentIfLinkAndUrlMatch(urlChunks: string[], navLink: NavLink): DehydratedSegment {
+ let allSegmentsMatch = true;
+ for (let i = 0; i < urlChunks.length; i++) {
+ if (!isPartMatch(urlChunks[i], navLink.segmentParts[i])) {
+ allSegmentsMatch = false;
+ break;
+ }
+ }
+ if (allSegmentsMatch) {
+ return {
+ id: navLink.segmentParts.join('/'),
+ name: navLink.name,
+ component: navLink.component,
+ loadChildren: navLink.loadChildren,
+ data: createMatchedData(urlChunks, navLink),
+ defaultHistory: navLink.defaultHistory
+ };
+ }
+ return null;
}
diff --git a/src/util/mock-providers.ts b/src/util/mock-providers.ts
index 8e56f3dcf4..de863aea54 100644
--- a/src/util/mock-providers.ts
+++ b/src/util/mock-providers.ts
@@ -221,7 +221,7 @@ export function mockContent(): Content {
}
export function mockZone(): NgZone {
- return new NgZone(false);
+ return new NgZone({enableLongStackTrace: false});
}
export function mockChangeDetectorRef(): ChangeDetectorRef {
@@ -390,8 +390,8 @@ export function mockComponentRef(): ComponentRef {
}
export function mockDeepLinker(linkConfig: DeepLinkConfig = null, app?: App) {
- let serializer = new UrlSerializer(linkConfig);
-
+ app = app || mockApp(mockConfig(), mockPlatform());
+ let serializer = new UrlSerializer(app, linkConfig);
let location = mockLocation();
return new DeepLinker(app || mockApp(), serializer, location, null, null);
@@ -449,7 +449,7 @@ export function mockOverlayPortal(app: App, config: Config, plt: MockPlatform):
let renderer = mockRenderer();
let componentFactoryResolver: any = null;
let gestureCtrl = new GestureController(app);
- let serializer = new UrlSerializer(null);
+ let serializer = new UrlSerializer(app, null);
let location = mockLocation();
let deepLinker = new DeepLinker(app, serializer, location, null, null);