mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 21:48:42 +08:00
feat(router): reverse lookup with params
This commit is contained in:
@ -25,6 +25,7 @@ import { Transition } from './transition';
|
|||||||
|
|
||||||
import iosTransitionAnimation from './animations/ios.transition';
|
import iosTransitionAnimation from './animations/ios.transition';
|
||||||
import mdTransitionAnimation from './animations/md.transition';
|
import mdTransitionAnimation from './animations/md.transition';
|
||||||
|
import { RouteID } from '../router/utils/interfaces';
|
||||||
|
|
||||||
const TrnsCtrl = new TransitionController();
|
const TrnsCtrl = new TransitionController();
|
||||||
|
|
||||||
@ -215,16 +216,19 @@ export class NavControllerBase implements NavOutlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
getRouteId(): string | null {
|
getRouteId(): RouteID|null {
|
||||||
const element = this.getContentElement();
|
const active = this.getActive();
|
||||||
if (element) {
|
if (active) {
|
||||||
return element.tagName;
|
return {
|
||||||
|
id: active.element.tagName,
|
||||||
|
params: active.data
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
getContentElement(): HTMLElement {
|
getContainerEl(): HTMLElement {
|
||||||
const active = this.getActive();
|
const active = this.getActive();
|
||||||
if (active) {
|
if (active) {
|
||||||
return active.element;
|
return active.element;
|
||||||
|
@ -48,7 +48,7 @@ boolean
|
|||||||
#### getByIndex()
|
#### getByIndex()
|
||||||
|
|
||||||
|
|
||||||
#### getContentElement()
|
#### getContainerEl()
|
||||||
|
|
||||||
|
|
||||||
#### getPrevious()
|
#### getPrevious()
|
||||||
|
@ -52,16 +52,20 @@ export class Router {
|
|||||||
}
|
}
|
||||||
console.debug('[IN] nav changed -> update URL');
|
console.debug('[IN] nav changed -> update URL');
|
||||||
const { ids, pivot } = this.readNavState();
|
const { ids, pivot } = this.readNavState();
|
||||||
const { chain, matches } = routerIDsToChain(ids, this.routes);
|
const chain = routerIDsToChain(ids, this.routes);
|
||||||
if (chain.length > matches) {
|
if (chain) {
|
||||||
// readNavState() found a pivot that is not initialized
|
if (chain.length > ids.length) {
|
||||||
console.debug('[IN] pivot uninitialized -> write partial nav state');
|
// readNavState() found a pivot that is not initialized
|
||||||
this.writeNavState(pivot, chain.slice(matches), 0);
|
console.debug('[IN] pivot uninitialized -> write partial nav state');
|
||||||
}
|
this.writeNavState(pivot, chain.slice(ids.length), 0);
|
||||||
|
}
|
||||||
|
|
||||||
const isPop = ev.detail.isPop === true;
|
const isPop = ev.detail.isPop === true;
|
||||||
const path = chainToPath(chain);
|
const path = chainToPath(chain);
|
||||||
this.writePath(path, isPop);
|
this.writePath(path, isPop);
|
||||||
|
} else {
|
||||||
|
console.warn('no matching URL for ', ids.map(i => i.id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
@ -80,7 +84,7 @@ export class Router {
|
|||||||
}
|
}
|
||||||
const direction = window.history.state >= this.state ? 1 : -1;
|
const direction = window.history.state >= this.state ? 1 : -1;
|
||||||
const node = document.querySelector('ion-app');
|
const node = document.querySelector('ion-app');
|
||||||
const {chain} = routerPathToChain(currentPath, this.routes);
|
const chain = routerPathToChain(currentPath, this.routes);
|
||||||
return this.writeNavState(node, chain, direction);
|
return this.writeNavState(node, chain, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,14 +104,12 @@ export class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private writePath(path: string[], isPop: boolean) {
|
private writePath(path: string[], isPop: boolean) {
|
||||||
this.state = writePath(window.history, this.base, this.useHash, path, isPop, this.state);
|
// busyURL is used to prevent reentering in the popstate event
|
||||||
|
this.state++;
|
||||||
|
writePath(window.history, this.base, this.useHash, path, isPop, this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readPath(): string[] | null {
|
private readPath(): string[] | null {
|
||||||
return readPath(window.location, this.base, this.useHash);
|
return readPath(window.location, this.base, this.useHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
return <slot/>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { RouteChain } from '../utils/interfaces';
|
import { RouteChain, RouteID } from '../utils/interfaces';
|
||||||
import { routerIDsToChain, routerPathToChain } from '../utils/matching';
|
import { routerIDsToChain, routerPathToChain } from '../utils/matching';
|
||||||
import { mockRouteElement } from './parser.spec';
|
import { mockRouteElement } from './parser.spec';
|
||||||
import { chainToPath, generatePath, parsePath } from '../utils/path';
|
import { chainToPath, generatePath, parsePath } from '../utils/path';
|
||||||
@ -18,19 +18,37 @@ describe('ionic-conference-app', () => {
|
|||||||
expect(getRouteIDs('/about', routes)).toEqual(['page-tabs', 'page-about']);
|
expect(getRouteIDs('/about', routes)).toEqual(['page-tabs', 'page-about']);
|
||||||
expect(getRouteIDs('/tutorial', routes)).toEqual(['page-tutorial']);
|
expect(getRouteIDs('/tutorial', routes)).toEqual(['page-tutorial']);
|
||||||
|
|
||||||
expect(getRoutePaths(['page-tabs', 'tab-schedule', 'page-schedule'], routes)).toEqual('/');
|
expect(getRoutePath([
|
||||||
expect(getRoutePaths(['page-tabs', 'tab-speaker', 'page-speaker-list'], routes)).toEqual('/speaker');
|
{id: 'PAGE-TABS'},
|
||||||
expect(getRoutePaths(['page-tabs', 'page-map'], routes)).toEqual('/map');
|
{id: 'tab-schedule'},
|
||||||
expect(getRoutePaths(['page-tabs', 'page-about'], routes)).toEqual('/about');
|
{id: 'page-schedule'}], routes)).toEqual('/');
|
||||||
expect(getRoutePaths(['page-tutorial'], routes)).toEqual('/tutorial');
|
|
||||||
|
|
||||||
|
expect(getRoutePath([
|
||||||
|
{id: 'page-tabs'},
|
||||||
|
{id: 'TAB-SPEAKER'}], routes)).toEqual('/speaker');
|
||||||
|
|
||||||
|
expect(getRoutePath([
|
||||||
|
{id: 'page-tabs'},
|
||||||
|
{id: 'TAB-SPEAKER'},
|
||||||
|
{id: 'page-speaker-list'}], routes)).toEqual('/speaker');
|
||||||
|
|
||||||
|
expect(getRoutePath([
|
||||||
|
{id: 'page-tabs'},
|
||||||
|
{id: 'PAGE-MAP'}], routes)).toEqual('/map');
|
||||||
|
|
||||||
|
expect(getRoutePath([
|
||||||
|
{id: 'page-tabs'},
|
||||||
|
{id: 'page-about'}], routes)).toEqual('/about');
|
||||||
|
|
||||||
|
expect(getRoutePath([
|
||||||
|
{id: 'page-tutorial'}], routes)).toEqual('/tutorial');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function conferenceAppRouting() {
|
function conferenceAppRouting() {
|
||||||
const p2 = mockRouteElement('/', 'tab-schedule');
|
const p2 = mockRouteElement('/', 'tab-schedule');
|
||||||
const p3 = mockRouteElement('/', 'page-schedule');
|
const p3 = mockRouteElement('/', 'PAGE-SCHEDULE');
|
||||||
p2.appendChild(p3);
|
p2.appendChild(p3);
|
||||||
|
|
||||||
const p4 = mockRouteElement('/speaker', 'tab-speaker');
|
const p4 = mockRouteElement('/speaker', 'tab-speaker');
|
||||||
@ -56,10 +74,10 @@ function conferenceAppRouting() {
|
|||||||
|
|
||||||
|
|
||||||
function getRouteIDs(path: string, routes: RouteChain[]): string[] {
|
function getRouteIDs(path: string, routes: RouteChain[]): string[] {
|
||||||
return routerPathToChain(parsePath(path), routes).chain.map(r => r.id);
|
return routerPathToChain(parsePath(path), routes).map(r => r.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoutePaths(ids: string[], routes: RouteChain[]): string {
|
function getRoutePath(ids: RouteID[], routes: RouteChain[]): string {
|
||||||
return generatePath(chainToPath(routerIDsToChain(ids, routes).chain));
|
return generatePath(chainToPath(routerIDsToChain(ids, routes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,34 +132,12 @@ describe('routerPathToChain', () => {
|
|||||||
chain3,
|
chain3,
|
||||||
chain4
|
chain4
|
||||||
];
|
];
|
||||||
expect(routerPathToChain(['to'], routes)).toEqual({
|
expect(routerPathToChain(['to'], routes)).toEqual(null);
|
||||||
chain: null,
|
expect(routerPathToChain([''], routes)).toEqual(null);
|
||||||
matches: 0,
|
expect(routerPathToChain(['segment', 'to'], routes)).toEqual(null);
|
||||||
});
|
expect(routerPathToChain(['hola'], routes)).toEqual(chain3);
|
||||||
|
expect(routerPathToChain(['hola', 'hola'], routes)).toEqual(chain3);
|
||||||
expect(routerPathToChain([''], routes)).toEqual({
|
expect(routerPathToChain(['hola', 'adios'], routes)).toEqual(chain4);
|
||||||
chain: null,
|
|
||||||
matches: 0,
|
|
||||||
});
|
|
||||||
expect(routerPathToChain(['segment', 'to'], routes)).toEqual({
|
|
||||||
chain: null,
|
|
||||||
matches: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(routerPathToChain(['hola'], routes)).toEqual({
|
|
||||||
chain: chain3,
|
|
||||||
matches: 1,
|
|
||||||
});
|
|
||||||
expect(routerPathToChain(['hola', 'hola'], routes)).toEqual({
|
|
||||||
chain: chain3,
|
|
||||||
matches: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(routerPathToChain(['hola', 'adios'], routes)).toEqual({
|
|
||||||
chain: chain4,
|
|
||||||
matches: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match the default route', () => {
|
it('should match the default route', () => {
|
||||||
@ -174,11 +152,11 @@ describe('routerPathToChain', () => {
|
|||||||
{ id: 'page2', path: [''], params: undefined }
|
{ id: 'page2', path: [''], params: undefined }
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(routerPathToChain([''], [chain1])).toEqual({chain: chain1, matches: 3});
|
expect(routerPathToChain([''], [chain1])).toEqual(chain1);
|
||||||
expect(routerPathToChain(['tab2'], [chain1])).toEqual({chain: null, matches: 0});
|
expect(routerPathToChain(['tab2'], [chain1])).toEqual(null);
|
||||||
|
|
||||||
expect(routerPathToChain([''], [chain2])).toEqual({chain: null, matches: 0});
|
expect(routerPathToChain([''], [chain2])).toEqual(null);
|
||||||
expect(routerPathToChain(['tab2'], [chain2])).toEqual({chain: chain2, matches: 3});
|
expect(routerPathToChain(['tab2'], [chain2])).toEqual(chain2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { RouteTree } from '../utils/interfaces';
|
|||||||
describe('readRoutes', () => {
|
describe('readRoutes', () => {
|
||||||
it('should read URL', () => {
|
it('should read URL', () => {
|
||||||
const root = mockElement('div');
|
const root = mockElement('div');
|
||||||
const r1 = mockRouteElement('/', 'main-page');
|
const r1 = mockRouteElement('/', 'MAIN-PAGE');
|
||||||
const r2 = mockRouteElement('/one-page', 'one-page');
|
const r2 = mockRouteElement('/one-page', 'one-page');
|
||||||
const r3 = mockRouteElement('secondpage', 'second-page');
|
const r3 = mockRouteElement('secondpage', 'second-page');
|
||||||
const r4 = mockRouteElement('/5/hola', '4');
|
const r4 = mockRouteElement('/5/hola', '4');
|
||||||
@ -20,12 +20,12 @@ describe('readRoutes', () => {
|
|||||||
r4.appendChild(r6);
|
r4.appendChild(r6);
|
||||||
|
|
||||||
const expected: RouteTree = [
|
const expected: RouteTree = [
|
||||||
{ path: [''], id: 'main-page', children: [], props: undefined },
|
{ path: [''], id: 'main-page', children: [], params: undefined },
|
||||||
{ path: ['one-page'], id: 'one-page', children: [], props: undefined },
|
{ path: ['one-page'], id: 'one-page', children: [], params: undefined },
|
||||||
{ path: ['secondpage'], id: 'second-page', props: undefined, children: [
|
{ path: ['secondpage'], id: 'second-page', params: undefined, children: [
|
||||||
{ path: ['5', 'hola'], id: '4', props: undefined, children: [
|
{ path: ['5', 'hola'], id: '4', params: undefined, children: [
|
||||||
{ path: ['path', 'to', 'five'], id: '5', children: [], props: undefined },
|
{ path: ['path', 'to', 'five'], id: '5', children: [], params: undefined },
|
||||||
{ path: ['path', 'to', 'five2'], id: '6', children: [], props: undefined }
|
{ path: ['path', 'to', 'five2'], id: '6', children: [], params: undefined }
|
||||||
] }
|
] }
|
||||||
] }
|
] }
|
||||||
];
|
];
|
||||||
@ -36,12 +36,12 @@ describe('readRoutes', () => {
|
|||||||
describe('flattenRouterTree', () => {
|
describe('flattenRouterTree', () => {
|
||||||
it('should process routes', () => {
|
it('should process routes', () => {
|
||||||
const entries: RouteTree = [
|
const entries: RouteTree = [
|
||||||
{ path: [''], id: 'hola', children: [], props: undefined },
|
{ path: [''], id: 'hola', children: [], params: undefined },
|
||||||
{ path: ['one-page'], id: 'one-page', children: [], props: undefined },
|
{ path: ['one-page'], id: 'one-page', children: [], params: undefined },
|
||||||
{ path: ['secondpage'], id: 'second-page', props: undefined, children: [
|
{ path: ['secondpage'], id: 'second-page', params: undefined, children: [
|
||||||
{ path: ['5', 'hola'], id: '4', props: undefined, children: [
|
{ path: ['5', 'hola'], id: '4', params: undefined, children: [
|
||||||
{ path: ['path', 'to', 'five'], id: '5', children: [], props: undefined },
|
{ path: ['path', 'to', 'five'], id: '5', children: [], params: undefined },
|
||||||
{ path: ['path', 'to', 'five2'], id: '6', children: [], props: undefined }
|
{ path: ['path', 'to', 'five2'], id: '6', children: [], params: undefined }
|
||||||
] }
|
] }
|
||||||
] }
|
] }
|
||||||
];
|
];
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { generatePath, parsePath } from '../utils/path';
|
import { chainToPath, generatePath, parsePath } from '../utils/path';
|
||||||
|
import { RouteChain } from '../utils/interfaces';
|
||||||
|
|
||||||
describe('parseURL', () => {
|
describe('parseURL', () => {
|
||||||
it('should parse empty path', () => {
|
it('should parse empty path', () => {
|
||||||
@ -53,3 +54,39 @@ describe('generatePath', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('chainToPath', () => {
|
||||||
|
it('should generate a simple URL', () => {
|
||||||
|
const chain: RouteChain = [
|
||||||
|
{ id: '2', path: [''], params: undefined },
|
||||||
|
{ id: '1', path: [''], params: undefined },
|
||||||
|
{ id: '3', path: ['segment', 'to'], params: undefined },
|
||||||
|
{ id: '4', path: [''], params: undefined },
|
||||||
|
{ id: '5', path: ['hola', '', 'hey'], params: undefined },
|
||||||
|
{ id: '6', path: [''], params: undefined },
|
||||||
|
{ id: '7', path: [':param'], params: {param: 'name'} },
|
||||||
|
{ id: '8', path: ['adios', ':name', ':id'], params: {name: 'manu', id: '123'} },
|
||||||
|
];
|
||||||
|
expect(chainToPath(chain)).toEqual(
|
||||||
|
['segment', 'to', 'hola', 'hey', 'name', 'adios', 'manu', '123']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should raise an exception', () => {
|
||||||
|
const chain: RouteChain = [
|
||||||
|
{ id: '3', path: ['segment'], params: undefined },
|
||||||
|
{ id: '8', path: [':name'], params: undefined },
|
||||||
|
];
|
||||||
|
expect(() => chainToPath(chain)).toThrowError('missing param name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should raise an exception 2', () => {
|
||||||
|
const chain: RouteChain = [
|
||||||
|
{ id: '3', path: ['segment'], params: undefined },
|
||||||
|
{ id: '8', path: [':name', ':id'], params: {name: 'hey'} },
|
||||||
|
];
|
||||||
|
expect(() => chainToPath(chain)).toThrowError('missing param id');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
@ -6,13 +6,6 @@ export class RouterSegments {
|
|||||||
this.path = path.slice();
|
this.path = path.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
isDefault(): boolean {
|
|
||||||
if (this.path.length > 0) {
|
|
||||||
return this.path[0] === '';
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
next(): string {
|
next(): string {
|
||||||
if (this.path.length > 0) {
|
if (this.path.length > 0) {
|
||||||
return this.path.shift() as string;
|
return this.path.shift() as string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { breadthFirstSearch } from './common';
|
import { breadthFirstSearch } from './common';
|
||||||
import { NavOutlet, RouteChain } from './interfaces';
|
import { NavOutlet, RouteChain, RouteID } from './interfaces';
|
||||||
|
|
||||||
export function writeNavState(root: HTMLElement, chain: RouteChain|null, index: number, direction: number): Promise<void> {
|
export function writeNavState(root: HTMLElement, chain: RouteChain|null, index: number, direction: number): Promise<void> {
|
||||||
if (!chain || index >= chain.length) {
|
if (!chain || index >= chain.length) {
|
||||||
@ -16,7 +16,7 @@ export function writeNavState(root: HTMLElement, chain: RouteChain|null, index:
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
direction = 0;
|
direction = 0;
|
||||||
}
|
}
|
||||||
const nextEl = node.getContentElement();
|
const nextEl = node.getContainerEl();
|
||||||
const promise = (nextEl)
|
const promise = (nextEl)
|
||||||
? writeNavState(nextEl, chain, index + 1, direction)
|
? writeNavState(nextEl, chain, index + 1, direction)
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
@ -29,15 +29,15 @@ export function writeNavState(root: HTMLElement, chain: RouteChain|null, index:
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function readNavState(node: HTMLElement) {
|
export function readNavState(node: HTMLElement) {
|
||||||
const stack: string[] = [];
|
const ids: RouteID[] = [];
|
||||||
let pivot: NavOutlet|null;
|
let pivot: NavOutlet|null;
|
||||||
while (true) {
|
while (true) {
|
||||||
pivot = breadthFirstSearch(node);
|
pivot = breadthFirstSearch(node);
|
||||||
if (pivot) {
|
if (pivot) {
|
||||||
const cmp = pivot.getRouteId();
|
const id = pivot.getRouteId();
|
||||||
if (cmp) {
|
if (id) {
|
||||||
node = pivot.getContentElement();
|
node = pivot.getContainerEl();
|
||||||
stack.push(cmp.toLowerCase());
|
ids.push(id);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -45,8 +45,5 @@ export function readNavState(node: HTMLElement) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {ids, pivot};
|
||||||
ids: stack,
|
|
||||||
pivot: pivot,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
export interface NavOutlet {
|
export interface NavOutlet {
|
||||||
setRouteId(id: string, data: any, direction: number): Promise<boolean>;
|
setRouteId(id: string, data: any, direction: number): Promise<boolean>;
|
||||||
markVisible?(): Promise<void>;
|
markVisible?(): Promise<void>;
|
||||||
getRouteId(): string;
|
getRouteId(): RouteID|null;
|
||||||
|
|
||||||
getContentElement(): HTMLElement | null;
|
getContainerEl(): HTMLElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RouteMatch {
|
export interface RouteID {
|
||||||
chain: RouteChain;
|
id: string;
|
||||||
matches: number;
|
params?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NavOutletElement = NavOutlet & HTMLStencilElement;
|
export type NavOutletElement = NavOutlet & HTMLStencilElement;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { RouterSegments } from './common';
|
import { RouterSegments } from './common';
|
||||||
import { RouteChain, RouteMatch } from './interfaces';
|
import { RouteChain, RouteID } from './interfaces';
|
||||||
|
|
||||||
export function matchesIDs(ids: string[], chain: RouteChain): number {
|
export function matchesIDs(ids: string[], chain: RouteChain): number {
|
||||||
const len = Math.min(ids.length, chain.length);
|
const len = Math.min(ids.length, chain.length);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (; i < len; i++) {
|
for (; i < len; i++) {
|
||||||
if (ids[i] !== chain[i].id) {
|
if (ids[i].toLowerCase() !== chain[i].id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ export function matchesPath(path: string[], chain: RouteChain): RouteChain | nul
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const matches = (matchesDefault)
|
const matches = (matchesDefault)
|
||||||
? matchesDefault === segments.isDefault()
|
? matchesDefault === (segments.next() === '')
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
@ -71,24 +71,29 @@ export function mergeParams(a: any, b: any): any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function routerIDsToChain(ids: string[], chains: RouteChain[]): RouteMatch {
|
export function routerIDsToChain(ids: RouteID[], chains: RouteChain[]): RouteChain|null {
|
||||||
let match: RouteChain|null = null;
|
let match: RouteChain|null = null;
|
||||||
let maxMatches = 0;
|
let maxMatches = 0;
|
||||||
|
const plainIDs = ids.map(i => i.id);
|
||||||
for (const chain of chains) {
|
for (const chain of chains) {
|
||||||
const score = matchesIDs(ids, chain);
|
const score = matchesIDs(plainIDs, chain);
|
||||||
if (score > maxMatches) {
|
if (score > maxMatches) {
|
||||||
match = chain;
|
match = chain;
|
||||||
maxMatches = score;
|
maxMatches = score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
if (match) {
|
||||||
chain: match,
|
return match.map((route, i) => ({
|
||||||
matches: maxMatches
|
id: route.id,
|
||||||
};
|
path: route.path,
|
||||||
|
params: mergeParams(route.params, ids[i] && ids[i].params)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function routerPathToChain(path: string[], chains: RouteChain[]): RouteMatch|null {
|
export function routerPathToChain(path: string[], chains: RouteChain[]): RouteChain|null {
|
||||||
let match: RouteChain = null;
|
let match: RouteChain = null;
|
||||||
let matches = 0;
|
let matches = 0;
|
||||||
for (const chain of chains) {
|
for (const chain of chains) {
|
||||||
@ -100,8 +105,5 @@ export function routerPathToChain(path: string[], chains: RouteChain[]): RouteMa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return match;
|
||||||
chain: match,
|
|
||||||
matches,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ export function readRoutes(root: Element): RouteTree {
|
|||||||
.filter(el => el.tagName === 'ION-ROUTE')
|
.filter(el => el.tagName === 'ION-ROUTE')
|
||||||
.map(el => ({
|
.map(el => ({
|
||||||
path: parsePath(readProp(el, 'path')),
|
path: parsePath(readProp(el, 'path')),
|
||||||
id: readProp(el, 'component'),
|
id: readProp(el, 'component').toLowerCase(),
|
||||||
params: el.params,
|
params: el.params,
|
||||||
children: readRoutes(el)
|
children: readRoutes(el)
|
||||||
}));
|
}));
|
||||||
|
@ -11,8 +11,16 @@ export function generatePath(segments: string[]): string {
|
|||||||
export function chainToPath(chain: RouteChain): string[] {
|
export function chainToPath(chain: RouteChain): string[] {
|
||||||
const path = [];
|
const path = [];
|
||||||
for (const route of chain) {
|
for (const route of chain) {
|
||||||
if (route.path[0] !== '') {
|
for (const segment of route.path) {
|
||||||
path.push(...route.path);
|
if (segment[0] === ':') {
|
||||||
|
const param = route.params && route.params[segment.slice(1)];
|
||||||
|
if (!param) {
|
||||||
|
throw new Error(`missing param ${segment.slice(1)}`);
|
||||||
|
}
|
||||||
|
path.push(param);
|
||||||
|
} else if (segment !== '') {
|
||||||
|
path.push(segment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
@ -24,14 +32,12 @@ export function writePath(history: History, base: string, usePath: boolean, path
|
|||||||
if (usePath) {
|
if (usePath) {
|
||||||
url = '#' + url;
|
url = '#' + url;
|
||||||
}
|
}
|
||||||
state++;
|
|
||||||
if (isPop) {
|
if (isPop) {
|
||||||
history.back();
|
// history.back();
|
||||||
history.replaceState(state, null, url);
|
history.replaceState(state, null, url);
|
||||||
} else {
|
} else {
|
||||||
history.pushState(state, null, url);
|
history.pushState(state, null, url);
|
||||||
}
|
}
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readPath(loc: Location, base: string, useHash: boolean): string[] | null {
|
export function readPath(loc: Location, base: string, useHash: boolean): string[] | null {
|
||||||
|
@ -235,7 +235,7 @@ Emitted when the current tab is selected.
|
|||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
#### getRouteId()
|
#### getTabId()
|
||||||
|
|
||||||
|
|
||||||
#### setActive()
|
#### setActive()
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core';
|
||||||
import { asyncRaf } from '../../utils/helpers';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-tab',
|
tag: 'ion-tab',
|
||||||
@ -86,7 +84,7 @@ export class Tab {
|
|||||||
@Event() ionSelect: EventEmitter<void>;
|
@Event() ionSelect: EventEmitter<void>;
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
getRouteId(): string|null {
|
getTabId(): string|null {
|
||||||
if (this.name) {
|
if (this.name) {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
@ -104,7 +102,7 @@ export class Tab {
|
|||||||
private prepareLazyLoaded(): Promise<any> {
|
private prepareLazyLoaded(): Promise<any> {
|
||||||
if (!this.loaded && this.component) {
|
if (!this.loaded && this.component) {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
return attachViewToDom(this.el, this.component).then(() => asyncRaf());
|
return attachViewToDom(this.el, this.component);
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ Emitted when the tab changes.
|
|||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
#### getContentElement()
|
#### getContainerEl()
|
||||||
|
|
||||||
|
|
||||||
#### getRouteId()
|
#### getRouteId()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
||||||
import { Config, NavOutlet } from '../../index';
|
import { Config, NavOutlet } from '../../index';
|
||||||
|
import { RouteID } from '../router/utils/interfaces';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -101,7 +102,7 @@ export class Tabs implements NavOutlet {
|
|||||||
@Method()
|
@Method()
|
||||||
getTab(tabOrIndex: string | number | HTMLIonTabElement): HTMLIonTabElement {
|
getTab(tabOrIndex: string | number | HTMLIonTabElement): HTMLIonTabElement {
|
||||||
if (typeof tabOrIndex === 'string') {
|
if (typeof tabOrIndex === 'string') {
|
||||||
return this.tabs.find(tab => tab.getRouteId() === tabOrIndex);
|
return this.tabs.find(tab => tab.getTabId() === tabOrIndex);
|
||||||
}
|
}
|
||||||
if (typeof tabOrIndex === 'number') {
|
if (typeof tabOrIndex === 'number') {
|
||||||
return this.tabs[tabOrIndex];
|
return this.tabs[tabOrIndex];
|
||||||
@ -136,16 +137,14 @@ export class Tabs implements NavOutlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
getRouteId(): string|null {
|
getRouteId(): RouteID|null {
|
||||||
if (this.selectedTab) {
|
const id = this.selectedTab && this.selectedTab.getTabId();
|
||||||
return this.selectedTab.getRouteId();
|
return id ? {id} : null;
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
getContentElement(): HTMLElement {
|
getContainerEl(): HTMLElement {
|
||||||
return this.routingView || this.selectedTab;
|
return this.routingView || this.selectedTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user