feat(router): add willChange event

This commit is contained in:
Manu Mtz.-Almeida
2018-04-30 18:49:22 +02:00
parent c905ba4611
commit d613411287
5 changed files with 62 additions and 33 deletions

View File

@@ -5163,7 +5163,8 @@ declare global {
}
namespace JSXElements {
export interface IonRouterAttributes extends HTMLAttributes {
'onIonRouteChanged'?: (event: CustomEvent<RouterEventDetail>) => void;
'onIonRouteDidChange'?: (event: CustomEvent<RouterEventDetail>) => void;
'onIonRouteWillChange'?: (event: CustomEvent<RouterEventDetail>) => void;
/**
* By default `ion-router` will match the routes at the root path ("/"). That can be changed when T
*/

View File

@@ -172,7 +172,10 @@ export class Nav implements NavOutlet {
setRouteId(id: string, params: any, direction: number): Promise<RouteWrite> {
const active = this.getActive();
if (matches(active, id, params)) {
return Promise.resolve({changed: false, element: active.element});
return Promise.resolve({
changed: false,
element: active.element
});
}
const viewController = this.views.find(v => matches(v, id, params));

View File

@@ -25,7 +25,6 @@ export class Router {
@Prop({ context: 'config' }) config!: Config;
@Prop({ context: 'queue' }) queue!: QueueController;
@Prop({ context: 'window' }) win!: Window;
@Prop({ context: 'isServer' }) isServer!: boolean;
/**
* By default `ion-router` will match the routes at the root path ("/").
@@ -51,7 +50,8 @@ export class Router {
*/
@Prop() useHash = true;
@Event() ionRouteChanged!: EventEmitter<RouterEventDetail>;
@Event() ionRouteWillChange!: EventEmitter<RouterEventDetail>;
@Event() ionRouteDidChange!: EventEmitter<RouterEventDetail>;
async componentWillLoad() {
console.debug('[ion-router] router will load');
@@ -150,11 +150,8 @@ export class Router {
console.debug('[ion-router] nav changed -> update URL', ids, path);
this.setPath(path, direction);
if (outlet) {
console.debug('[ion-router] updating nested outlet', outlet);
await this.writeNavState(outlet, chain, RouterDirection.None, ids.length);
}
this.emitRouteChange(path, null);
await this.writeNavState(outlet, chain, RouterDirection.None, path, null, ids.length);
return true;
}
@@ -175,6 +172,8 @@ export class Router {
console.error('[ion-router] URL is not part of the routing set');
return false;
}
// lookup redirect rule
const redirect = routeRedirect(path, this.redirects);
let redirectFrom: string[]|null = null;
if (redirect) {
@@ -182,21 +181,38 @@ export class Router {
redirectFrom = redirect.from;
path = redirect.to!;
}
// lookup route chain
const chain = routerPathToChain(path, this.routes);
const changed = await this.writeNavState(this.win.document.body, chain, direction);
if (changed) {
this.emitRouteChange(path, redirectFrom);
if (!chain) {
console.error('[ion-router] the path does not match any route');
return false;
}
return changed;
// write DOM give
return this.writeNavState(this.win.document.body, chain, direction, path, redirectFrom);
}
private async writeNavState(node: any, chain: RouteChain | null, direction: RouterDirection, index = 0): Promise<boolean> {
private async writeNavState(
node: HTMLElement|undefined, chain: RouteChain, direction: RouterDirection,
path: string[], redirectFrom: string[] | null,
index = 0
): Promise<boolean> {
if (this.busy) {
return false;
}
this.busy = true;
// generate route event and emit will change
const event = this.routeChangeEvent(path, redirectFrom);
this.ionRouteWillChange.emit(event);
const changed = await writeNavState(node, chain, direction, index);
this.busy = false;
// emit did change
this.emitRouteDidChange(event, changed);
return changed;
}
@@ -209,16 +225,23 @@ export class Router {
return readPath(this.win.location, this.root, this.useHash);
}
private emitRouteChange(path: string[], redirectPath: string[]|null) {
console.debug('[ion-router] route changed', path);
const from = this.previousPath;
const redirectedFrom = redirectPath ? generatePath(redirectPath) : null;
const to = generatePath(path);
this.previousPath = to;
this.ionRouteChanged.emit({
from,
redirectedFrom,
to: to
private emitRouteDidChange(event: RouterEventDetail, changed: boolean) {
console.debug('[ion-router] route changed', event.to);
this.previousPath = event.to;
this.ionRouteDidChange.emit({
...event,
changed
});
}
private routeChangeEvent(path: string[], redirectFromPath: string[]|null) {
const from = this.previousPath;
const to = generatePath(path);
const redirectedFrom = redirectFromPath ? generatePath(redirectFromPath) : null;
return {
from,
redirectedFrom,
to,
};
}
}

View File

@@ -1,12 +1,12 @@
import { NavOutlet, NavOutletElement, RouteChain, RouteID, RouterDirection } from './interface';
import { NavOutletElement, RouteChain, RouteID, RouterDirection } from './interface';
export async function writeNavState(root: HTMLElement|undefined, chain: RouteChain|null, direction: RouterDirection, index: number, changed = false): Promise<boolean> {
export async function writeNavState(root: HTMLElement | undefined, chain: RouteChain, direction: RouterDirection, index: number, changed = false): Promise<boolean> {
try {
// find next navigation outlet in the DOM
const outlet = searchNavNode(root);
// make sure we can continue interating the DOM, otherwise abort
if (!chain || index >= chain.length || !outlet) {
if (index >= chain.length || !outlet) {
return changed;
}
await outlet.componentOnReady();
@@ -38,8 +38,8 @@ export async function writeNavState(root: HTMLElement|undefined, chain: RouteCha
export function readNavState(root: HTMLElement | undefined) {
const ids: RouteID[] = [];
let outlet: NavOutlet|null;
let node: HTMLElement|undefined = root;
let outlet: NavOutletElement | undefined;
let node: HTMLElement | undefined = root;
while (true) {
outlet = searchNavNode(node);
if (outlet) {
@@ -69,12 +69,13 @@ export function waitUntilNavNode(win: Window) {
const QUERY = ':not([no-router]) ion-nav, :not([no-router]) ion-tabs, :not([no-router]) ion-router-outlet';
function searchNavNode(root: HTMLElement|undefined): NavOutletElement|null {
function searchNavNode(root: HTMLElement | undefined): NavOutletElement | undefined {
if (!root) {
return null;
return undefined;
}
if (root.matches(QUERY)) {
return root as NavOutletElement;
}
return root.querySelector(QUERY);
const outlet = root.querySelector<NavOutletElement>(QUERY);
return outlet ? outlet : undefined;
}

View File

@@ -8,6 +8,7 @@ export interface RouterEventDetail {
from: string|null;
redirectedFrom: string|null;
to: string;
changed?: boolean;
}
export const enum RouterDirection {
@@ -29,7 +30,7 @@ export interface RouteWrite {
export interface RouteID {
id: string;
element?: HTMLElement|undefined;
element: HTMLElement | undefined;
params?: any;
}