From: Eduardo San Martin Morote Date: Thu, 18 Apr 2019 16:53:47 +0000 (+0200) Subject: feat: add beforeEach X-Git-Tag: v4.0.0-alpha.0~432 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18e615bc025c318869feb79c1661b78132b0583f;p=thirdparty%2Fvuejs%2Frouter.git feat: add beforeEach --- diff --git a/explorations/html5.ts b/explorations/html5.ts index eb03b491..c4d0a9a2 100644 --- a/explorations/html5.ts +++ b/explorations/html5.ts @@ -12,6 +12,12 @@ const r = new Router({ ], }) +r.beforeEach((to, from, next) => { + console.log(`Guard from ${from.fullPath} to ${to.fullPath}`) + if (to.params.id === 'no-name') return next(false) + next() +}) + // const h = new HTML5History() // @ts-ignore const h = r.history @@ -23,40 +29,47 @@ window.r = r h.listen((to, from, { type }) => { console.log(`popstate(${type})`, { to, from }) }) +async function run() { + // r.push('/multiple/one/two') -// r.push('/multiple/one/two') + // h.push('/hey') + // h.push('/hey?lol') + // h.push('/foo') + // h.push('/replace-me') + // h.replace('/bar') -// h.push('/hey') -// h.push('/hey?lol') -// h.push('/foo') -// h.push('/replace-me') -// h.replace('/bar') + // r.push('/about') + await r.push({ + path: '/', + }) -// r.push('/about') -r.push({ - path: '/', -}) + await r.push({ + name: 'user', + params: { + id: '6', + }, + }) -r.push({ - name: 'user', - params: { - id: '6', - }, -}) + await r.push({ + name: 'user', + params: { + id: '5', + }, + }) -r.push({ - name: 'user', - params: { - id: '5', - }, -}) + try { + await r.push({ + params: { + id: 'no-name', + }, + }) + } catch (err) { + console.log('Navigation aborted', err) + } -r.push({ - params: { - id: 'no-name', - }, -}) + await r.push({ + hash: '#hey', + }) +} -r.push({ - hash: '#hey', -}) +run() diff --git a/src/history/base.ts b/src/history/base.ts index ce84bcdc..b61cd720 100644 --- a/src/history/base.ts +++ b/src/history/base.ts @@ -1,4 +1,5 @@ import * as utils from './utils' +import { ListenerRemover } from '../types' export type HistoryQuery = Record @@ -52,8 +53,6 @@ export interface NavigationCallback { ): void } -export type RemoveListener = () => void - export abstract class BaseHistory { // previousState: object location: HistoryLocationNormalized = START @@ -81,7 +80,7 @@ export abstract class BaseHistory { * @param callback callback to be called whenever the route changes * @returns */ - abstract listen(callback: NavigationCallback): RemoveListener + abstract listen(callback: NavigationCallback): ListenerRemover /** * ensure the current location matches the external source diff --git a/src/history/html5.ts b/src/history/html5.ts index e19d3fdb..44ee80ea 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -125,7 +125,10 @@ export class HTML5History extends BaseHistory { // call all listeners const navigationInfo = { type: - from === state.forward ? NavigationType.back : NavigationType.forward, + // TODO: we should save somekind of id to detect the navigation type + state.forward && from.fullPath === state.forward.fullPath + ? NavigationType.back + : NavigationType.forward, } this._listeners.forEach(listener => listener(this.location, from, navigationInfo) diff --git a/src/router.ts b/src/router.ts index 342e5a26..4e680887 100644 --- a/src/router.ts +++ b/src/router.ts @@ -6,9 +6,13 @@ import { START_LOCATION_NORMALIZED, RouteLocationNormalized, MatcherLocationNormalized, + ListenerRemover, + NavigationGuard, + TODO, + NavigationGuardCallback, } from './types/index' -interface RouterOptions { +export interface RouterOptions { history: BaseHistory routes: RouteRecord[] } @@ -16,6 +20,7 @@ interface RouterOptions { export class Router { protected history: BaseHistory private matcher: RouterMatcher + private beforeGuards: NavigationGuard[] = [] currentRoute: Readonly = START_LOCATION_NORMALIZED constructor(options: RouterOptions) { @@ -41,7 +46,7 @@ export class Router { * Trigger a navigation, should resolve all guards first * @param to Where to go */ - push(to: RouteLocation) { + async push(to: RouteLocation) { let url: HistoryLocationNormalized let location: MatcherLocationNormalized if (typeof to === 'string' || 'path' in to) { @@ -59,13 +64,46 @@ export class Router { }) } - // TODO: call hooks, guards + // TODO: refactor in a function, some kind of queue + const toLocation: RouteLocationNormalized = { ...url, ...location } + await this.navigate(toLocation, this.currentRoute) this.history.push(url) - this.currentRoute = { - ...url, - ...location, + this.currentRoute = toLocation + } + + async navigate( + to: RouteLocationNormalized, + from: RouteLocationNormalized + ): Promise { + // TODO: Will probably need to be some kind of queue in the future that allows to remove + // elements and other stuff + const guards: Promise[] = [] + + for (const guard of this.beforeGuards) { + guards.push( + new Promise((resolve, reject) => { + const next: NavigationGuardCallback = (valid?: boolean) => { + if (valid === false) reject(new Error('Aborted')) + else resolve() + } + + guard(to, from, next) + }) + ) + } + + console.log('Guarding against', guards.length, 'guards') + for (const guard of guards) { + await guard } } getRouteRecord(location: RouteLocation) {} + + beforeEach(guard: NavigationGuard): ListenerRemover { + this.beforeGuards.push(guard) + return () => { + this.beforeGuards.splice(this.beforeGuards.indexOf(guard), 1) + } + } } diff --git a/src/types/index.ts b/src/types/index.ts index 7aa0ef4e..e43c8efa 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,8 @@ import { HistoryQuery } from '../history/base' -type TODO = any +export type TODO = any + +export type ListenerRemover = () => void // TODO: support numbers for easier writing but cast them export type RouteParams = Record @@ -89,3 +91,15 @@ export interface MatcherLocationNormalized { // record? params: RouteLocationNormalized['params'] } + +export interface NavigationGuardCallback { + (): void + (valid: false): void +} +export interface NavigationGuard { + ( + to: RouteLocationNormalized, + from: RouteLocationNormalized, + next: NavigationGuardCallback + ): any +}