]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix(guards): run beforeRouteEnter with app context (#2117)
authorGennadii Fairushin <yozi-developer@yandex.ru>
Mon, 29 Jan 2024 08:56:02 +0000 (08:56 +0000)
committerGitHub <noreply@github.com>
Mon, 29 Jan 2024 08:56:02 +0000 (09:56 +0100)
Fix vuejs/router#2051

packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts
packages/router/src/navigationGuards.ts
packages/router/src/router.ts

index a660b1c12c7c0b5ead462ec9f2578356448bcb0e..fe20236bec3f633f375ab06a33ff2d5bd7149e2e 100644 (file)
@@ -48,4 +48,92 @@ describe('inject() within navigation guards', () => {
       await router.isReady()
     })
   }
+
+  describe('in-component guards', () => {
+    it('beforeRouteEnter', async () => {
+      expect.assertions(1)
+      const router = createRouter({
+        routes: [
+          {
+            path: '/',
+            component: {
+              template: `<div>Page</div>`,
+              beforeRouteEnter() {
+                expect(inject('test')).toBe('hello')
+              },
+            },
+          },
+        ],
+      })
+      factory(router)
+      await router.isReady()
+      await router.push('/')
+    })
+
+    it('beforeRouteEnter + lazy load', async () => {
+      expect.assertions(1)
+      const router = createRouter({
+        routes: [
+          {
+            path: '/',
+            component: () =>
+              new Promise(r =>
+                r({
+                  template: `<div>Page</div>`,
+                  beforeRouteEnter() {
+                    expect(inject('test')).toBe('hello')
+                  },
+                })
+              ),
+          },
+        ],
+      })
+      factory(router)
+      await router.isReady()
+      await router.push('/')
+    })
+
+    it('beforeRouteUpdate', async () => {
+      expect.assertions(1)
+      const router = createRouter({
+        routes: [
+          {
+            path: '/',
+            component: {
+              template: `<div>Page</div>`,
+              beforeRouteUpdate() {
+                expect(inject('test')).toBe('hello')
+              },
+            },
+          },
+        ],
+      })
+      factory(router)
+      await router.isReady()
+      await router.push('/')
+      await router.push('/#other')
+    })
+
+    it('beforeRouteLeave', async () => {
+      expect.assertions(1)
+      const router = createRouter({
+        routes: [
+          { path: '/', component: PageComponent },
+          {
+            path: '/foo',
+            component: {
+              template: `<div>Page</div>`,
+              beforeRouteLeave() {
+                expect(inject('test')).toBe('hello')
+              },
+            },
+          },
+        ],
+      })
+      factory(router)
+      await router.isReady()
+      await router.push('/foo')
+      await router.push('/')
+    })
+  })
 })
index 148a51f277da0cb146b7ff5b26877fb09635a214..27eac6cfc938bfbd05aafe20c2f9097869143ed9 100644 (file)
@@ -117,14 +117,16 @@ export function guardToPromiseFn(
   to: RouteLocationNormalized,
   from: RouteLocationNormalizedLoaded,
   record: RouteRecordNormalized,
-  name: string
+  name: string,
+  runWithContext: <T>(fn: () => T) => T
 ): () => Promise<void>
 export function guardToPromiseFn(
   guard: NavigationGuard,
   to: RouteLocationNormalized,
   from: RouteLocationNormalizedLoaded,
   record?: RouteRecordNormalized,
-  name?: string
+  name?: string,
+  runWithContext: <T>(fn: () => T) => T = fn => fn()
 ): () => Promise<void> {
   // keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place
   const enterCallbackArray =
@@ -173,11 +175,13 @@ export function guardToPromiseFn(
       }
 
       // wrapping with Promise.resolve allows it to work with both async and sync guards
-      const guardReturn = guard.call(
-        record && record.instances[name!],
-        to,
-        from,
-        __DEV__ ? canOnlyBeCalledOnce(next, to, from) : next
+      const guardReturn = runWithContext(() =>
+        guard.call(
+          record && record.instances[name!],
+          to,
+          from,
+          __DEV__ ? canOnlyBeCalledOnce(next, to, from) : next
+        )
       )
       let guardCall = Promise.resolve(guardReturn)
 
@@ -231,7 +235,8 @@ export function extractComponentsGuards(
   matched: RouteRecordNormalized[],
   guardType: GuardType,
   to: RouteLocationNormalized,
-  from: RouteLocationNormalizedLoaded
+  from: RouteLocationNormalizedLoaded,
+  runWithContext: <T>(fn: () => T) => T = fn => fn()
 ) {
   const guards: Array<() => Promise<void>> = []
 
@@ -292,7 +297,10 @@ export function extractComponentsGuards(
         const options: ComponentOptions =
           (rawComponent as any).__vccOpts || rawComponent
         const guard = options[guardType]
-        guard && guards.push(guardToPromiseFn(guard, to, from, record, name))
+        guard &&
+          guards.push(
+            guardToPromiseFn(guard, to, from, record, name, runWithContext)
+          )
       } else {
         // start requesting the chunk already
         let componentPromise: Promise<
@@ -324,7 +332,10 @@ export function extractComponentsGuards(
             const options: ComponentOptions =
               (resolvedComponent as any).__vccOpts || resolvedComponent
             const guard = options[guardType]
-            return guard && guardToPromiseFn(guard, to, from, record, name)()
+            return (
+              guard &&
+              guardToPromiseFn(guard, to, from, record, name, runWithContext)()
+            )
           })
         )
       }
index f5cc271eefe00379b15f481c179ed6956beb1a53..b1ec39dfd3fa7e65fef336bc32aa1cea5274ea59 100644 (file)
@@ -879,7 +879,8 @@ export function createRouter(options: RouterOptions): Router {
             enteringRecords,
             'beforeRouteEnter',
             to,
-            from
+            from,
+            runWithContext
           )
           guards.push(canceledNavigationCheck)