--- /dev/null
+/**
+ * @jest-environment jsdom
+ */
+import {
+ createRouter,
+ createMemoryHistory,
+ onBeforeRouteUpdate,
+} from '../../src'
+import { createApp, defineComponent } from 'vue'
+
+const component = {
+ template: '<div>Generic</div>',
+}
+
+describe('onBeforeRouteUpdate', () => {
+ it('invokes with the component context', async () => {
+ expect.assertions(2)
+ const spy = jest
+ .fn()
+ .mockImplementationOnce(function (this: any, to, from, next) {
+ expect(typeof this.counter).toBe('number')
+ next()
+ })
+ const WithLeave = defineComponent({
+ template: `text`,
+ // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings
+ data: () => ({ counter: 0 }),
+ setup() {
+ onBeforeRouteUpdate(spy)
+ },
+ })
+
+ const router = createRouter({
+ history: createMemoryHistory(),
+ routes: [
+ { path: '/', component },
+ { path: '/foo', component: WithLeave as any },
+ ],
+ })
+ const app = createApp({
+ template: `
+ <router-view />
+ `,
+ })
+ app.use(router)
+ const rootEl = document.createElement('div')
+ document.body.appendChild(rootEl)
+ app.mount(rootEl)
+
+ await router.isReady()
+ await router.push('/foo')
+ await router.push('/foo?q')
+ expect(spy).toHaveBeenCalledTimes(1)
+ })
+})
aliasOf: undefined,
components: { default: {} },
leaveGuards: [],
+ updateGuards: [],
instances: {},
meta: {},
name: undefined,
children: [{ path: '/child' }],
components: { default: {} },
leaveGuards: [],
+ updateGuards: [],
instances: {},
meta: { foo: true },
name: 'name',
children: [{ path: '/child' }],
components: { one: {} },
leaveGuards: [],
+ updateGuards: [],
instances: {},
meta: { foo: true },
name: 'name',
export { NavigationFailureType, NavigationFailure } from './errors'
-export { onBeforeRouteLeave } from './navigationGuards'
+export { onBeforeRouteLeave, onBeforeRouteUpdate } from './navigationGuards'
export { RouterLink, useLink } from './RouterLink'
export { RouterView } from './RouterView'
children: record.children || [],
instances: {},
leaveGuards: [],
+ updateGuards: [],
components:
'components' in record
? record.components
props: Exclude<_RouteRecordBase['props'], void>
beforeEnter: RouteRecordMultipleViews['beforeEnter']
leaveGuards: NavigationGuard[]
+ updateGuards: NavigationGuard[]
instances: Record<string, ComponentPublicInstance | undefined | null>
// can only be of of the same type as this record
aliasOf: RouteRecordNormalized | undefined
const instance = getCurrentInstance()
if (!instance) {
__DEV__ &&
- warn('onRouteLeave must be called at the top of a setup function')
+ warn('onBeforeRouteLeave must be called at the top of a setup function')
return
}
- const activeRecord = inject(matchedRouteKey, {} as any).value
+ const activeRecord: RouteRecordNormalized | undefined = inject(
+ matchedRouteKey,
+ {} as any
+ ).value
if (!activeRecord) {
__DEV__ &&
- warn('onRouteLeave must be called at the top of a setup function')
+ warn('onBeforeRouteLeave must be called at the top of a setup function')
return
}
)
}
+export function onBeforeRouteUpdate(updateGuard: NavigationGuard) {
+ const instance = getCurrentInstance()
+ if (!instance) {
+ __DEV__ &&
+ warn('onBeforeRouteUpdate must be called at the top of a setup function')
+ return
+ }
+
+ const activeRecord: RouteRecordNormalized | undefined = inject(
+ matchedRouteKey,
+ {} as any
+ ).value
+
+ if (!activeRecord) {
+ __DEV__ &&
+ warn('onBeforeRouteUpdate must be called at the top of a setup function')
+ return
+ }
+
+ activeRecord.updateGuards.push(
+ // @ts-ignore do we even want to allow that? Passing the context in a composition api hook doesn't make sense
+ updateGuard.bind(instance.proxy)
+ )
+}
+
export function guardToPromiseFn(
guard: NavigationGuard,
to: RouteLocationNormalized,
const [
leavingRecords,
- // updatingRecords,
+ updatingRecords,
// enteringRecords,
] = extractChangingRecords(to, from)
from
)
+ for (const record of updatingRecords) {
+ for (const guard of record.updateGuards) {
+ guards.push(guardToPromiseFn(guard, to, from))
+ }
+ }
+
// run the queue of per route beforeEnter guards
return runGuardQueue(guards)
})