mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
318737535f | ||
|
|
04fe92cd58 | ||
|
|
b809665944 | ||
|
|
4d786b30ba | ||
|
|
4f49f27824 | ||
|
|
7e8bd5a8fe | ||
|
|
f4539aacc9 | ||
|
|
4911d9f01a | ||
|
|
6e64b8d915 | ||
|
|
e3216da03e | ||
|
|
bd0c265978 | ||
|
|
846eb09991 | ||
|
|
ac4a043314 | ||
|
|
106950533c | ||
|
|
295fe783b0 | ||
|
|
0a6bb3bb21 | ||
|
|
1b9c3daef1 | ||
|
|
26b09f1d49 | ||
|
|
1e9539b9df | ||
|
|
17b3a39f0d | ||
|
|
475b722c7d | ||
|
|
50beafae6a | ||
|
|
f605f0a74c | ||
|
|
e401997a42 | ||
|
|
16f2ebe241 | ||
|
|
584afd040f | ||
|
|
de0f9d5f28 | ||
|
|
4596dbe5c0 | ||
|
|
400aa549d4 | ||
|
|
add0c4ecfe | ||
|
|
519d657e6e | ||
|
|
a8ceee467b | ||
|
|
97f9522110 | ||
|
|
961bfc3ebb | ||
|
|
5b9fe5e81a | ||
|
|
c4e7552b56 | ||
|
|
cec718c6c7 | ||
|
|
ab511c4744 | ||
|
|
2d49e10da4 | ||
|
|
ce46c24413 | ||
|
|
f7fce5fa16 | ||
|
|
d23b9f7d49 | ||
|
|
4c13535416 | ||
|
|
889b49f372 | ||
|
|
acb6facc7b | ||
|
|
2153940de8 |
3
.github/ISSUE_TEMPLATE.md
vendored
3
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,3 +1,6 @@
|
||||
**Resources:**
|
||||
Before submitting an issue, please consult our troubleshooting guide (http://ionicframework.com/docs/troubleshooting/) and developer resources (http://ionicframework.com/docs/developer-resources/)
|
||||
|
||||
**Ionic version:** (check one with "x")
|
||||
[ ] **1.x** (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1)
|
||||
[ ] **2.x**
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ log.txt
|
||||
*.sublime-workspace
|
||||
|
||||
.idea/
|
||||
.sourcemaps/
|
||||
.vscode/
|
||||
.sass-cache/
|
||||
.versions/
|
||||
|
||||
148
CHANGELOG.md
148
CHANGELOG.md
@@ -1,3 +1,151 @@
|
||||
<a name="3.6.0"></a>
|
||||
# [3.6.0](https://github.com/ionic-team/ionic/compare/v3.5.3...v3.6.0) (2017-07-27)
|
||||
|
||||
|
||||
### Upgrade Instructions
|
||||
|
||||
`ionic-angular` 3.6.0 requires developer's to update to the latest version of the `Ionic CLI` and `@ionic/app-scripts`.
|
||||
|
||||
To upgrade, please run
|
||||
|
||||
```
|
||||
npm install -g ionic@latest
|
||||
npm install @ionic/app-scripts@latest --save-dev
|
||||
npm install ionic-angular@latest --save
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
The URL when using deep linking is shortened and improved in this release. Due to a limitation in our nav system, if you're using `ion-tabs` and have a tab name that matches a segment, meaning you have a tab name of `schedule` and a segment of `schedule`, there could potentially be issues. To mitigate these issues, make sure you set the `tabUrlPath` property on the `ion-tab` and give it a unique name. This limitation will require an API change to fix so it will be resolved in `ionic-angular` 4.x.
|
||||
|
||||
|
||||
The upgrades include necessary changes to generators that add back lazy loading functionality, as well as an improved way of generating component/directives/and pipes.
|
||||
|
||||
### New Generators
|
||||
|
||||
The release adds back the functionality to generate lazy loaded pages.
|
||||
To generate a lazy loaded page, run:
|
||||
|
||||
```bash
|
||||
ionic g page <Page-Name>
|
||||
```
|
||||
|
||||
This will include a `.module.ts` file in the page directory created. If you do not want to generate a lazy loaded page, you can run:
|
||||
|
||||
```bash
|
||||
ionic g page <Page-Name> --no-module
|
||||
```
|
||||
|
||||
This will also generate lazy loaded tabs as well, accepting the `--no-module` flag as well to disable it.
|
||||
|
||||
|
||||
For pipes/components/components, we now generate a shared common module for each of these.
|
||||
|
||||
So running:
|
||||
|
||||
```bash
|
||||
ionic g component music-card
|
||||
```
|
||||
|
||||
Will create a `components/components.module.ts` file that declares and exports the `music-card` component.
|
||||
We think that this will allow developers to get up and running with custom components much faster and will less overhead.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **list:** remove margin of MD buttons in ion-item-options ([#12263](https://github.com/ionic-team/ionic/issues/12263)) ([97f9522](https://github.com/ionic-team/ionic/commit/97f9522))
|
||||
* **nav:** make call to setPages return the promise so if it rejects it doesn't get lost ([de0f9d5](https://github.com/ionic-team/ionic/commit/de0f9d5))
|
||||
* **navigation:** account for race conditions in developer's code ([4596dbe](https://github.com/ionic-team/ionic/commit/4596dbe))
|
||||
* **navigation:** fix bug where that occurred when tab identifier and segment had the exact same string ([add0c4e](https://github.com/ionic-team/ionic/commit/add0c4e))
|
||||
* **navigation:** fix null pointer exceptions that would occur when destroying a nav controller while its transitioning ([584afd0](https://github.com/ionic-team/ionic/commit/584afd0))
|
||||
* **navigation:** reduce urls to minimum set of fields ([a8ceee4](https://github.com/ionic-team/ionic/commit/a8ceee4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **generators:** refactor generators ([400aa54](https://github.com/ionic-team/ionic/commit/400aa54))
|
||||
|
||||
|
||||
|
||||
<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)
|
||||
|
||||
## Upgrade Instructions
|
||||
`ionic-angular@3.5.2` is a drop-in replacement for `3.5.1`. To install it, simply run `npm install ionic-angular@3.5.2 --save --save-exact`.
|
||||
|
||||
We have released a new version of our build process for `ionic-angular` apps, `@ionic/app-scripts` in conjunction with this release of `ionic-angular`. While it's not a required update, we recommend it because we have greatly improved the developer experience. Incremental, or update builds while developing are much faster now. We've also added `scope hoisting` for better start-up performance on production builds.
|
||||
|
||||
To upgrade to `@ionic/app-scripts`, run the following command:
|
||||
|
||||
```
|
||||
rm -rf node_modules
|
||||
npm install @ionic/app-scripts@2.0.2 --save-dev --save-exact
|
||||
```
|
||||
|
||||
After installing the update, you'll need to make a minor change to the `src/index.html` file to include a new `<script>` tag for `build/vendor.js`. The reason for this breaking change in `@ionic/app-scripts` is for faster builds. By separating out the `node_modules` dependencies into a `vendor.js` file, the incremental build is faster.
|
||||
|
||||
```
|
||||
...
|
||||
<body>
|
||||
|
||||
<!-- Ionic's root component and where the app will load -->
|
||||
<ion-app></ion-app>
|
||||
|
||||
<!-- cordova.js required for cordova apps -->
|
||||
<script src="cordova.js"></script>
|
||||
|
||||
<!-- The polyfills js is generated during the build process -->
|
||||
<script src="build/polyfills.js"></script>
|
||||
|
||||
<!-- The vendor js is generated during the build process
|
||||
and includes all files in the node_modules directory -->
|
||||
<script src="build/vendor.js"></script>
|
||||
|
||||
<!-- The bundle js is generated during the build process -->
|
||||
<script src="build/main.js"></script>
|
||||
|
||||
</body>
|
||||
...
|
||||
```
|
||||
|
||||
If you're customizing `@ionic/app-scripts`, we recommend you review the [changelog](https://github.com/ionic-team/ionic-app-scripts/blob/master/CHANGELOG.md), and update any of your configs accordingly.
|
||||
|
||||
## Notes
|
||||
`3.5.2` is the same as `3.5.1`. We had a small publishing error.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **navigation:** fix swipe-to-go-back ([04e78d8](https://github.com/ionic-team/ionic/commit/04e78d8))
|
||||
* **navigation:** mark as not transitioning on success in addition to '_transitionFinish', provide no ([48b3243](https://github.com/ionic-team/ionic/commit/48b3243))
|
||||
* **navigation:** navs can have n child navs instead of just one ([fce4422](https://github.com/ionic-team/ionic/commit/fce4422))
|
||||
* **navigation:** restore getActiveChildNav method to maintain old API, add deprecation notice ([d22d77b](https://github.com/ionic-team/ionic/commit/d22d77b))
|
||||
* **navigation:** ts2.4 compatibility ([08be9dc](https://github.com/ionic-team/ionic/commit/08be9dc)), closes [#12233](https://github.com/ionic-team/ionic/issues/12233) [#12235](https://github.com/ionic-team/ionic/issues/12235)
|
||||
* **select:** not activated on enter in input field ([ad25cd1](https://github.com/ionic-team/ionic/commit/ad25cd1)), closes [#12202](https://github.com/ionic-team/ionic/issues/12202)
|
||||
* **sliding-item:** ionSwipe event is fired ([#12157](https://github.com/ionic-team/ionic/issues/12157)) ([b5aa304](https://github.com/ionic-team/ionic/commit/b5aa304)), closes [#12146](https://github.com/ionic-team/ionic/issues/12146)
|
||||
* **tabs:** have tabs behavior match nav when navigating back/forth via the url ([3f39e14](https://github.com/ionic-team/ionic/commit/3f39e14))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **navigation:** support for named ion-nav/ion-tabs to improve url in the short term ([486bff0](https://github.com/ionic-team/ionic/commit/486bff0))
|
||||
|
||||
|
||||
<a name="3.5.1"></a>
|
||||
## [3.5.1](https://github.com/ionic-team/ionic/compare/v3.5.0...v3.5.1) (2017-07-13)
|
||||
|
||||
See the [3.5.2](https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md#352-2017-07-13) changelog. We had a publishing error here.
|
||||
|
||||
<a name="3.5.0"></a>
|
||||
# [3.5.0](https://github.com/ionic-team/ionic/compare/v3.4.2...v3.5.0) (2017-06-28)
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ jobs:
|
||||
working_directory: ~/ionic/
|
||||
docker:
|
||||
- image: node:7
|
||||
branches:
|
||||
ignore:
|
||||
- mono-refactor
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
||||
1417
package-lock.json
generated
1417
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "ionic2",
|
||||
"version": "3.5.0",
|
||||
"version": "3.6.0",
|
||||
"description": "A powerful framework for building mobile and progressive web apps with JavaScript and Angular",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Component } from '@angular/core';
|
||||
/**
|
||||
* Generated class for the $CLASSNAME component.
|
||||
*
|
||||
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
|
||||
* for more info on Angular Components.
|
||||
* See https://angular.io/api/core/Component for more info on Angular
|
||||
* Components.
|
||||
*/
|
||||
@Component({
|
||||
selector: '$FILENAME',
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Directive } from '@angular/core';
|
||||
/**
|
||||
* Generated class for the $CLASSNAME directive.
|
||||
*
|
||||
* See https://angular.io/docs/ts/latest/api/core/index/DirectiveMetadata-class.html
|
||||
* for more info on Angular Directives.
|
||||
* See https://angular.io/api/core/Directive for more info on Angular
|
||||
* Directives.
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[$FILENAME]' // Attribute selector
|
||||
|
||||
@@ -9,8 +9,5 @@ import { $CLASSNAME } from './$FILENAME';
|
||||
imports: [
|
||||
IonicPageModule.forChild($CLASSNAME),
|
||||
],
|
||||
exports: [
|
||||
$CLASSNAME
|
||||
]
|
||||
})
|
||||
export class $CLASSNAMEModule {}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
$IMPORTSTATEMENT
|
||||
|
||||
/**
|
||||
* Generated class for the $CLASSNAME page.
|
||||
*
|
||||
* See http://ionicframework.com/docs/components/#navigation for more info
|
||||
* on Ionic pages and navigation.
|
||||
* See https://ionicframework.com/docs/components/#navigation for more info on
|
||||
* Ionic pages and navigation.
|
||||
*/
|
||||
|
||||
$IONICPAGE
|
||||
@Component({
|
||||
selector: 'page-$FILENAME',
|
||||
templateUrl: '$FILENAME.html',
|
||||
|
||||
@@ -3,11 +3,10 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||
/**
|
||||
* Generated class for the $CLASSNAME pipe.
|
||||
*
|
||||
* See https://angular.io/docs/ts/latest/guide/pipes.html for more info on
|
||||
* Angular Pipes.
|
||||
* See https://angular.io/api/core/Pipe for more info on Angular Pipes.
|
||||
*/
|
||||
@Pipe({
|
||||
name: '$FILENAME',
|
||||
name: '$PIPENAME',
|
||||
})
|
||||
export class $CLASSNAME implements PipeTransform {
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,8 @@ import 'rxjs/add/operator/map';
|
||||
/*
|
||||
Generated class for the $CLASSNAME provider.
|
||||
|
||||
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
|
||||
for more info on providers and Angular DI.
|
||||
See https://angular.io/guide/dependency-injection for more info on providers
|
||||
and Angular DI.
|
||||
*/
|
||||
@Injectable()
|
||||
export class $CLASSNAME {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
$TABS_IMPORTSTATEMENT
|
||||
|
||||
/**
|
||||
* Generated class for the $CLASSNAME tabs.
|
||||
*
|
||||
* See https://angular.io/docs/ts/latest/guide/dependency-injection.html for
|
||||
* more info on providers and Angular DI.
|
||||
* See https://ionicframework.com/docs/components/#navigation for more info on
|
||||
* Ionic pages and navigation.
|
||||
*/
|
||||
$IONICPAGE
|
||||
@Component({
|
||||
selector: 'page-$FILENAME',
|
||||
templateUrl: '$FILENAME.html'
|
||||
|
||||
@@ -206,9 +206,21 @@ export class App {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {NavController} Returns the active NavController. Using this method is preferred when we need access to the top-level navigation controller while on the outside views and handlers like `registerBackButtonAction()`
|
||||
* @return {NavController} Returns the first Active Nav Controller from the list. This method is deprecated
|
||||
*/
|
||||
getActiveNavs(navId?: string): NavControllerBase[] {
|
||||
getActiveNav(): NavControllerBase {
|
||||
console.warn('(getActiveNav) is deprecated and will be removed in the next major release. Use getActiveNavs instead.');
|
||||
const navs = this.getActiveNavs();
|
||||
if (navs && navs.length) {
|
||||
return navs[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {NavController[]} Returns the active NavControllers. Using this method is preferred when we need access to the top-level navigation controller while on the outside views and handlers like `registerBackButtonAction()`
|
||||
*/
|
||||
getActiveNavs(rootNavId?: string): NavControllerBase[] {
|
||||
const portal = this._appRoot._getPortal(Constants.PORTAL_MODAL);
|
||||
if (portal.length() > 0) {
|
||||
return <NavControllerBase[]> findTopNavs(portal);
|
||||
@@ -219,7 +231,16 @@ export class App {
|
||||
if (this._rootNavs.size === 1) {
|
||||
return <NavControllerBase[]> findTopNavs(this._rootNavs.values().next().value);
|
||||
}
|
||||
return <NavControllerBase[]> findTopNavs(this.getRootNavById(navId));
|
||||
if (rootNavId) {
|
||||
return <NavControllerBase[]> findTopNavs(this._rootNavs.get(rootNavId));
|
||||
}
|
||||
// fallback to just using all root names
|
||||
let activeNavs: NavigationContainer[] = [];
|
||||
this._rootNavs.forEach(nav => {
|
||||
const topNavs = findTopNavs(nav);
|
||||
activeNavs = activeNavs.concat(topNavs);
|
||||
});
|
||||
return <NavControllerBase[]> activeNavs;
|
||||
}
|
||||
|
||||
getRootNav(): any {
|
||||
@@ -398,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) {
|
||||
@@ -434,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;
|
||||
|
||||
@@ -312,7 +312,7 @@ describe('App', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getActiveNav', () => {
|
||||
describe('getActiveNavs', () => {
|
||||
|
||||
it('should get active NavController when using tabs with nested nav', () => {
|
||||
const nav = mockNavController();
|
||||
@@ -340,6 +340,11 @@ describe('App', () => {
|
||||
expect(activeNavs.length).toEqual(2);
|
||||
expect(activeNavs[0]).toEqual(nav2);
|
||||
expect(activeNavs[1]).toEqual(nav3);
|
||||
|
||||
const activeNavsTwo = app.getActiveNavs();
|
||||
expect(activeNavsTwo.length).toEqual(2);
|
||||
expect(activeNavsTwo[0]).toEqual(nav2);
|
||||
expect(activeNavsTwo[1]).toEqual(nav3);
|
||||
});
|
||||
|
||||
it('should get active NavController when using tabs, nested in a root nav', () => {
|
||||
@@ -355,10 +360,12 @@ describe('App', () => {
|
||||
tab2.setSelected(true);
|
||||
|
||||
expect(app.getActiveNavs(nav.id)[0]).toBe(tab2);
|
||||
expect(app.getActiveNavs()[0]).toBe(tab2);
|
||||
|
||||
tab2.setSelected(false);
|
||||
tab3.setSelected(true);
|
||||
expect(app.getActiveNavs(nav.id)[0]).toBe(tab3);
|
||||
expect(app.getActiveNavs()[0]).toBe(tab3);
|
||||
});
|
||||
|
||||
it('should get active tab NavController when using tabs, and tabs is the root', () => {
|
||||
@@ -371,10 +378,12 @@ describe('App', () => {
|
||||
tab2.setSelected(true);
|
||||
|
||||
expect(app.getActiveNavs(tabs.id)[0]).toBe(tab2);
|
||||
expect(app.getActiveNavs()[0]).toBe(tab2);
|
||||
|
||||
tab2.setSelected(false);
|
||||
tab3.setSelected(true);
|
||||
expect(app.getActiveNavs(tabs.id)[0]).toBe(tab3);
|
||||
expect(app.getActiveNavs()[0]).toBe(tab3);
|
||||
});
|
||||
|
||||
it('should get active NavController when nested 3 deep', () => {
|
||||
@@ -387,6 +396,8 @@ describe('App', () => {
|
||||
nav2.registerChildNav(nav3);
|
||||
|
||||
expect(app.getActiveNavs(nav1.id)[0]).toBe(nav3);
|
||||
expect(app.getActiveNavs()[0]).toBe(nav3);
|
||||
expect(app.getActiveNavs().length).toBe(1);
|
||||
});
|
||||
|
||||
it('should get active NavController when nested 2 deep', () => {
|
||||
@@ -399,12 +410,15 @@ describe('App', () => {
|
||||
const activeNav = app.getActiveNavs(nav1.id)[0];
|
||||
|
||||
expect(activeNav).toBe(nav2);
|
||||
|
||||
expect(app.getActiveNavs()[0]).toBe(nav2);
|
||||
});
|
||||
|
||||
it('should get active NavController when only one nav controller', () => {
|
||||
const nav = mockNavController();
|
||||
app.registerRootNav(nav);
|
||||
expect(app.getActiveNavs(nav.id)[0]).toBe(nav);
|
||||
expect(app.getActiveNavs()[0]).toBe(nav);
|
||||
});
|
||||
|
||||
it('should set/get the root nav controller', () => {
|
||||
@@ -414,9 +428,9 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should not get an active NavController if there is not root set', () => {
|
||||
const activeNav = app.getActiveNavs('');
|
||||
const activeNavs = app.getActiveNavs();
|
||||
const rootNav = app.getRootNavById('');
|
||||
expect(activeNav.length).toEqual(0);
|
||||
expect(activeNavs.length).toEqual(0);
|
||||
expect(rootNav).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -438,6 +452,9 @@ describe('App', () => {
|
||||
|
||||
expect(activeNavOne).toBe(childNavOne);
|
||||
expect(activeNavTwo).toBe(childNavTwo);
|
||||
|
||||
expect(app.getActiveNavs()[0]).toBe(childNavOne);
|
||||
expect(app.getActiveNavs()[1]).toBe(childNavTwo);
|
||||
});
|
||||
|
||||
it('should get the active nav when no id is provided assuming there is one nav', () => {
|
||||
@@ -451,6 +468,167 @@ describe('App', () => {
|
||||
|
||||
expect(result).toEqual(childNavOne);
|
||||
});
|
||||
|
||||
it('should return the all the active navs when there is not an id passed', () => {
|
||||
const rootNavOne = mockNavController();
|
||||
app.registerRootNav(rootNavOne);
|
||||
|
||||
const rootNavTwo = mockNavController();
|
||||
app.registerRootNav(rootNavTwo);
|
||||
|
||||
const childNavOne = mockNavController();
|
||||
rootNavOne.registerChildNav(childNavOne);
|
||||
|
||||
const childChildNavOne = mockNavController();
|
||||
childNavOne.registerChildNav(childChildNavOne);
|
||||
|
||||
const childNavTwo = mockNavController();
|
||||
rootNavTwo.registerChildNav(childNavTwo);
|
||||
|
||||
const childChildNavTwo = mockNavController();
|
||||
childNavTwo.registerChildNav(childChildNavTwo);
|
||||
|
||||
const results = app.getActiveNavs();
|
||||
expect(results.length).toEqual(2);
|
||||
expect(results[0]).toEqual(childChildNavOne);
|
||||
expect(results[1]).toEqual(childChildNavTwo);
|
||||
|
||||
const withIdResultsOne = app.getActiveNavs(rootNavOne.id);
|
||||
expect(withIdResultsOne.length).toEqual(1);
|
||||
expect(withIdResultsOne[0]).toEqual(childChildNavOne);
|
||||
|
||||
const withIdResultsTwo = app.getActiveNavs(rootNavTwo.id);
|
||||
expect(withIdResultsTwo.length).toEqual(1);
|
||||
expect(withIdResultsTwo[0]).toEqual(childChildNavTwo);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getActiveNav', () => {
|
||||
it('should get active NavController when using tabs with nested nav', () => {
|
||||
const nav = mockNavController();
|
||||
app.registerRootNav(nav);
|
||||
|
||||
const tabs = mockTabs();
|
||||
const tab1 = mockTab(tabs);
|
||||
const tab2 = mockTab(tabs);
|
||||
nav.registerChildNav(tabs);
|
||||
|
||||
tab2.setSelected(true);
|
||||
const nav2 = mockNavController();
|
||||
nav2.name = 'nav2';
|
||||
const nav3 = mockNavController();
|
||||
nav3.name = 'nav3';
|
||||
const nav4 = mockNavController();
|
||||
nav4.name = 'nav4';
|
||||
|
||||
tab1.registerChildNav(nav4);
|
||||
// tab 2 registers two child navs!!
|
||||
tab2.registerChildNav(nav2);
|
||||
tab2.registerChildNav(nav3);
|
||||
|
||||
const activeNav = app.getActiveNav();
|
||||
expect(activeNav).toEqual(nav2);
|
||||
});
|
||||
|
||||
it('should get active NavController when using tabs, nested in a root nav', () => {
|
||||
const nav = mockNavController();
|
||||
app.registerRootNav(nav);
|
||||
|
||||
const tabs = mockTabs();
|
||||
mockTab(tabs);
|
||||
const tab2 = mockTab(tabs);
|
||||
const tab3 = mockTab(tabs);
|
||||
nav.registerChildNav(tabs);
|
||||
|
||||
tab2.setSelected(true);
|
||||
|
||||
expect(app.getActiveNav()).toBe(tab2);
|
||||
|
||||
tab2.setSelected(false);
|
||||
tab3.setSelected(true);
|
||||
expect(app.getActiveNav()).toBe(tab3);
|
||||
});
|
||||
|
||||
it('should get active tab NavController when using tabs, and tabs is the root', () => {
|
||||
const tabs = mockTabs();
|
||||
mockTab(tabs);
|
||||
const tab2 = mockTab(tabs);
|
||||
const tab3 = mockTab(tabs);
|
||||
app.registerRootNav(tabs);
|
||||
|
||||
tab2.setSelected(true);
|
||||
|
||||
expect(app.getActiveNav()).toBe(tab2);
|
||||
|
||||
tab2.setSelected(false);
|
||||
tab3.setSelected(true);
|
||||
expect(app.getActiveNav()).toBe(tab3);
|
||||
});
|
||||
|
||||
it('should get active NavController when nested 3 deep', () => {
|
||||
const nav1 = mockNavController();
|
||||
const nav2 = mockNavController();
|
||||
const nav3 = mockNavController();
|
||||
app.registerRootNav(nav1);
|
||||
|
||||
nav1.registerChildNav(nav2);
|
||||
nav2.registerChildNav(nav3);
|
||||
|
||||
expect(app.getActiveNav()).toBe(nav3);
|
||||
});
|
||||
|
||||
it('should get active NavController when nested 2 deep', () => {
|
||||
const nav1 = mockNavController();
|
||||
const nav2 = mockNavController();
|
||||
app.registerRootNav(nav1);
|
||||
|
||||
nav1.registerChildNav(nav2);
|
||||
|
||||
const activeNav = app.getActiveNav();
|
||||
|
||||
expect(activeNav).toBe(nav2);
|
||||
});
|
||||
|
||||
it('should get active NavController when only one nav controller', () => {
|
||||
const nav = mockNavController();
|
||||
app.registerRootNav(nav);
|
||||
expect(app.getActiveNav()).toBe(nav);
|
||||
});
|
||||
|
||||
it('should not get an active NavController if there is not root set', () => {
|
||||
const activeNav = app.getActiveNav();
|
||||
expect(activeNav).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should just work when there are multiple active navs', () => {
|
||||
const rootNavOne = mockNavController();
|
||||
const rootNavTwo = mockNavController();
|
||||
|
||||
app.registerRootNav(rootNavOne);
|
||||
app.registerRootNav(rootNavTwo);
|
||||
|
||||
const childNavOne = mockNavController();
|
||||
const childNavTwo = mockNavController();
|
||||
|
||||
rootNavOne.registerChildNav(childNavOne);
|
||||
rootNavTwo.registerChildNav(childNavTwo);
|
||||
|
||||
const activeNavOne = app.getActiveNav();
|
||||
|
||||
expect(activeNavOne).toBe(childNavOne);
|
||||
});
|
||||
|
||||
it('should get the active nav when no id is provided assuming there is one nav', () => {
|
||||
const rootNavOne = mockNavController();
|
||||
app.registerRootNav(rootNavOne);
|
||||
|
||||
const childNavOne = mockNavController();
|
||||
rootNavOne.registerChildNav(childNavOne);
|
||||
|
||||
const result = app.getActiveNav();
|
||||
|
||||
expect(result).toEqual(childNavOne);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRootNavs', () => {
|
||||
@@ -589,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;
|
||||
|
||||
@@ -31,7 +31,7 @@ export class EventEmitterProxy<T> extends EventEmitter<T> {
|
||||
* The Content component provides an easy to use content area with
|
||||
* some useful methods to control the scrollable area. There should
|
||||
* only be one content in a single view component. If additional scrollable
|
||||
* elements are need, use [ionScroll](../../scroll/Scroll).
|
||||
* elements are needed, use [ionScroll](../../scroll/Scroll).
|
||||
*
|
||||
*
|
||||
* The content area can also implement pull-to-refresh with the
|
||||
|
||||
@@ -135,6 +135,8 @@ import {
|
||||
* to serialize and pass within JSON objects, and sending databases a standardized
|
||||
* format which it can be easily parsed if need be.
|
||||
*
|
||||
* To create an ISO datetime string for the current date and time, e.g. use `const currentDate = (new Date()).toISOString();`.
|
||||
*
|
||||
* An ISO format can be used as a simple year, or just the hour and minute, or get more
|
||||
* detailed down to the millisecond and timezone. Any of the ISO formats below can be used,
|
||||
* and after a user selects a new value, Ionic will continue to use the same ISO format
|
||||
@@ -167,7 +169,7 @@ import {
|
||||
* ## Min and Max Datetimes
|
||||
*
|
||||
* Dates are infinite in either direction, so for a user's selection there should be at
|
||||
* least some form of restricting the dates that can be selected. Be default, the maximum
|
||||
* least some form of restricting the dates that can be selected. By default, the maximum
|
||||
* date is to the end of the current year, and the minimum date is from the beginning
|
||||
* of the year that was 100 years ago.
|
||||
*
|
||||
|
||||
@@ -211,12 +211,12 @@ export class TextInput extends BaseInput<string> implements IonicFormInput {
|
||||
@ViewChild('textInput', { read: ElementRef }) _native: ElementRef;
|
||||
|
||||
/**
|
||||
* @input {string} Instructional text that shows before the input has a value.
|
||||
* @input {string} Set the input's autocomplete property. Values: `"on"`, `"off"`. Default `"off"`.
|
||||
*/
|
||||
@Input() autocomplete: string = '';
|
||||
|
||||
/**
|
||||
* @input {string} Instructional text that shows before the input has a value.
|
||||
* @input {string} Set the input's autocorrect property. Values: `"on"`, `"off"`. Default `"off"`.
|
||||
*/
|
||||
@Input() autocorrect: string = '';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { DeepLinker } from '../../navigation/deep-linker';
|
||||
*
|
||||
* When a modal (or any other overlay such as an alert or actionsheet) is
|
||||
* "presented" to a nav controller, the overlay is added to the app's root nav.
|
||||
* After the modal has been presented, from within the component instance The
|
||||
* After the modal has been presented, from within the component instance, the
|
||||
* modal can later be closed or "dismissed" by using the ViewController's
|
||||
* `dismiss` method. Additionally, you can dismiss any overlay by using `pop`
|
||||
* on the root nav controller. Modals are not reusable. When a modal is dismissed
|
||||
@@ -112,6 +112,25 @@ import { DeepLinker } from '../../navigation/deep-linker';
|
||||
*
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* A common issue is that a developer may try to implement navigation in a modal, but when you try NavController.push(),
|
||||
* you will notice that the status bar on iOS gets cut off. The proper way to implement navigation in a modal is to
|
||||
* make the modal component a navigation container, and set the root page to the page you want to show in your modal.
|
||||
*
|
||||
* ```ts
|
||||
* @Component({
|
||||
* template: '<ion-nav [root]="rootPage" [rootParams]="rootParams"></ion-nav>'
|
||||
* })
|
||||
* export class MyModalWrapper {
|
||||
* rootPage = 'MyModalContentPage'; // This is the page you want your modal to display
|
||||
* rootParams;
|
||||
*
|
||||
* constructor(navParams: NavParams, private viewCtrl: ViewController) {
|
||||
* this.rootParams = Object.assign({}, navParams.data, {viewCtrl: viewCtrl});
|
||||
* // This line will send the view controller into your child views, so you can dismiss the modals from there.
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @demo /docs/demos/src/modal/
|
||||
* @see {@link /docs/components#modals Modal Component Docs}
|
||||
*/
|
||||
|
||||
@@ -32,8 +32,8 @@ import { Page } from '../../navigation/nav-util';
|
||||
* template: `<button ion-button [navPush]="pushPage" [navParams]="params">Go</button>`
|
||||
* })
|
||||
* class MyPage {
|
||||
* params: Object;
|
||||
* pushPage: any;
|
||||
* params: Object;
|
||||
* constructor(){
|
||||
* this.pushPage = LoginPage;
|
||||
* this.params = { id: 42 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -693,8 +693,10 @@ function destroyLoop(s: Slides) {
|
||||
eachChild(s._wrapper, '.' + CLS.slide + '.' + CLS.slideDuplicate, ele => {
|
||||
ele.parentElement.removeChild(ele);
|
||||
});
|
||||
for (var i = 0; i < s._slides.length; i++) {
|
||||
s._slides[i].removeAttribute('data-swiper-slide-index');
|
||||
if (s._slides) {
|
||||
for (var i = 0; i < s._slides.length; i++) {
|
||||
s._slides[i].removeAttribute('data-swiper-slide-index');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { isTrueProperty } from '../../util/util';
|
||||
import { Tab as ITab } from '../../navigation/nav-interfaces';
|
||||
import { NavControllerBase } from '../../navigation/nav-controller-base';
|
||||
import { NavOptions } from '../../navigation/nav-util';
|
||||
import { NavOptions, TransitionDoneFn } from '../../navigation/nav-util';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { TabButton } from './tab-button';
|
||||
import { Tabs } from './tabs';
|
||||
@@ -304,7 +304,7 @@ export class Tab extends NavControllerBase implements ITab {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
load(opts: NavOptions, done?: () => void) {
|
||||
load(opts: NavOptions, done?: TransitionDoneFn) {
|
||||
if (this._lazyRootFromUrl || (!this._loaded && this.root)) {
|
||||
this.setElementClass('show-tab', true);
|
||||
// okay, first thing we need to do if check if the view already exists
|
||||
@@ -317,7 +317,10 @@ export class Tab extends NavControllerBase implements ITab {
|
||||
if (i === numViews) {
|
||||
// this is the last view in the stack and it's the same
|
||||
// as the segment so there's no change needed
|
||||
return done();
|
||||
if (done) {
|
||||
done(false, false);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
// it's not the exact view as the end
|
||||
// let's have this nav go back to this exact view
|
||||
@@ -343,7 +346,10 @@ export class Tab extends NavControllerBase implements ITab {
|
||||
this._dom.read(() => {
|
||||
this.resize();
|
||||
});
|
||||
return done();
|
||||
if (done) {
|
||||
done(false, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -507,6 +507,13 @@ export class Tabs extends Ion implements AfterViewInit, RootNode, ITabs, Navigat
|
||||
return selected ? [selected] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getAllChildNavs(): any[] {
|
||||
return this._tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -635,7 +642,7 @@ export class Tabs extends Ion implements AfterViewInit, RootNode, ITabs, Navigat
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getSelectedTabIndex(secondaryId: string, fallbackIndex: number = 0): number {
|
||||
_getSelectedTabIndex(secondaryId: string = '', fallbackIndex: number = 0): number {
|
||||
// we found a segment which probably represents which tab to select
|
||||
const indexMatch = secondaryId.match(/tab-(\d+)/);
|
||||
if (indexMatch) {
|
||||
|
||||
@@ -37,6 +37,8 @@ import { ToastOptions } from './toast-options';
|
||||
*
|
||||
* @usage
|
||||
* ```ts
|
||||
* import { ToastController } from 'ionic-angular';
|
||||
*
|
||||
* constructor(private toastCtrl: ToastController) {
|
||||
*
|
||||
* }
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ViewController } from '../../navigation/view-controller';
|
||||
* @name Header
|
||||
* @description
|
||||
* Header is a parent component that holds the navbar and toolbar component.
|
||||
* It's important to note that `ion-header` needs to be the one of the three root elements of a page
|
||||
* It's important to note that `ion-header` needs to be one of the three root elements of a page
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
|
||||
@@ -118,6 +118,7 @@ import { isArray, isDefined, isFunction, isObject } from '../util/util';
|
||||
* | `popoverEnter` | `string` | The name of the transition to use while a popover is presented. |
|
||||
* | `popoverLeave` | `string` | The name of the transition to use while a popover is dismissed. |
|
||||
* | `spinner` | `string` | The default spinner to use when a name is not defined. |
|
||||
* | `statusbarPadding` | `boolean` | Whether to hide extra padding for statusbar. |
|
||||
* | `swipeBackEnabled` | `boolean` | Whether native iOS swipe to go back functionality is enabled. |
|
||||
* | `tabsHighlight` | `boolean` | Whether to show a highlight line under the tab when it is selected. |
|
||||
* | `tabsLayout` | `string` | The layout to use for all tabs. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"title-hide"`. |
|
||||
|
||||
@@ -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 ] },
|
||||
]
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ComponentFactory, ComponentFactoryResolver } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
import { App } from '../components/app/app';
|
||||
import { DIRECTION_BACK, NavLink, NavSegment, convertToViews, isNav, isTab, isTabs, } from './nav-util';
|
||||
import { DIRECTION_BACK, NavLink, NavSegment, TransitionDoneFn, convertToViews, isNav, isTab, isTabs } from './nav-util';
|
||||
import { ModuleLoader } from '../util/module-loader';
|
||||
import { isArray, isPresent } from '../util/util';
|
||||
import { Tab, Tabs } from './nav-interfaces';
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,9 +380,9 @@ export class DeepLinker {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_loadViewForSegment(navContainer: NavigationContainer, segment: NavSegment, done: Function) {
|
||||
_loadViewForSegment(navContainer: NavigationContainer, segment: NavSegment, done: TransitionDoneFn) {
|
||||
if (!segment) {
|
||||
return done();
|
||||
return done(false, false);
|
||||
}
|
||||
|
||||
if (isTabs(navContainer) || (isTab(navContainer) && navContainer.parent)) {
|
||||
@@ -393,7 +395,7 @@ export class DeepLinker {
|
||||
updateUrl: false,
|
||||
animate: false
|
||||
}, true);
|
||||
return done();
|
||||
return done(false, false);
|
||||
}
|
||||
|
||||
const navController = <NavController> <any> navContainer;
|
||||
@@ -408,7 +410,7 @@ export class DeepLinker {
|
||||
if (i === numViews) {
|
||||
// this is the last view in the stack and it's the same
|
||||
// as the segment so there's no change needed
|
||||
return done();
|
||||
return done(false, false);
|
||||
} else {
|
||||
// it's not the exact view as the end
|
||||
// let's have this nav go back to this exact view
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
STATE_DESTROYED,
|
||||
STATE_INITIALIZED,
|
||||
STATE_NEW,
|
||||
TransitionDoneFn,
|
||||
TransitionInstruction,
|
||||
convertToViews,
|
||||
} from './nav-util';
|
||||
@@ -62,6 +63,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,9 +104,10 @@ 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> {
|
||||
push(page: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
insertStart: -1,
|
||||
insertViews: [{ page: page, params: params }],
|
||||
@@ -112,7 +115,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}, done);
|
||||
}
|
||||
|
||||
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
insertStart: insertIndex,
|
||||
insertViews: [{ page: page, params: params }],
|
||||
@@ -120,7 +123,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}, done);
|
||||
}
|
||||
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
insertStart: insertIndex,
|
||||
insertViews: insertPages,
|
||||
@@ -128,7 +131,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}, done);
|
||||
}
|
||||
|
||||
pop(opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
pop(opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
removeStart: -1,
|
||||
removeCount: 1,
|
||||
@@ -136,7 +139,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}, done);
|
||||
}
|
||||
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
let config: TransitionInstruction = {
|
||||
removeStart: -1,
|
||||
removeCount: -1,
|
||||
@@ -151,7 +154,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
return this._queueTrns(config, done);
|
||||
}
|
||||
|
||||
popToRoot(opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
popToRoot(opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
removeStart: 1,
|
||||
removeCount: -1,
|
||||
@@ -167,7 +170,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
remove(startIndex: number, removeCount: number = 1, opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
remove(startIndex: number, removeCount: number = 1, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
removeStart: startIndex,
|
||||
removeCount: removeCount,
|
||||
@@ -175,7 +178,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}, done);
|
||||
}
|
||||
|
||||
removeView(viewController: ViewController, opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
removeView(viewController: ViewController, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this._queueTrns({
|
||||
removeView: viewController,
|
||||
removeStart: 0,
|
||||
@@ -184,12 +187,12 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
}, done);
|
||||
}
|
||||
|
||||
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
return this.setPages([{ page: pageOrViewCtrl, params: params }], opts, done);
|
||||
}
|
||||
|
||||
|
||||
setPages(viewControllers: any[], opts?: NavOptions, done?: () => void): Promise<any> {
|
||||
setPages(viewControllers: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise<any> {
|
||||
if (isBlank(opts)) {
|
||||
opts = {};
|
||||
}
|
||||
@@ -216,7 +219,7 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
// 7. _transitionStart(): called once the transition actually starts, it initializes the Animation underneath.
|
||||
// 8. _transitionFinish(): called once the transition finishes
|
||||
// 9. _cleanup(): syncs the navigation internal state with the DOM. For example it removes the pages from the DOM or hides/show them.
|
||||
_queueTrns(ti: TransitionInstruction, done: () => void): Promise<boolean> {
|
||||
_queueTrns(ti: TransitionInstruction, done: TransitionDoneFn): Promise<boolean> {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
ti.resolve = resolve;
|
||||
ti.reject = reject;
|
||||
@@ -284,7 +287,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 +597,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 +858,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 +996,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 +1026,8 @@ export class NavControllerBase extends Ion implements NavController {
|
||||
if (this.parent && this.parent.unregisterChildNav) {
|
||||
this.parent.unregisterChildNav(this);
|
||||
}
|
||||
|
||||
this._destroyed = true;
|
||||
}
|
||||
|
||||
swipeBackStart() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EventEmitter } from '@angular/core';
|
||||
|
||||
import { Config } from '../config/config';
|
||||
import { NavOptions } from './nav-util';
|
||||
import { NavOptions, TransitionDoneFn } from './nav-util';
|
||||
import { Page } from './nav-util';
|
||||
import { ViewController } from './view-controller';
|
||||
import { NavigationContainer } from './navigation-container';
|
||||
@@ -420,7 +420,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract push(page: Page | string, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract push(page: Page | string, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* Inserts a component into the nav stack at the specified index. This is useful if
|
||||
@@ -433,7 +433,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract insert(insertIndex: number, page: Page | string, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract insert(insertIndex: number, page: Page | string, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* Inserts an array of components into the nav stack at the specified index.
|
||||
@@ -445,7 +445,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract insertPages(insertIndex: number, insertPages: Array<{page: Page | string, params?: any}>, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract insertPages(insertIndex: number, insertPages: Array<{page: Page | string, params?: any}>, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* Call to navigate back from a current component. Similar to `push()`, you
|
||||
@@ -454,7 +454,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract pop(opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract pop(opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* Navigate back to the root of the stack, no matter how far back that is.
|
||||
@@ -462,7 +462,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract popToRoot(opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract popToRoot(opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
@@ -475,11 +475,10 @@ export abstract class NavController implements NavigationContainer {
|
||||
* when a new instance needs to be created.
|
||||
*
|
||||
* @param {Page|string|ViewController} page The component class or deeplink name you want to push onto the navigation stack.
|
||||
* @param {object} [params={}] Any NavParams to be used when a new view instance is created at the root.
|
||||
* @param {object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract popTo(page: Page | string | ViewController, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract popTo(page: Page | string | ViewController, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
@@ -497,7 +496,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Any options you want to use pass to transtion.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract remove(startIndex: number, removeCount?: number, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract remove(startIndex: number, removeCount?: number, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* Removes the specified view controller from the nav stack.
|
||||
@@ -506,7 +505,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {object} [opts={}] Any options you want to use pass to transtion.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract removeView(viewController: ViewController, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract removeView(viewController: ViewController, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* Set the root for the current navigation stack.
|
||||
@@ -516,7 +515,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {Function} done Callback function on done.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract setRoot(pageOrViewCtrl: Page | string | ViewController, params?: any, opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract setRoot(pageOrViewCtrl: Page | string | ViewController, params?: any, opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
abstract goToRoot(options: NavOptions): Promise<any>;
|
||||
|
||||
/**
|
||||
@@ -529,7 +528,7 @@ export abstract class NavController implements NavigationContainer {
|
||||
* @param {Object} [opts={}] Nav options to go with this transition.
|
||||
* @returns {Promise} Returns a promise which is resolved when the transition has completed.
|
||||
*/
|
||||
abstract setPages(pages: ({page: Page | string, params?: any} | ViewController)[], opts?: NavOptions, done?: Function): Promise<any>;
|
||||
abstract setPages(pages: ({page: Page | string, params?: any} | ViewController)[], opts?: NavOptions, done?: TransitionDoneFn): Promise<any>;
|
||||
|
||||
/**
|
||||
* @param {number} index The index of the page to get.
|
||||
@@ -599,6 +598,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.
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*
|
||||
* @usage
|
||||
* ```ts
|
||||
* import { NavParams } from 'ionic-angular';
|
||||
*
|
||||
* export class MyClass{
|
||||
* constructor(public navParams: NavParams){
|
||||
* // userParams is an object we have in our nav-parameters
|
||||
@@ -32,6 +34,8 @@ export class NavParams {
|
||||
* Get the value of a nav-parameter for the current view
|
||||
*
|
||||
* ```ts
|
||||
* import { NavParams } from 'ionic-angular';
|
||||
*
|
||||
* export class MyClass{
|
||||
* constructor(public navParams: NavParams){
|
||||
* // userParams is an object we have in our nav-parameters
|
||||
|
||||
@@ -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 {
|
||||
@@ -211,6 +221,10 @@ export interface TransitionRejectFn {
|
||||
(rejectReason: any, transition?: Transition): void;
|
||||
}
|
||||
|
||||
export interface TransitionDoneFn {
|
||||
(hasCompleted: boolean, requiresTransition: boolean, enteringName?: string, leavingName?: string, direction?: string): void;
|
||||
}
|
||||
|
||||
export interface TransitionInstruction {
|
||||
opts: NavOptions;
|
||||
insertStart?: number;
|
||||
@@ -220,7 +234,7 @@ export interface TransitionInstruction {
|
||||
removeCount?: number;
|
||||
resolve?: (hasCompleted: boolean) => void;
|
||||
reject?: (rejectReason: string) => void;
|
||||
done?: Function;
|
||||
done?: TransitionDoneFn;
|
||||
leavingRequiresTransition?: boolean;
|
||||
enteringRequiresTransition?: boolean;
|
||||
requiresTransition?: boolean;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export type DocumentDirection = 'ltr' | 'rtl';
|
||||
*
|
||||
* @Component({...})
|
||||
* export MyPage {
|
||||
* constructor(public plt: Platform) {
|
||||
* constructor(public platform: Platform) {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
@@ -145,8 +145,8 @@ export class Platform {
|
||||
*
|
||||
* @Component({...})
|
||||
* export MyPage {
|
||||
* constructor(public plt: Platform) {
|
||||
* if (this.plt.is('ios')) {
|
||||
* constructor(public platform: Platform) {
|
||||
* if (this.platform.is('ios')) {
|
||||
* // This will only print when on iOS
|
||||
* console.log('I am an iOS device!');
|
||||
* }
|
||||
@@ -186,9 +186,9 @@ export class Platform {
|
||||
*
|
||||
* @Component({...})
|
||||
* export MyPage {
|
||||
* constructor(public plt: Platform) {
|
||||
* constructor(public platform: Platform) {
|
||||
* // This will print an array of the current platforms
|
||||
* console.log(this.plt.platforms());
|
||||
* console.log(this.platform.platforms());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
@@ -208,10 +208,10 @@ export class Platform {
|
||||
*
|
||||
* @Component({...})
|
||||
* export MyPage {
|
||||
* constructor(public plt: Platform) {
|
||||
* constructor(public platform: Platform) {
|
||||
* // This will print an object containing
|
||||
* // all of the platforms and their versions
|
||||
* console.log(plt.versions());
|
||||
* console.log(platform.versions());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
@@ -255,8 +255,8 @@ export class Platform {
|
||||
*
|
||||
* @Component({...})
|
||||
* export MyApp {
|
||||
* constructor(public plt: Platform) {
|
||||
* this.plt.ready().then((readySource) => {
|
||||
* constructor(public platform: Platform) {
|
||||
* this.platform.ready().then((readySource) => {
|
||||
* console.log('Platform ready from', readySource);
|
||||
* // Platform now ready, execute any required native code
|
||||
* });
|
||||
|
||||
@@ -11,7 +11,7 @@ import { GestureController } from '../gestures/gesture-controller';
|
||||
import { Haptic } from '../tap-click/haptic';
|
||||
import { IonicApp } from '../components/app/app-root';
|
||||
import { Menu } from '../components/menu/menu';
|
||||
import { NavOptions } from '../navigation/nav-util';
|
||||
import { NavOptions, TransitionDoneFn } from '../navigation/nav-util';
|
||||
import { NavControllerBase } from '../navigation/nav-controller-base';
|
||||
import { OverlayPortal } from '../components/app/overlay-portal';
|
||||
import { PageTransition } from '../transitions/page-transition';
|
||||
@@ -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);
|
||||
|
||||
@@ -501,8 +501,9 @@ export function mockTab(parentTabs: Tabs, overrideLoad: boolean = true): Tab {
|
||||
);
|
||||
|
||||
if (overrideLoad) {
|
||||
tab.load = (_opts: any, cb: Function) => {
|
||||
cb();
|
||||
tab.load = (_opts: any, cb: TransitionDoneFn) => {
|
||||
cb(false, false);
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user