From d23ca30b4588172f1093bbae4be348421cc4ca33 Mon Sep 17 00:00:00 2001 From: Eduardo Wesley Date: Thu, 13 Apr 2023 08:02:04 -0300 Subject: [PATCH] feat(useRouteQuery,useRouteParams): new `transform` handler (#2191) Co-authored-by: Anthony Fu --- packages/router/_types.ts | 9 ++++ packages/router/useRouteParams/index.md | 2 + packages/router/useRouteParams/index.test.ts | 24 ++++++++++ packages/router/useRouteParams/index.ts | 39 ++++++++++------ packages/router/useRouteQuery/index.md | 2 + packages/router/useRouteQuery/index.test.ts | 34 ++++++++++++++ packages/router/useRouteQuery/index.ts | 49 +++++++++++++------- 7 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 packages/router/useRouteParams/index.test.ts create mode 100644 packages/router/useRouteQuery/index.test.ts diff --git a/packages/router/_types.ts b/packages/router/_types.ts index 6dc416afc00..e962380572d 100644 --- a/packages/router/_types.ts +++ b/packages/router/_types.ts @@ -1,6 +1,8 @@ import type { MaybeRef } from '@vueuse/shared' import type { useRoute, useRouter } from 'vue-router' +export type RouterQueryValue = null | undefined | string | string[] + export interface ReactiveRouteOptions { /** * Mode to update the router query, ref is also acceptable @@ -19,3 +21,10 @@ export interface ReactiveRouteOptions { */ router?: ReturnType } + +export interface ReactiveRouteOptionsWithTransform extends ReactiveRouteOptions { + /** + * Function to transform data before return + */ + transform?: (val: V) => R +} diff --git a/packages/router/useRouteParams/index.md b/packages/router/useRouteParams/index.md index d186aaa1a8d..2a4a624d9fa 100644 --- a/packages/router/useRouteParams/index.md +++ b/packages/router/useRouteParams/index.md @@ -15,6 +15,8 @@ const userId = useRouteParams('userId') const userId = useRouteParams('userId', '-1') // or with a default value +const userId = useRouteParams('page', '1', { transform: Number }) // or transforming value + console.log(userId.value) // route.params.userId userId.value = '100' // router.replace({ params: { userId: '100' } }) ``` diff --git a/packages/router/useRouteParams/index.test.ts b/packages/router/useRouteParams/index.test.ts new file mode 100644 index 00000000000..0fbb86ff61b --- /dev/null +++ b/packages/router/useRouteParams/index.test.ts @@ -0,0 +1,24 @@ +import { useRouteParams } from '.' + +describe('useRouteQuery', () => { + const getRoute = (params: Record = {}) => ({ + query: {}, + fullPath: '', + hash: '', + matched: [], + meta: {}, + name: '', + params: { id: '1', ...params }, + path: '', + redirectedFrom: undefined, + }) + + it('should return transformed value', () => { + const router = {} as any + const route = getRoute() + + const id = useRouteParams('id', '1', { transform: Number, route, router }) + + expect(id.value).toBe(1) + }) +}) diff --git a/packages/router/useRouteParams/index.ts b/packages/router/useRouteParams/index.ts index 0214246d827..533727e29e1 100644 --- a/packages/router/useRouteParams/index.ts +++ b/packages/router/useRouteParams/index.ts @@ -2,27 +2,40 @@ import type { Ref } from 'vue-demi' import { computed, nextTick } from 'vue-demi' import { useRoute, useRouter } from 'vue-router' import { toValue } from '@vueuse/shared' -import type { ReactiveRouteOptions } from '../_types' +import type { ReactiveRouteOptionsWithTransform, RouterQueryValue } from '../_types' -export function useRouteParams(name: string): Ref -export function useRouteParams(name: string, defaultValue?: T, options?: ReactiveRouteOptions): Ref -export function useRouteParams( +export function useRouteParams( + name: string +): Ref + +export function useRouteParams< + T extends RouterQueryValue = RouterQueryValue, + K = T, +>( name: string, defaultValue?: T, - { + options?: ReactiveRouteOptionsWithTransform +): Ref + +export function useRouteParams< + T extends RouterQueryValue = RouterQueryValue, + K = T, +>( + name: string, + defaultValue?: T, + options: ReactiveRouteOptionsWithTransform = {}, +): Ref { + const { mode = 'replace', route = useRoute(), router = useRouter(), - }: ReactiveRouteOptions = {}, -) { + transform = value => value as any as K, + } = options + return computed({ get() { - const data = route.params[name] - if (data == null) - return defaultValue ?? null - if (Array.isArray(data)) - return data.filter(Boolean) - return data + const data = route.params[name] ?? defaultValue + return transform(data as T) }, set(v) { nextTick(() => { diff --git a/packages/router/useRouteQuery/index.md b/packages/router/useRouteQuery/index.md index d0e77ae4c4d..22066499abf 100644 --- a/packages/router/useRouteQuery/index.md +++ b/packages/router/useRouteQuery/index.md @@ -15,6 +15,8 @@ const search = useRouteQuery('search') const search = useRouteQuery('search', 'foo') // or with a default value +const page = useRouteQuery('page', '1', { transform: Number }) // or transforming value + console.log(search.value) // route.query.search search.value = 'foobar' // router.replace({ query: { search: 'foobar' } }) ``` diff --git a/packages/router/useRouteQuery/index.test.ts b/packages/router/useRouteQuery/index.test.ts new file mode 100644 index 00000000000..0ff11a73d9f --- /dev/null +++ b/packages/router/useRouteQuery/index.test.ts @@ -0,0 +1,34 @@ +import { useRouteQuery } from '.' + +describe('useRouteQuery', () => { + const getRoute = (query: Record = {}) => ({ + query: { + search: 'vue3', + page: '1', + ...query, + }, + fullPath: '', + hash: '', + matched: [], + meta: {}, + name: '', + params: {}, + path: '', + redirectedFrom: undefined, + }) + + it('should return transformed value', () => { + const router = {} as any + const route = getRoute() + const transform = Number + const toArray = (param: string | string[] | null) => Array.isArray(param) ? param : [param] + + const page = useRouteQuery('page', '1', { transform, route, router }) + const perPage = useRouteQuery('perPage', '15', { transform, route, router }) + const tags = useRouteQuery('tags', [], { transform: toArray, route: getRoute({ tags: 'vite' }), router }) + + expect(page.value).toBe(1) + expect(perPage.value).toBe(15) + expect(tags.value).toEqual(['vite']) + }) +}) diff --git a/packages/router/useRouteQuery/index.ts b/packages/router/useRouteQuery/index.ts index 5873161ccd8..5e8a4318bda 100644 --- a/packages/router/useRouteQuery/index.ts +++ b/packages/router/useRouteQuery/index.ts @@ -1,37 +1,50 @@ import type { Ref } from 'vue-demi' import { computed, nextTick } from 'vue-demi' -import { useRoute, useRouter } from 'vue-router' import { toValue } from '@vueuse/shared' -import type { ReactiveRouteOptions } from '../_types' +import { useRoute, useRouter } from 'vue-router' +import type { ReactiveRouteOptionsWithTransform, RouterQueryValue } from '../_types' -let queue: Record = {} +let _queue: Record = {} -export function useRouteQuery(name: string): Ref -export function useRouteQuery(name: string, defaultValue?: T, options?: ReactiveRouteOptions): Ref -export function useRouteQuery( +export function useRouteQuery( + name: string +): Ref + +export function useRouteQuery< + T extends RouterQueryValue = RouterQueryValue, + K = T, +>( name: string, defaultValue?: T, - { + options?: ReactiveRouteOptionsWithTransform +): Ref + +export function useRouteQuery< + T extends RouterQueryValue = RouterQueryValue, + K = T, +>( + name: string, + defaultValue?: T, + options: ReactiveRouteOptionsWithTransform = {}, +): Ref { + const { mode = 'replace', route = useRoute(), router = useRouter(), - }: ReactiveRouteOptions = {}, -) { + transform = value => value as any as K, + } = options + return computed({ get() { - const data = route.query[name] - if (data == null) - return defaultValue ?? null - if (Array.isArray(data)) - return data.filter(Boolean) - return data + const data = route.query[name] ?? defaultValue + return transform(data as T) }, set(v) { - queue[name] = (v === defaultValue || v === null) ? undefined : v + _queue[name] = (v === defaultValue || v === null) ? undefined : v nextTick(() => { - router[toValue(mode)]({ ...route, query: { ...route.query, ...queue } }) - nextTick(() => queue = {}) + router[toValue(mode)]({ ...route, query: { ...route.query, ..._queue } }) + nextTick(() => _queue = {}) }) }, })