mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
584afd040f | ||
|
|
de0f9d5f28 | ||
|
|
4596dbe5c0 | ||
|
|
400aa549d4 | ||
|
|
add0c4ecfe | ||
|
|
519d657e6e | ||
|
|
a8ceee467b | ||
|
|
97f9522110 | ||
|
|
961bfc3ebb | ||
|
|
5b9fe5e81a | ||
|
|
c4e7552b56 | ||
|
|
cec718c6c7 | ||
|
|
ab511c4744 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,3 +1,15 @@
|
||||
<a name="3.5.3"></a>
|
||||
## [3.5.3](https://github.com/ionic-team/ionic/compare/v3.5.2...v3.5.3) (2017-07-14)
|
||||
|
||||
## Upgrade Instructions
|
||||
`ionic-angular@3.5.3` is a drop-in replacement for `3.5.2`. To install it, simply run `npm install ionic-angular@3.5.3 --save --save-exact`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **app:** restore getActiveNav api ([2d49e10](https://github.com/ionic-team/ionic/commit/2d49e10))
|
||||
|
||||
|
||||
|
||||
<a name="3.5.2"></a>
|
||||
## [3.5.2](https://github.com/ionic-team/ionic/compare/v3.5.1...v3.5.2) (2017-07-13)
|
||||
|
||||
|
||||
68
package-lock.json
generated
68
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ionic2",
|
||||
"version": "3.5.0",
|
||||
"version": "3.5.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -249,7 +249,7 @@
|
||||
"integrity": "sha1-Co7eMJzg69ui8FNOV0aSUR3XHrY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/del": {
|
||||
@@ -267,7 +267,7 @@
|
||||
"integrity": "sha512-b7mVHoURu1xaP/V6xw1sYwyv9V0EZ7euyi+sdnbnTZxEkAh4/hzPsI6Eflq+ZzHQ/Tgl7l16Jz+0oz8F46MLnA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
@@ -276,7 +276,7 @@
|
||||
"integrity": "sha1-GV8RvNmhuX2eQSxrZombVFRxofc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/glob": {
|
||||
@@ -286,7 +286,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/minimatch": "2.0.29",
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/gulp": {
|
||||
@@ -295,7 +295,7 @@
|
||||
"integrity": "sha1-g8WcaBzCM9Hsf4LSaVVVZvoTMVY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81",
|
||||
"@types/node": "6.0.83",
|
||||
"@types/orchestrator": "0.3.0",
|
||||
"@types/vinyl": "2.0.0"
|
||||
}
|
||||
@@ -306,7 +306,7 @@
|
||||
"integrity": "sha1-P+Cy9g/QR+/1gijqE5TTXV30a3E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/hammerjs": {
|
||||
@@ -333,7 +333,7 @@
|
||||
"integrity": "sha1-y1UumCbKPAjZ5lSd6ezY8VQX8G0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
@@ -355,9 +355,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "6.0.81",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.81.tgz",
|
||||
"integrity": "sha512-KdtXOH8l9O2wwOOX+swjbFx+YW/RJFfI14o6S50+Zy79FK1WFGkzFdDsiuNjrG5L6FaBSKpKzSpWgTvXurbbYg==",
|
||||
"version": "6.0.83",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.83.tgz",
|
||||
"integrity": "sha512-Q92+tkWnX7nmT0ZG+/wFxzJr+idr00T12MgsY3p0sZIu8nfvYF8i5pbY3BVZw6ad6yS2MLF71sfMr+ySatc2Gw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/orchestrator": {
|
||||
@@ -366,7 +366,7 @@
|
||||
"integrity": "sha1-v4ShaZyTMNT+ic2BJj6PwJ+zKXg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81",
|
||||
"@types/node": "6.0.83",
|
||||
"@types/q": "0.0.35"
|
||||
}
|
||||
},
|
||||
@@ -392,7 +392,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/gulp": "3.8.32",
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/selenium-webdriver": {
|
||||
@@ -429,7 +429,7 @@
|
||||
"integrity": "sha1-abK91NNhjIoSY7i6p2ObLDDtgn4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"@types/vinyl": {
|
||||
@@ -438,7 +438,7 @@
|
||||
"integrity": "sha1-/SE79/QTbd4h/hiVUAsSwYb4wmg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81"
|
||||
"@types/node": "6.0.83"
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
@@ -842,7 +842,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "2.1.5",
|
||||
"caniuse-lite": "1.0.30000700",
|
||||
"caniuse-lite": "1.0.30000701",
|
||||
"normalize-range": "0.1.2",
|
||||
"num2fraction": "1.2.2",
|
||||
"postcss": "6.0.6",
|
||||
@@ -2198,7 +2198,7 @@
|
||||
"integrity": "sha1-6IJVDfPRzW1IHBo+ADjyuvE6RxE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "1.0.30000700",
|
||||
"caniuse-lite": "1.0.30000701",
|
||||
"electron-to-chromium": "1.3.15"
|
||||
}
|
||||
},
|
||||
@@ -2311,15 +2311,15 @@
|
||||
}
|
||||
},
|
||||
"caniuse-db": {
|
||||
"version": "1.0.30000700",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000700.tgz",
|
||||
"integrity": "sha1-l8/Eg4Ze6oV33Ho2dJKbmr9VMJU=",
|
||||
"version": "1.0.30000701",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000701.tgz",
|
||||
"integrity": "sha1-LjKwaZO/Pb2QtD2T8E4m0Rr93Lo=",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30000700",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000700.tgz",
|
||||
"integrity": "sha1-YISHHsdcb6YjJ96XYiUU+V2dsmo=",
|
||||
"version": "1.0.30000701",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000701.tgz",
|
||||
"integrity": "sha1-nWc89rdNyz1cIdITF2sBGsakW6o=",
|
||||
"dev": true
|
||||
},
|
||||
"canonical-path": {
|
||||
@@ -2341,9 +2341,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"catharsis": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.8.tgz",
|
||||
"integrity": "sha1-aTR59DqsVJ2Aa9c+kkzQ2USVGgY=",
|
||||
"version": "0.8.9",
|
||||
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz",
|
||||
"integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"underscore-contrib": "0.3.0"
|
||||
@@ -3489,7 +3489,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"canonical-path": "0.0.2",
|
||||
"catharsis": "0.8.8",
|
||||
"catharsis": "0.8.9",
|
||||
"change-case": "3.0.0",
|
||||
"dgeni": "0.4.9",
|
||||
"espree": "2.2.5",
|
||||
@@ -6219,7 +6219,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "1.7.7",
|
||||
"caniuse-db": "1.0.30000700",
|
||||
"caniuse-db": "1.0.30000701",
|
||||
"normalize-range": "0.1.2",
|
||||
"num2fraction": "1.2.2",
|
||||
"postcss": "5.2.17",
|
||||
@@ -6232,7 +6232,7 @@
|
||||
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-db": "1.0.30000700",
|
||||
"caniuse-db": "1.0.30000701",
|
||||
"electron-to-chromium": "1.3.15"
|
||||
}
|
||||
}
|
||||
@@ -7711,7 +7711,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camel-case": "3.0.0",
|
||||
"clean-css": "4.1.6",
|
||||
"clean-css": "4.1.7",
|
||||
"commander": "2.9.0",
|
||||
"he": "1.1.1",
|
||||
"ncname": "1.0.0",
|
||||
@@ -7721,9 +7721,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"clean-css": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.6.tgz",
|
||||
"integrity": "sha1-Wke+tSaZTLT3vzYYilXtO0VSjws=",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.7.tgz",
|
||||
"integrity": "sha1-ua6k+FZ5iJzz6ui0A0nsTr390DI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-map": "0.5.6"
|
||||
@@ -10936,7 +10936,7 @@
|
||||
"integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "6.0.81",
|
||||
"@types/node": "6.0.83",
|
||||
"@types/q": "0.0.32",
|
||||
"@types/selenium-webdriver": "2.53.42",
|
||||
"blocking-proxy": "0.0.5",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "ionic2",
|
||||
"version": "3.5.2",
|
||||
"version": "3.5.3",
|
||||
"description": "A powerful framework for building mobile and progressive web apps with JavaScript and Angular",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -148,4 +148,4 @@
|
||||
"pre-push#master": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,14 @@ module.exports = function jekyll(renderDocsProcessor) {
|
||||
if (docs[i].href) {
|
||||
docs[i].href = doc.href.replace('content/', '');
|
||||
}
|
||||
if (docs[i].description) {
|
||||
docs[i].description = docs[i].description.replace(/(\#\#\#).+/g, (section) => {
|
||||
const title = section.replace(/^(\#+\s?)/, '');
|
||||
const segment = title.replace(/[^a-zA-Z0-9]+/g, '-').toLowerCase();
|
||||
|
||||
return `\n<h3><a class="anchor" name="${segment}" href="#${segment}">${title}</a></h3>\n`;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
docs.push({
|
||||
|
||||
23
scripts/docs/templates/common.template.html
vendored
23
scripts/docs/templates/common.template.html
vendored
@@ -242,7 +242,7 @@ Improve this doc
|
||||
|
||||
<!-- @usage tag -->
|
||||
<@ if doc.usage @>
|
||||
<h2><a class="anchor" name="usage" href="#usage"></a>Usage</h2>
|
||||
<h2><a class="anchor" name="usage" href="#usage">Usage</a></h2>
|
||||
<@ block usage @>
|
||||
<$ doc.usage | marked $>
|
||||
<@ endblock @>
|
||||
@@ -250,7 +250,7 @@ Improve this doc
|
||||
|
||||
<!-- @property tags -->
|
||||
<@ if doc.properties @>
|
||||
<h2><a class="anchor" name="attributes" href="#attributes"></a>Attributes:</h2>
|
||||
<h2><a class="anchor" name="attributes" href="#attributes">Attributes</a></h2>
|
||||
<table class="table" style="margin:0;">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -293,10 +293,10 @@ Improve this doc
|
||||
|
||||
|
||||
<@- if doc.statics.length -@>
|
||||
<h2><a class="anchor" name="static-members" href="#static-members"></a>Static Members</h2>
|
||||
<h2><a class="anchor" name="static-members" href="#static-members">Static Members</a></h2>
|
||||
<@- for method in doc.statics @><@ if not method.internal @>
|
||||
<div id="<$ method.name $>"></div>
|
||||
<h3><a class="anchor" name="<$ method.name $>" href="#<$ method.name $>"></a><$ functionSyntax(method) $></h3>
|
||||
<h3><a class="anchor" name="<$ method.name $>" href="#<$ method.name $>"><$ functionSyntax(method) $></a></h3>
|
||||
|
||||
<$ method.description $>
|
||||
|
||||
@@ -327,14 +327,15 @@ Improve this doc
|
||||
<!-- instance methods on the class -->
|
||||
<@- if doc.members and doc.members.length @>
|
||||
|
||||
<h2><a class="anchor" name="instance-members" href="#instance-members"></a>Instance Members</h2>
|
||||
<h2><a class="anchor" name="instance-members" href="#instance-members">Instance Members</a></h2>
|
||||
<@- for method in doc.members @>
|
||||
|
||||
<div id="<$ method.name $>"></div>
|
||||
|
||||
<h3>
|
||||
<a class="anchor" name="<$ method.name $>" href="#<$ method.name $>"></a>
|
||||
<a class="anchor" name="<$ method.name $>" href="#<$ method.name $>">
|
||||
<$ functionSyntax(method) $>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<$ method.description $>
|
||||
@@ -366,26 +367,26 @@ Improve this doc
|
||||
|
||||
<@- if doc.inputs and doc.inputs.length @>
|
||||
<!-- input methods on the class -->
|
||||
<h2><a class="anchor" name="input-properties" href="#input-properties"></a>Input Properties</h2>
|
||||
<h2><a class="anchor" name="input-properties" href="#input-properties">Input Properties</a></h2>
|
||||
<$ inputTable(doc.inputs) $>
|
||||
<@- endif -@>
|
||||
|
||||
<@- if doc.outputs and doc.outputs.length @>
|
||||
<!-- output events on the class -->
|
||||
<h2><a class="anchor" name="output-events" href="#output-events"></a>Output Events</h2>
|
||||
<h2><a class="anchor" name="output-events" href="#output-events">Output Events</a></h2>
|
||||
<$ outputTable(doc.outputs) $>
|
||||
<@- endif -@>
|
||||
|
||||
|
||||
<@ block advanced @>
|
||||
<@- if doc.advanced -@>
|
||||
<h2><a class="anchor" name="advanced" href="#advanced"></a>Advanced</h2>
|
||||
<h2><a class="anchor" name="advanced" href="#advanced">Advanced</a></h2>
|
||||
<$ doc.advanced | marked $>
|
||||
<@- endif -@>
|
||||
<@ endblock @>
|
||||
|
||||
<@ if doc.sassVariables @>
|
||||
<h2 id="sass-variable-header"><a class="anchor" name="sass-variables" href="#sass-variables"></a>Sass Variables</h2>
|
||||
<h2 id="sass-variable-header"><a class="anchor" name="sass-variables" href="#sass-variables">Sass Variables</a></h2>
|
||||
<$ sassTable(doc.sassVariables) $>
|
||||
<@ endif @>
|
||||
|
||||
@@ -393,7 +394,7 @@ Improve this doc
|
||||
<!-- related link -->
|
||||
<@- if doc.see @>
|
||||
|
||||
<h2><a class="anchor" name="related" href="#related"></a>Related</h2>
|
||||
<h2><a class="anchor" name="related" href="#related">Related</a></h2>
|
||||
<@ for s in doc.see @>
|
||||
<$ s | safe $> <@- if not loop.last @>,<@- endif -@>
|
||||
<@- endfor -@>
|
||||
|
||||
@@ -46,7 +46,7 @@ export function config(config) {
|
||||
'dist/ionic-angular/umd/**/!(*spec).js': ['coverage'],
|
||||
'dist/ionic-angular/**/*.js': ['sourcemap']
|
||||
},
|
||||
reporters: ['dots', 'coverage', 'spec'],
|
||||
reporters: ['coverage', 'spec'],
|
||||
specReporter: {
|
||||
maxLogLines: 5, // limit number of lines logged per test
|
||||
suppressErrorSummary: true, // do not print error summary
|
||||
|
||||
@@ -9,8 +9,5 @@ import { $CLASSNAME } from './$FILENAME';
|
||||
imports: [
|
||||
IonicPageModule.forChild($CLASSNAME),
|
||||
],
|
||||
exports: [
|
||||
$CLASSNAME
|
||||
]
|
||||
})
|
||||
export class $CLASSNAMEModule {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
$IMPORTSTATEMENT
|
||||
|
||||
/**
|
||||
* Generated class for the $CLASSNAME page.
|
||||
@@ -7,7 +7,7 @@ import { NavController, NavParams } from 'ionic-angular';
|
||||
* See http://ionicframework.com/docs/components/#navigation for more info
|
||||
* on Ionic pages and navigation.
|
||||
*/
|
||||
|
||||
$IONICPAGE
|
||||
@Component({
|
||||
selector: 'page-$FILENAME',
|
||||
templateUrl: '$FILENAME.html',
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||
* Angular Pipes.
|
||||
*/
|
||||
@Pipe({
|
||||
name: '$FILENAME',
|
||||
name: '$PIPENAME',
|
||||
})
|
||||
export class $CLASSNAME implements PipeTransform {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
$TABS_IMPORTSTATEMENT
|
||||
|
||||
/**
|
||||
* Generated class for the $CLASSNAME tabs.
|
||||
@@ -7,6 +7,7 @@ import { NavController } from 'ionic-angular';
|
||||
* See https://angular.io/docs/ts/latest/guide/dependency-injection.html for
|
||||
* more info on providers and Angular DI.
|
||||
*/
|
||||
$IONICPAGE
|
||||
@Component({
|
||||
selector: 'page-$FILENAME',
|
||||
templateUrl: '$FILENAME.html'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -87,14 +87,14 @@ $list-md-header-color: #757575 !default;
|
||||
}
|
||||
|
||||
.list-md ion-item-options .button {
|
||||
@include margin(1px, 0);
|
||||
@include margin(0);
|
||||
@include border-radius(0);
|
||||
|
||||
display: inline-flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
height: calc(100% - 2px);
|
||||
height: 100%;
|
||||
|
||||
border: 0;
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ export class Nav extends NavControllerBase implements AfterViewInit, RootNode, I
|
||||
|
||||
if (segment && (segment.component || segment.loadChildren)) {
|
||||
return this._linker.initViews(segment).then(views => {
|
||||
this.setPages(views, null, null);
|
||||
return this.setPages(views, null, null);
|
||||
});
|
||||
} else if (this._root) {
|
||||
// no segment match, so use the root property but don't set the url I guess
|
||||
@@ -133,6 +133,7 @@ export class Nav extends NavControllerBase implements AfterViewInit, RootNode, I
|
||||
get root(): any {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
set root(page: any) {
|
||||
this._root = page;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Component } from '@angular/core';
|
||||
@Component({
|
||||
template: `
|
||||
<ion-split-pane>
|
||||
<ion-nav [root]="rootOne"></ion-nav>
|
||||
<ion-nav [root]="rootTwo" main #content></ion-nav>
|
||||
<ion-nav [root]="rootOne" name="left"></ion-nav>
|
||||
<ion-nav [root]="rootTwo" main #content name="right"></ion-nav>
|
||||
|
||||
</ion-split-pane>
|
||||
`
|
||||
|
||||
@@ -19,7 +19,6 @@ import { IonicPage, NavController, NavParams } from '../../../../../../..';
|
||||
<div>
|
||||
Name: {{paramTwo}}
|
||||
</div>
|
||||
<button ion-button (click)="goToNext()">Next</button>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
|
||||
@@ -14,10 +14,10 @@ import { IonicPage, NavController, NavParams } from '../../../../../../..';
|
||||
<ion-content>
|
||||
Tabs 1 Tab 2 Page 3
|
||||
<div>
|
||||
Param One: {{userId}}
|
||||
Param One: {{paramOne}}
|
||||
</div>
|
||||
<div>
|
||||
Param Two: {{name}}
|
||||
Param Two: {{paramTwo}}
|
||||
</div>
|
||||
</ion-content>
|
||||
`
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `<ion-nav [root]="root"></ion-nav>`
|
||||
})
|
||||
export class AppComponent {
|
||||
root = 'LoginPage';
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
@@ -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 { }
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { IonicPage, NavController, } from '../../../../../..';
|
||||
|
||||
@IonicPage()
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>My Super Cool, multi-pane App</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col style="height: 1000px; background-color: blue">
|
||||
<ion-nav root="FirstPage" name="left"></ion-nav>
|
||||
</ion-col>
|
||||
<ion-col style="height: 1000px; background-color: green">
|
||||
<ion-nav root="FourthPage" name="right"></ion-nav>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class LandingPage {
|
||||
constructor(public nav: NavController) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Page One</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<h2>Page One</h2>
|
||||
<button ion-button (click)="goToPageTwo()">Go to Page Two</button>
|
||||
</ion-content>
|
||||
@@ -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 { }
|
||||
@@ -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'});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Page Two</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<h2>Page Two</h2>
|
||||
<div>
|
||||
User ID: {{userId}}
|
||||
</div>
|
||||
<div>
|
||||
Name {{name}}
|
||||
</div>
|
||||
<button ion-button (click)="goToNextPage()">Go to Next</button>
|
||||
</ion-content>
|
||||
@@ -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 { }
|
||||
@@ -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'});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Page Three</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
Page Three
|
||||
<div>
|
||||
Param One: {{paramOne}}
|
||||
</div>
|
||||
<div>
|
||||
Param Two: {{paramTwo}}
|
||||
</div>
|
||||
</ion-content>
|
||||
@@ -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 { }
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 { }
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { IonicPage, NavController, } from '../../../../../..';
|
||||
|
||||
@IonicPage()
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Login</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<button ion-button (click)="clickMe()">Login</button>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class LoginPage {
|
||||
constructor(public nav: NavController) {
|
||||
}
|
||||
|
||||
clickMe() {
|
||||
this.nav.push('LandingPage');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Page Five</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<h2>Page Five</h2>
|
||||
<div>
|
||||
User ID: {{userId}}
|
||||
</div>
|
||||
<div>
|
||||
Name {{name}}
|
||||
</div>
|
||||
<button ion-button (click)="goToNextPage()">Go to Next</button>
|
||||
</ion-content>
|
||||
@@ -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 { }
|
||||
@@ -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'});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Page Four</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<h2>Page Four</h2>
|
||||
<button ion-button (click)="goToPageTwo()">Next</button>
|
||||
</ion-content>
|
||||
@@ -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 { }
|
||||
@@ -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'});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Page Six</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
Page Six
|
||||
<div>
|
||||
Param One: {{paramOne}}
|
||||
</div>
|
||||
<div>
|
||||
Param Two: {{paramTwo}}
|
||||
</div>
|
||||
</ion-content>
|
||||
@@ -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 { }
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -507,6 +507,13 @@ export class Tabs extends Ion implements AfterViewInit, RootNode, ITabs, Navigat
|
||||
return selected ? [selected] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getAllChildNavs(): any[] {
|
||||
return this._tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -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 ] },
|
||||
]
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
_viewport: ViewContainerRef;
|
||||
_views: ViewController[] = [];
|
||||
_zIndexOffset: number = 0;
|
||||
_destroyed: boolean;
|
||||
|
||||
viewDidLoad: EventEmitter<any> = new EventEmitter();
|
||||
viewWillEnter: EventEmitter<any> = new EventEmitter();
|
||||
@@ -102,6 +103,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
this._sbEnabled = config.getBoolean('swipeBackEnabled');
|
||||
this._children = [];
|
||||
this.id = 'n' + (++ctrlIds);
|
||||
this._destroyed = false;
|
||||
}
|
||||
|
||||
push(page: any, params?: any, opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
@@ -284,7 +286,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
if (ti.done) {
|
||||
ti.done(false, false, rejectReason);
|
||||
}
|
||||
if (ti.reject) {
|
||||
if (ti.reject && !this._destroyed) {
|
||||
ti.reject(rejectReason);
|
||||
} else {
|
||||
ti.resolve(false);
|
||||
@@ -594,7 +596,6 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}
|
||||
|
||||
_transition(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction): Promise<NavResult> {
|
||||
|
||||
if (!ti.requiresTransition) {
|
||||
// transition is not required, so we are already done!
|
||||
// they're inserting/removing the views somewhere in the middle or
|
||||
@@ -856,35 +857,38 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
_cleanup(activeView: ViewController) {
|
||||
// ok, cleanup time!! Destroy all of the views that are
|
||||
// INACTIVE and come after the active view
|
||||
const activeViewIndex = this._views.indexOf(activeView);
|
||||
const views = this._views;
|
||||
let reorderZIndexes = false;
|
||||
let view: ViewController;
|
||||
let i: number;
|
||||
// only do this if the views exist, though
|
||||
if (!this._destroyed) {
|
||||
const activeViewIndex = this._views.indexOf(activeView);
|
||||
const views = this._views;
|
||||
let reorderZIndexes = false;
|
||||
let view: ViewController;
|
||||
let i: number;
|
||||
|
||||
for (i = views.length - 1; i >= 0; i--) {
|
||||
view = views[i];
|
||||
if (i > activeViewIndex) {
|
||||
// this view comes after the active view
|
||||
// let's unload it
|
||||
this._willUnload(view);
|
||||
this._destroyView(view);
|
||||
|
||||
} else if (i < activeViewIndex && !this._isPortal) {
|
||||
// this view comes before the active view
|
||||
// and it is not a portal then ensure it is hidden
|
||||
view._domShow(false, this._renderer);
|
||||
}
|
||||
if (view._zIndex <= 0) {
|
||||
reorderZIndexes = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._isPortal && reorderZIndexes) {
|
||||
for (i = 0; i < views.length; i++) {
|
||||
for (i = views.length - 1; i >= 0; i--) {
|
||||
view = views[i];
|
||||
// ******** DOM WRITE ****************
|
||||
view._setZIndex(view._zIndex + INIT_ZINDEX + 1, this._renderer);
|
||||
if (i > activeViewIndex) {
|
||||
// this view comes after the active view
|
||||
// let's unload it
|
||||
this._willUnload(view);
|
||||
this._destroyView(view);
|
||||
|
||||
} else if (i < activeViewIndex && !this._isPortal) {
|
||||
// this view comes before the active view
|
||||
// and it is not a portal then ensure it is hidden
|
||||
view._domShow(false, this._renderer);
|
||||
}
|
||||
if (view._zIndex <= 0) {
|
||||
reorderZIndexes = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._isPortal && reorderZIndexes) {
|
||||
for (i = 0; i < views.length; i++) {
|
||||
view = views[i];
|
||||
// ******** DOM WRITE ****************
|
||||
view._setZIndex(view._zIndex + INIT_ZINDEX + 1, this._renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -991,6 +995,10 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
getAllChildNavs(): any[] {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
registerChildNav(container: NavigationContainer) {
|
||||
this._children.push(container);
|
||||
}
|
||||
@@ -1017,6 +1025,8 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
if (this.parent && this.parent.unregisterChildNav) {
|
||||
this.parent.unregisterChildNav(this);
|
||||
}
|
||||
|
||||
this._destroyed = true;
|
||||
}
|
||||
|
||||
swipeBackStart() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1099,13 +1099,13 @@ describe('NavController', () => {
|
||||
|
||||
it('should not crash when destroyed while transitioning', (done) => {
|
||||
let view1 = mockView(MockView1);
|
||||
nav.push(view1).then(() => {
|
||||
fail('it should not succeed');
|
||||
nav.push(view1).then((succeded: boolean) => {
|
||||
expect(succeded).toEqual(false);
|
||||
done();
|
||||
}).catch((err: any) => {
|
||||
expect(err).toEqual('nav controller was destroyed');
|
||||
}).catch(() => {
|
||||
fail('should never get here');
|
||||
done();
|
||||
});
|
||||
});
|
||||
nav.destroy();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { NavLink, NavSegment } from '../nav-util';
|
||||
import {
|
||||
NavGroup,
|
||||
UrlSerializer,
|
||||
convertUrlToDehydratedSegments,
|
||||
convertUrlToSegments,
|
||||
createMatchedData,
|
||||
findLinkByComponentData,
|
||||
formatUrlPart,
|
||||
isPartMatch,
|
||||
navGroupStringtoObjects,
|
||||
normalizeLinks,
|
||||
parseUrlParts,
|
||||
urlToNavGroupStrings,
|
||||
} 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 +22,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 +70,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 +90,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 +102,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);
|
||||
@@ -103,182 +111,66 @@ describe('UrlSerializer', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('urlToNavGroupStrings', () => {
|
||||
it('should return an empty array of groups when there isnt a nav/tabs keyword', () => {
|
||||
const url = 'some/bogus/url';
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should return a single nav group', () => {
|
||||
const url = 'nav/23/chunk/of/segment';
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0]).toEqual(url);
|
||||
});
|
||||
|
||||
it('should return multiple nav groups', () => {
|
||||
const urlGroupOne = 'nav/1/chunk/of/segment';
|
||||
const urlGroupTwo = 'nav/2/chunk/two';
|
||||
const urlGroupThree = 'nav/3/chunk/three';
|
||||
const url = `${urlGroupOne}/${urlGroupTwo}/${urlGroupThree}`;
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(3);
|
||||
expect(result[0]).toEqual(urlGroupOne);
|
||||
expect(result[1]).toEqual(urlGroupTwo);
|
||||
expect(result[2]).toEqual(urlGroupThree);
|
||||
});
|
||||
|
||||
it('should return a single tabs group', () => {
|
||||
const url = 'tabs/1/tab-one/chunk/of/segment';
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0]).toEqual(url);
|
||||
});
|
||||
|
||||
it('should return multiple tabs groups', () => {
|
||||
const urlGroupOne = 'tabs/1/tab-one/chunk/of/segment';
|
||||
const urlGroupTwo = 'tabs/2/tab-one/chunk/two';
|
||||
const urlGroupThree = 'tabs/3/tab-two/chunk/three';
|
||||
const url = `${urlGroupOne}/${urlGroupTwo}/${urlGroupThree}`;
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(3);
|
||||
expect(result[0]).toEqual(urlGroupOne);
|
||||
expect(result[1]).toEqual(urlGroupTwo);
|
||||
expect(result[2]).toEqual(urlGroupThree);
|
||||
});
|
||||
|
||||
it('should return groups when url has both nav and tabs and starts with tabs', () => {
|
||||
const urlGroupOne = 'tabs/1/tab-one/chunk/of/segment';
|
||||
const urlGroupTwo = 'nav/2/chunk/two';
|
||||
const urlGroupThree = 'tabs/3/tab-two/chunk/three';
|
||||
const urlGroupFour = 'nav/4/chunk/four';
|
||||
const url = `${urlGroupOne}/${urlGroupTwo}/${urlGroupThree}/${urlGroupFour}`;
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(4);
|
||||
expect(result[0]).toEqual(urlGroupOne);
|
||||
expect(result[1]).toEqual(urlGroupTwo);
|
||||
expect(result[2]).toEqual(urlGroupThree);
|
||||
expect(result[3]).toEqual(urlGroupFour);
|
||||
});
|
||||
|
||||
it('should return groups when url has both nav and tabs and starts with nav', () => {
|
||||
const urlGroupOne = 'nav/1/chunk/of/segment';
|
||||
const urlGroupTwo = 'tabs/1/tab-one/chunk/of/segment';
|
||||
const urlGroupThree = 'tabs/3/tab-two/chunk/three';
|
||||
const urlGroupFour = 'nav/4/chunk/four';
|
||||
const url = `${urlGroupOne}/${urlGroupTwo}/${urlGroupThree}/${urlGroupFour}`;
|
||||
const result = urlToNavGroupStrings(url);
|
||||
expect(result.length).toEqual(4);
|
||||
expect(result[0]).toEqual(urlGroupOne);
|
||||
expect(result[1]).toEqual(urlGroupTwo);
|
||||
expect(result[2]).toEqual(urlGroupThree);
|
||||
expect(result[3]).toEqual(urlGroupFour);
|
||||
});
|
||||
});
|
||||
|
||||
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', 'schedule', 'taco/burrito'];
|
||||
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(5);
|
||||
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');
|
||||
});
|
||||
});
|
||||
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('parse', () => {
|
||||
expect(objects[3].type).toEqual(null);
|
||||
expect(objects[3].navId).toEqual(null);
|
||||
expect(objects[3].secondaryId).toEqual(null);
|
||||
expect(objects[3].segmentPieces.length).toEqual(1);
|
||||
expect(objects[3].segmentPieces[0]).toEqual('schedule');
|
||||
|
||||
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[4].type).toEqual(null);
|
||||
expect(objects[4].navId).toEqual(null);
|
||||
expect(objects[4].secondaryId).toEqual(null);
|
||||
expect(objects[4].segmentPieces.length).toEqual(2);
|
||||
expect(objects[4].segmentPieces[0]).toEqual('taco');
|
||||
expect(objects[4].segmentPieces[1]).toEqual('burrito');
|
||||
});
|
||||
});
|
||||
|
||||
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 +234,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 +468,358 @@ describe('UrlSerializer', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('urlToNavGroupStrings', () => {
|
||||
it('should get an array with a single piece url back', () => {
|
||||
const url = 'test';
|
||||
const result = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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 = urlToNavGroupStrings(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');
|
||||
});
|
||||
|
||||
it('should return a segment w/ secondary id even if it has the same name as a router link basic', () => {
|
||||
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: 'schedule', segment: 'schedule' };
|
||||
|
||||
const links = normalizeLinks([link1, link2, link3, link4]);
|
||||
const url = 'schedule/schedule';
|
||||
|
||||
const segmentPairs = convertUrlToDehydratedSegments(url, links);
|
||||
expect(segmentPairs.length).toEqual(1);
|
||||
expect(segmentPairs[0].segments.length).toEqual(1);
|
||||
expect(segmentPairs[0].segments[0].id).toEqual('schedule');
|
||||
expect(segmentPairs[0].segments[0].name).toEqual('schedule');
|
||||
expect(segmentPairs[0].segments[0].secondaryId).toEqual('schedule');
|
||||
});
|
||||
|
||||
it('should return a segment for the secondary id even if it has the same name as a router link advanced', () => {
|
||||
const link1 = { component: MockView1, name: 'about', segment: 'about/:id' };
|
||||
const link2 = { component: MockView1, name: 'schedule', segment: 'schedule/paramOne/:paramOne/paramTwo/:paramTwo' };
|
||||
const link3 = { component: MockView1, name: 'ThirdPage', segment: 'third-page' };
|
||||
const link4 = { component: MockView1, name: 'FourthPage', segment: 'fourth-page/object/:objectId' };
|
||||
const link5 = { component: MockView1, name: 'taco-page', segment: 'taco-page' };
|
||||
|
||||
const links = normalizeLinks([link1, link2, link3, link4, link5]);
|
||||
const url = 'schedule/schedule/paramOne/hello/paramTwo/goodbye'
|
||||
+ '/about/about/123'
|
||||
+ '/tabs/t1/tab-one/third-page'
|
||||
+ '/tabs/t2/tab-two/fourth-page/object/456'
|
||||
+ '/fifth-page/taco-page';
|
||||
|
||||
const segmentPairs = convertUrlToDehydratedSegments(url, links);
|
||||
expect(segmentPairs.length).toEqual(3);
|
||||
|
||||
expect(segmentPairs[0].segments.length).toEqual(2);
|
||||
|
||||
expect(segmentPairs[0].segments[0].id).toEqual('schedule/paramOne/hello/paramTwo/goodbye');
|
||||
expect(segmentPairs[0].segments[0].name).toEqual('schedule');
|
||||
expect(segmentPairs[0].segments[0].secondaryId).toEqual('schedule');
|
||||
expect(segmentPairs[0].segments[0].data.paramOne).toEqual('hello');
|
||||
expect(segmentPairs[0].segments[0].data.paramTwo).toEqual('goodbye');
|
||||
|
||||
expect(segmentPairs[0].segments[1].id).toEqual('about/123');
|
||||
expect(segmentPairs[0].segments[1].name).toEqual('about');
|
||||
expect(segmentPairs[0].segments[1].secondaryId).toEqual('about');
|
||||
expect(segmentPairs[0].segments[1].data.id).toEqual('123');
|
||||
|
||||
expect(segmentPairs[1].segments.length).toEqual(1);
|
||||
|
||||
expect(segmentPairs[1].navGroup.navId).toEqual('t1');
|
||||
expect(segmentPairs[1].navGroup.type).toEqual('tabs');
|
||||
expect(segmentPairs[1].segments[0].id).toEqual('third-page');
|
||||
expect(segmentPairs[1].segments[0].name).toEqual('ThirdPage');
|
||||
expect(segmentPairs[1].segments[0].secondaryId).toEqual('tab-one');
|
||||
|
||||
expect(segmentPairs[2].segments.length).toEqual(2);
|
||||
|
||||
expect(segmentPairs[2].navGroup.navId).toEqual('t2');
|
||||
expect(segmentPairs[2].navGroup.type).toEqual('tabs');
|
||||
expect(segmentPairs[2].segments[0].id).toEqual('fourth-page/object/456');
|
||||
expect(segmentPairs[2].segments[0].name).toEqual('FourthPage');
|
||||
expect(segmentPairs[2].segments[0].secondaryId).toEqual('tab-two');
|
||||
expect(segmentPairs[2].segments[0].data.objectId).toEqual('456');
|
||||
|
||||
expect(segmentPairs[2].segments[1].id).toEqual('taco-page');
|
||||
expect(segmentPairs[2].segments[1].name).toEqual('taco-page');
|
||||
expect(segmentPairs[2].segments[1].secondaryId).toEqual('fifth-page');
|
||||
});
|
||||
});
|
||||
|
||||
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 +834,5 @@ class NotFound {}
|
||||
|
||||
function mockSerializer(navLinks?: NavLink[]) {
|
||||
let deepLinkConfig = mockDeepLinkConfig(navLinks);
|
||||
return new UrlSerializer(deepLinkConfig);
|
||||
return new UrlSerializer(mockApp(), deepLinkConfig);
|
||||
}
|
||||
|
||||
@@ -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,26 +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 urlToNavGroupStrings(url: string): string[] {
|
||||
const tokens = url.split('/');
|
||||
const keywordIndexes = [];
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i] === 'nav' || tokens[i] === 'tabs') {
|
||||
keywordIndexes.push(i);
|
||||
}
|
||||
}
|
||||
const groupings: string[] = [];
|
||||
for (let i = 0; i < keywordIndexes.length; i++) {
|
||||
const startIndex = keywordIndexes[i];
|
||||
const endIndex = keywordIndexes[i + 1 < keywordIndexes.length ? i + 1 : keywordIndexes.length];
|
||||
const group = tokens.slice(startIndex, endIndex);
|
||||
groupings.push(group.join('/'));
|
||||
}
|
||||
return groupings;
|
||||
export function setupUrlSerializer(app: App, userDeepLinkConfig: any): UrlSerializer {
|
||||
return new UrlSerializer(app, userDeepLinkConfig);
|
||||
}
|
||||
|
||||
export function navGroupStringtoObjects(navGroupStrings: string[]): NavGroup[] {
|
||||
@@ -334,20 +300,251 @@ 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 urlToNavGroupStrings(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 = urlToNavGroupStrings(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]));
|
||||
navs = navs[0].getActiveChildNavs();
|
||||
} else if (navs.length > 1) {
|
||||
// this is almost certainly an async race condition bug in userland
|
||||
// if you're in this state, it would be nice to just bail here
|
||||
// but alas we must perservere and handle the issue
|
||||
// the simple solution is to just use the last child
|
||||
// because that is probably what the user wants anyway
|
||||
// remember, do not harm, even if it makes our shizzle ugly
|
||||
segments.push(hydrateSegment(dehydratedSegment, navs[navs.length - 1]));
|
||||
navs = navs[navs.length - 1].getActiveChildNavs();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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.
|
||||
* We look at segment pieces in reverse order to try to build segments
|
||||
* as in, if you had an array like this
|
||||
* ['my', 'super', 'cool', 'url']
|
||||
* we want to look at the pieces in reverse order:
|
||||
* url
|
||||
* cool url
|
||||
* super cool url
|
||||
* my super cool url
|
||||
* cool
|
||||
* super cool
|
||||
* my super cool
|
||||
* super
|
||||
* my super
|
||||
* my
|
||||
**/
|
||||
export function getSegmentsFromNavGroups(navGroups: NavGroup[], navLinks: NavLink[]) {
|
||||
const pairs: DehydratedSegmentPair[] = [];
|
||||
const usedNavLinks = new Set<string>();
|
||||
for (const navGroup of navGroups) {
|
||||
const segments: DehydratedSegment[] = [];
|
||||
|
||||
const segmentPieces = navGroup.segmentPieces.concat([]);
|
||||
|
||||
for (let i = segmentPieces.length; i >= 0; i--) {
|
||||
let created = false;
|
||||
for (let j = 0; j < i; j++) {
|
||||
const startIndex = i - j - 1;
|
||||
const endIndex = i;
|
||||
const subsetOfUrl = segmentPieces.slice(startIndex, endIndex);
|
||||
for (const navLink of navLinks) {
|
||||
if (!usedNavLinks.has(navLink.name)) {
|
||||
const segment = getSegmentsFromUrlPieces(subsetOfUrl, navLink);
|
||||
|
||||
if (segment) {
|
||||
i = startIndex + 1;
|
||||
usedNavLinks.add(navLink.name);
|
||||
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 = startIndex; k < endIndex; k++) {
|
||||
segmentPieces[k] = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (created) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!created && segmentPieces[i - 1]) {
|
||||
// this is very likely a tab's secondary identifier
|
||||
segments.push({
|
||||
id: null,
|
||||
name: null,
|
||||
secondaryId: segmentPieces[i - 1],
|
||||
component: null,
|
||||
loadChildren: null,
|
||||
data: null,
|
||||
defaultHistory: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// since we're getting segments in from right-to-left in the url, reverse them
|
||||
// so they're in the correct order. Also filter out and bogus segments
|
||||
const orderedSegments = segments.reverse();
|
||||
|
||||
|
||||
// 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 < orderedSegments.length; i++) {
|
||||
if (orderedSegments[i].secondaryId && !orderedSegments[i].id && ((i + 1) <= orderedSegments.length - 1)) {
|
||||
orderedSegments[i + 1].secondaryId = orderedSegments[i].secondaryId;
|
||||
orderedSegments[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.name || 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;
|
||||
}
|
||||
|
||||
@@ -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<any> {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user