mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(router): add willChange event
This commit is contained in:
3
core/src/components.d.ts
vendored
3
core/src/components.d.ts
vendored
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user