Skip to content

Commit c93e259

Browse files
committed
fix: null should be preserved in relative navigations
The fix is a bit more complicated that I anticipated, I will come back to this later on as the currently documented version works perfectly. - the nullish params are removed before being passed to the matcher - The encodeParam function transform null into '' - The applyToParams also works with arrays but it makes no sense to allow null in array params Ideally, I would make the matcher a bit more permissive so the encoding is kept at the router level. I think the matcher sholud be responsible for removing the nullish parameters but that also means the encode function should leave nullish values untouched. We might need an intermediate Type for this shape of Params, it gets a little bit tedious in terms of types, so I would like to avoid adding more types. Close #1893
1 parent b298d56 commit c93e259

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

packages/router/__tests__/router.spec.ts

+13
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,19 @@ describe('Router', () => {
331331
expect(router.currentRoute.value.params).toEqual({})
332332
})
333333

334+
it('removes null/undefined optional params when current location has it on relative navigations', async () => {
335+
const { router } = await newRouter()
336+
const withParam = router.resolve({ name: 'optional', params: { p: 'a' } })
337+
const implicitNull = router.resolve({ params: { p: null } }, withParam)
338+
const implicitUndefined = router.resolve(
339+
{ params: { p: undefined } },
340+
withParam
341+
)
342+
343+
expect(implicitNull.params).toEqual({})
344+
expect(implicitUndefined.params).toEqual({})
345+
})
346+
334347
it('keeps empty strings in optional params', async () => {
335348
const { router } = await newRouter()
336349
const route1 = router.resolve({ name: 'optional', params: { p: '' } })

packages/router/src/encoding.ts

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { assign } from './utils'
12
import { warn } from './warning'
23

34
/**
@@ -120,14 +121,34 @@ export function encodePath(text: string | number): string {
120121
/**
121122
* Encode characters that need to be encoded on the path section of the URL as a
122123
* param. This function encodes everything {@link encodePath} does plus the
123-
* slash (`/`) character. If `text` is `null` or `undefined`, returns an empty
124-
* string instead.
124+
* slash (`/`) character. If `text` is `null` or `undefined`, it keeps the value as is.
125125
*
126126
* @param text - string to encode
127127
* @returns encoded string
128128
*/
129-
export function encodeParam(text: string | number | null | undefined): string {
130-
return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F')
129+
export function encodeParam(
130+
text: string | number | null | undefined
131+
): string | null | undefined {
132+
return text == null ? text : encodePath(text).replace(SLASH_RE, '%2F')
133+
}
134+
135+
/**
136+
* Remove nullish values from an object. This function creates a copy of the object. Used for params and query.
137+
*
138+
* @param obj - plain object to remove nullish values from
139+
* @returns a new object with only defined values
140+
*/
141+
export function withoutNullishValues(
142+
obj: Record<string, unknown>
143+
): Record<string, unknown> {
144+
const targetParams = assign({}, obj)
145+
for (const key in targetParams) {
146+
if (targetParams[key] == null) {
147+
delete targetParams[key]
148+
}
149+
}
150+
151+
return targetParams
131152
}
132153

133154
/**

packages/router/src/utils/index.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@ export function isESModule(obj: any): obj is { default: RouteComponent } {
1313

1414
export const assign = Object.assign
1515

16+
export function applyToParams(
17+
fn: (v: string | number | null | undefined) => string | null | undefined,
18+
params: RouteParamsRaw | undefined
19+
): RouteParamsRaw
1620
export function applyToParams(
1721
fn: (v: string | number | null | undefined) => string,
1822
params: RouteParamsRaw | undefined
19-
): RouteParams {
23+
): RouteParams
24+
export function applyToParams(
25+
fn: (v: string | number | null | undefined) => string | null | undefined,
26+
params: RouteParamsRaw | undefined
27+
): RouteParams | RouteParamsRaw {
2028
const newParams: RouteParams = {}
2129

2230
for (const key in params) {

0 commit comments

Comments
 (0)