]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: add beforeEach
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 18 Apr 2019 16:53:47 +0000 (18:53 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 18 Apr 2019 16:53:47 +0000 (18:53 +0200)
explorations/html5.ts
src/history/base.ts
src/history/html5.ts
src/router.ts
src/types/index.ts

index eb03b4917321d23e7070e246ae39c92312b90749..c4d0a9a2b03cdb52a472ea268ab58c53a6da6fef 100644 (file)
@@ -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()
index ce84bcdca8c91e148de697aca369d432a75a1e1b..b61cd720a1fe0bc5238aa0676579c6190b6eea5a 100644 (file)
@@ -1,4 +1,5 @@
 import * as utils from './utils'
+import { ListenerRemover } from '../types'
 
 export type HistoryQuery = Record<string, string | string[]>
 
@@ -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
index e19d3fdbe2f35de61daaf5a668d8efd4161554eb..44ee80ea9668aecdc0c1d144618d0e5dcf17c532 100644 (file)
@@ -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)
index 342e5a26ac7a240f7856f8bc4cffdf93cb116dd4..4e68088761dd5c36ffc7012acb27d272369092d9 100644 (file)
@@ -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<RouteLocationNormalized> = 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> {
+    // TODO: Will probably need to be some kind of queue in the future that allows to remove
+    // elements and other stuff
+    const guards: Promise<any>[] = []
+
+    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)
+    }
+  }
 }
index 7aa0ef4e1ea41a232d973c8958839003c2876a65..e43c8efa6e6e118b2a9a3ad9186875b7f0fb4069 100644 (file)
@@ -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<string, string | string[]>
@@ -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
+}