From: Eduardo San Martin Morote Date: Tue, 28 Apr 2020 09:07:45 +0000 (+0200) Subject: feat: add onBeforeRouteUpdate X-Git-Tag: v4.0.0-alpha.8~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=96c95035653a52f94781808fccbf262a02a3cd79;p=thirdparty%2Fvuejs%2Frouter.git feat: add onBeforeRouteUpdate --- diff --git a/__tests__/guards/onBeforeRouteUpdate.spec.ts b/__tests__/guards/onBeforeRouteUpdate.spec.ts new file mode 100644 index 00000000..4762d369 --- /dev/null +++ b/__tests__/guards/onBeforeRouteUpdate.spec.ts @@ -0,0 +1,55 @@ +/** + * @jest-environment jsdom + */ +import { + createRouter, + createMemoryHistory, + onBeforeRouteUpdate, +} from '../../src' +import { createApp, defineComponent } from 'vue' + +const component = { + template: '
Generic
', +} + +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: ` + + `, + }) + 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) + }) +}) diff --git a/__tests__/matcher/records.spec.ts b/__tests__/matcher/records.spec.ts index afb168c3..aa25b115 100644 --- a/__tests__/matcher/records.spec.ts +++ b/__tests__/matcher/records.spec.ts @@ -12,6 +12,7 @@ describe('normalizeRouteRecord', () => { aliasOf: undefined, components: { default: {} }, leaveGuards: [], + updateGuards: [], instances: {}, meta: {}, name: undefined, @@ -35,6 +36,7 @@ describe('normalizeRouteRecord', () => { children: [{ path: '/child' }], components: { default: {} }, leaveGuards: [], + updateGuards: [], instances: {}, meta: { foo: true }, name: 'name', @@ -76,6 +78,7 @@ describe('normalizeRouteRecord', () => { children: [{ path: '/child' }], components: { one: {} }, leaveGuards: [], + updateGuards: [], instances: {}, meta: { foo: true }, name: 'name', diff --git a/src/index.ts b/src/index.ts index 8a3e46d1..7495b1eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,7 +37,7 @@ export { export { NavigationFailureType, NavigationFailure } from './errors' -export { onBeforeRouteLeave } from './navigationGuards' +export { onBeforeRouteLeave, onBeforeRouteUpdate } from './navigationGuards' export { RouterLink, useLink } from './RouterLink' export { RouterView } from './RouterView' diff --git a/src/matcher/index.ts b/src/matcher/index.ts index 0c018584..9cd960df 100644 --- a/src/matcher/index.ts +++ b/src/matcher/index.ts @@ -304,6 +304,7 @@ export function normalizeRouteRecord( children: record.children || [], instances: {}, leaveGuards: [], + updateGuards: [], components: 'components' in record ? record.components diff --git a/src/matcher/types.ts b/src/matcher/types.ts index ccf9d059..8ce537a1 100644 --- a/src/matcher/types.ts +++ b/src/matcher/types.ts @@ -16,6 +16,7 @@ export interface RouteRecordNormalized { props: Exclude<_RouteRecordBase['props'], void> beforeEnter: RouteRecordMultipleViews['beforeEnter'] leaveGuards: NavigationGuard[] + updateGuards: NavigationGuard[] instances: Record // can only be of of the same type as this record aliasOf: RouteRecordNormalized | undefined diff --git a/src/navigationGuards.ts b/src/navigationGuards.ts index 98012886..a5c78ec5 100644 --- a/src/navigationGuards.ts +++ b/src/navigationGuards.ts @@ -26,15 +26,18 @@ export function onBeforeRouteLeave(leaveGuard: NavigationGuard) { 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 } @@ -44,6 +47,31 @@ export function onBeforeRouteLeave(leaveGuard: NavigationGuard) { ) } +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, diff --git a/src/router.ts b/src/router.ts index 74da53a8..26dd3cc7 100644 --- a/src/router.ts +++ b/src/router.ts @@ -395,7 +395,7 @@ export function createRouter({ const [ leavingRecords, - // updatingRecords, + updatingRecords, // enteringRecords, ] = extractChangingRecords(to, from) @@ -425,6 +425,12 @@ export function createRouter({ 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) })