fix(router): route information is stateless

This commit is contained in:
Manu Mtz.-Almeida
2018-05-08 21:48:04 +02:00
parent ba551fda01
commit 0f8477dc34
4 changed files with 28 additions and 37 deletions

View File

@ -3,9 +3,9 @@ import { Config, QueueController } from '../../interface';
import { debounce } from '../../utils/helpers'; import { debounce } from '../../utils/helpers';
import { printRedirects, printRoutes } from './utils/debug'; import { printRedirects, printRoutes } from './utils/debug';
import { readNavState, waitUntilNavNode, writeNavState } from './utils/dom'; import { readNavState, waitUntilNavNode, writeNavState } from './utils/dom';
import { RouteChain, RouteRedirect, RouterDirection, RouterEventDetail } from './utils/interface'; import { RouteChain, RouterDirection, RouterEventDetail } from './utils/interface';
import { routeRedirect, routerIDsToChain, routerPathToChain } from './utils/matching'; import { routeRedirect, routerIDsToChain, routerPathToChain } from './utils/matching';
import { flattenRouterTree, readRedirects, readRoutes } from './utils/parser'; import { readRedirects, readRoutes } from './utils/parser';
import { chainToPath, generatePath, parsePath, readPath, writePath } from './utils/path'; import { chainToPath, generatePath, parsePath, readPath, writePath } from './utils/path';
@ -14,9 +14,7 @@ import { chainToPath, generatePath, parsePath, readPath, writePath } from './uti
}) })
export class Router { export class Router {
private routes: RouteChain[] = [];
private previousPath: string|null = null; private previousPath: string|null = null;
private redirects: RouteRedirect[] = [];
private busy = false; private busy = false;
private state = 0; private state = 0;
private lastState = 0; private lastState = 0;
@ -59,17 +57,11 @@ export class Router {
await waitUntilNavNode(this.win); await waitUntilNavNode(this.win);
console.debug('[ion-router] found nav'); console.debug('[ion-router] found nav');
const tree = readRoutes(this.el); await this.onRoutesChanged();
this.routes = flattenRouterTree(tree);
this.redirects = readRedirects(this.el);
this.win.addEventListener('ionRouteRedirectChanged', debounce(this.onRedirectChanged.bind(this), 10)); this.win.addEventListener('ionRouteRedirectChanged', debounce(this.onRedirectChanged.bind(this), 10));
this.win.addEventListener('ionRouteDataChanged', debounce(this.onRoutesChanged.bind(this), 100)); this.win.addEventListener('ionRouteDataChanged', debounce(this.onRoutesChanged.bind(this), 100));
this.onRedirectChanged();
const changed = await this.writeNavStateRoot(this.getPath(), RouterDirection.None);
if (!changed) {
console.error('[ion-router] did not change on will load');
}
} }
@Listen('window:popstate') @Listen('window:popstate')
@ -81,17 +73,14 @@ export class Router {
} }
private onRedirectChanged() { private onRedirectChanged() {
this.redirects = readRedirects(this.el);
const path = this.getPath(); const path = this.getPath();
if (path && routeRedirect(path, this.redirects)) { if (path && routeRedirect(path, readRedirects(this.el))) {
this.writeNavStateRoot(path, RouterDirection.None); this.writeNavStateRoot(path, RouterDirection.None);
} }
} }
private onRoutesChanged() { private onRoutesChanged() {
const tree = readRoutes(this.el); return this.writeNavStateRoot(this.getPath(), RouterDirection.None);
this.routes = flattenRouterTree(tree);
this.writeNavStateRoot(this.getPath(), RouterDirection.None);
} }
private historyDirection() { private historyDirection() {
@ -117,8 +106,8 @@ export class Router {
printDebug() { printDebug() {
console.debug('CURRENT PATH', this.getPath()); console.debug('CURRENT PATH', this.getPath());
console.debug('PREVIOUS PATH', this.previousPath); console.debug('PREVIOUS PATH', this.previousPath);
printRoutes(this.routes); printRoutes(readRoutes(this.el));
printRedirects(this.redirects); printRedirects(readRedirects(this.el));
} }
@Method() @Method()
@ -127,7 +116,8 @@ export class Router {
return false; return false;
} }
const { ids, outlet } = readNavState(this.win.document.body); const { ids, outlet } = readNavState(this.win.document.body);
const chain = routerIDsToChain(ids, this.routes); const routes = readRoutes(this.el);
const chain = routerIDsToChain(ids, routes);
if (!chain) { if (!chain) {
console.warn('[ion-router] no matching URL for ', ids.map(i => i.id)); console.warn('[ion-router] no matching URL for ', ids.map(i => i.id));
return false; return false;
@ -149,9 +139,9 @@ export class Router {
@Method() @Method()
push(url: string, direction = RouterDirection.Forward) { push(url: string, direction = RouterDirection.Forward) {
const path = parsePath(url); const path = parsePath(url);
this.setPath(path, direction);
console.debug('[ion-router] URL pushed -> updating nav', url, direction); console.debug('[ion-router] URL pushed -> updating nav', url, direction);
this.setPath(path, direction);
return this.writeNavStateRoot(path, direction); return this.writeNavStateRoot(path, direction);
} }
@ -165,16 +155,18 @@ export class Router {
} }
// lookup redirect rule // lookup redirect rule
const redirect = routeRedirect(path, this.redirects); const redirects = readRedirects(this.el);
const redirect = routeRedirect(path, redirects);
let redirectFrom: string[]|null = null; let redirectFrom: string[]|null = null;
if (redirect) { if (redirect) {
this.setPath(redirect.to!, direction); this.setPath(redirect.to, direction);
redirectFrom = redirect.from; redirectFrom = redirect.from;
path = redirect.to!; path = redirect.to;
} }
// lookup route chain // lookup route chain
const chain = routerPathToChain(path, this.routes); const routes = readRoutes(this.el);
const chain = routerPathToChain(path, routes);
if (!chain) { if (!chain) {
console.error('[ion-router] the path does not match any route'); console.error('[ion-router] the path does not match any route');
return false; return false;

View File

@ -19,7 +19,7 @@ export const enum RouterDirection {
export interface RouteRedirect { export interface RouteRedirect {
from: string[]; from: string[];
to: string[]|undefined; to?: string[];
} }
export interface RouteWrite { export interface RouteWrite {

View File

@ -1,7 +1,7 @@
import { RouteChain, RouteID, RouteRedirect } from './interface'; import { RouteChain, RouteID, RouteRedirect } from './interface';
export function matchesRedirect(input: string[], route: RouteRedirect): boolean { export function matchesRedirect(input: string[], route: RouteRedirect): route is Required<RouteRedirect> {
const {from, to} = route; const {from, to} = route;
if (to === undefined) { if (to === undefined) {
return false; return false;
@ -23,13 +23,8 @@ export function matchesRedirect(input: string[], route: RouteRedirect): boolean
return from.length === input.length; return from.length === input.length;
} }
export function routeRedirect(path: string[], routes: RouteRedirect[]): RouteRedirect|null { export function routeRedirect(path: string[], routes: RouteRedirect[]) {
for (const route of routes) { return routes.find(route => matchesRedirect(path, route)) as Required<RouteRedirect> | undefined;
if (matchesRedirect(path, route)) {
return route;
}
}
return null;
} }

View File

@ -14,7 +14,11 @@ export function readRedirects(root: Element): RouteRedirect[] {
}); });
} }
export function readRoutes(root: Element, node = root): RouteTree { export function readRoutes(root: Element): RouteChain[] {
return flattenRouterTree(readRouteNodes(root));
}
export function readRouteNodes(root: Element, node = root): RouteTree {
return (Array.from(node.children) as HTMLIonRouteElement[]) return (Array.from(node.children) as HTMLIonRouteElement[])
.filter(el => el.tagName === 'ION-ROUTE' && el.component) .filter(el => el.tagName === 'ION-ROUTE' && el.component)
.map(el => { .map(el => {
@ -26,7 +30,7 @@ export function readRoutes(root: Element, node = root): RouteTree {
path: parsePath(readProp(el, 'url')), path: parsePath(readProp(el, 'url')),
id: component.toLowerCase(), id: component.toLowerCase(),
params: el.componentProps, params: el.componentProps,
children: readRoutes(root, el) children: readRouteNodes(root, el)
}; };
}); });
} }