]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: allow numbers as params
authorpikax <carlos@hypermob.co.uk>
Sun, 3 May 2020 21:29:54 +0000 (23:29 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Sun, 3 May 2020 21:37:56 +0000 (23:37 +0200)
Close #206

Co-authored-by: Eduardo San Martin Morote <posva13@gmail.com>
__tests__/router.spec.ts
src/encoding.ts
src/router.ts
src/types/index.ts
src/utils/index.ts

index 1d6c2a8c737279641215f10de16a28bc4d05bdfe..7100673aeda870e25569c4db6931d7418d00a4f2 100644 (file)
@@ -254,6 +254,12 @@ describe('Router', () => {
     expect(spy).toHaveBeenCalledTimes(1)
   })
 
+  it('casts number params to string', async () => {
+    const { router } = await newRouter()
+    await router.push({ name: 'Param', params: { p: 0 } })
+    expect(router.currentRoute.value).toMatchObject({ params: { p: '0' } })
+  })
+
   it('navigates to same route record but different query', async () => {
     const { router } = await newRouter()
     await router.push('/?q=1')
index c863c25e3467cec3668b0ef98560ed4dbb63fbc8..260b6c374d8f437e018ca14de865b79aefdc2299 100644 (file)
@@ -84,7 +84,7 @@ export function encodeQueryProperty(text: string | number): string {
  * @param text - string to encode
  * @returns encoded string
  */
-export function encodePath(text: string): string {
+export function encodePath(text: string | number): string {
   return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F')
 }
 
@@ -96,7 +96,7 @@ export function encodePath(text: string): string {
  * @param text - string to encode
  * @returns encoded string
  */
-export function encodeParam(text: string): string {
+export function encodeParam(text: string | number): string {
   return encodePath(text).replace(SLASH_RE, '%2F')
 }
 
@@ -107,11 +107,11 @@ export function encodeParam(text: string): string {
  * @param text - string to decode
  * @returns decoded string
  */
-export function decode(text: string): string {
+export function decode(text: string | number): string {
   try {
-    return decodeURIComponent(text)
+    return decodeURIComponent('' + text)
   } catch (err) {
     __DEV__ && warn(`Error decoding "${text}". Using original value`)
   }
-  return text
+  return '' + text
 }
index acefaa3d1e95260842534144256117ed5dcdd360..e02bf3e2926b638ff94b5b5da9242ce948428f7d 100644 (file)
@@ -5,13 +5,13 @@ import {
   PostNavigationGuard,
   START_LOCATION_NORMALIZED,
   Lazy,
-  MatcherLocation,
   RouteLocationNormalizedLoaded,
   RouteLocation,
   RouteRecordName,
   isRouteName,
   NavigationGuardWithThis,
   RouteLocationOptions,
+  MatcherLocationRaw,
 } from './types'
 import { RouterHistory, HistoryState } from './history/common'
 import {
@@ -180,6 +180,10 @@ export function createRouter(options: RouterOptions): Router {
     history.scrollRestoration = 'manual'
   }
 
+  const normalizeParams = applyToParams.bind(
+    null,
+    paramValue => '' + paramValue
+  )
   const encodeParams = applyToParams.bind(null, encodeParam)
   const decodeParams = applyToParams.bind(null, decode)
 
@@ -249,6 +253,8 @@ export function createRouter(options: RouterOptions): Router {
       }
     }
 
+    let matcherLocation: MatcherLocationRaw
+
     // path could be relative in object as well
     if ('path' in rawLocation) {
       if (
@@ -263,28 +269,25 @@ export function createRouter(options: RouterOptions): Router {
           }" was passed with params but they will be ignored. Use a named route alongside params instead.`
         )
       }
-      rawLocation = {
+      matcherLocation = {
         ...rawLocation,
         path: parseURL(parseQuery, rawLocation.path, currentLocation.path).path,
       }
+    } else {
+      matcherLocation = {
+        ...rawLocation,
+        params: encodeParams(rawLocation.params),
+      }
     }
 
-    let matchedRoute: MatcherLocation = // relative or named location, path is ignored
-      // for same reason TS thinks rawLocation.params can be undefined
-      matcher.resolve(
-        'params' in rawLocation
-          ? { ...rawLocation, params: encodeParams(rawLocation.params) }
-          : rawLocation,
-        currentLocation
-      )
+    let matchedRoute = matcher.resolve(matcherLocation, currentLocation)
 
     const hash = encodeHash(rawLocation.hash || '')
 
     // put back the unencoded params as given by the user (avoid the cost of decoding them)
-    // TODO: normalize params if we accept numbers as raw values
     matchedRoute.params =
       'params' in rawLocation
-        ? rawLocation.params!
+        ? normalizeParams(rawLocation.params)
         : decodeParams(matchedRoute.params)
 
     const fullPath = stringifyURL(stringifyQuery, {
index 64d1be2af6fc37fcc560b679552212848e2a14f8..d15753d5d46ae0cc4df94705ec897ba2c8f3d1b4 100644 (file)
@@ -20,14 +20,12 @@ export type VueUseOptions<T> = {
 export type TODO = any
 
 export type RouteParamValue = string
-// TODO: should we allow more values like numbers and normalize them to strings?
-// type RouteParamValueRaw = RouteParamValue | number
+export type RouteParamValueRaw = RouteParamValue | number
 export type RouteParams = Record<string, RouteParamValue | RouteParamValue[]>
-export type RouteParamsRaw = RouteParams
-// export type RouteParamsRaw = Record<
-//   string,
-//   RouteParamValueRaw | RouteParamValueRaw[]
-// >
+export type RouteParamsRaw = Record<
+  string,
+  RouteParamValueRaw | RouteParamValueRaw[]
+>
 
 export interface RouteQueryAndHash {
   query?: LocationQueryRaw
@@ -37,13 +35,22 @@ export interface LocationAsPath {
   path: string
 }
 
+export interface LocationAsNameRaw {
+  name: RouteRecordName
+  params?: RouteParamsRaw
+}
+
 export interface LocationAsName {
   name: RouteRecordName
+  params?: RouteParams
+}
+
+export interface LocationAsRelativeRaw {
   params?: RouteParamsRaw
 }
 
 export interface LocationAsRelative {
-  params?: RouteParamsRaw
+  params?: RouteParams
 }
 
 export interface RouteLocationOptions {
@@ -67,8 +74,8 @@ export interface RouteLocationOptions {
 export type RouteLocationRaw =
   | string
   | (RouteQueryAndHash & LocationAsPath & RouteLocationOptions)
-  | (RouteQueryAndHash & LocationAsName & RouteLocationOptions)
-  | (RouteQueryAndHash & LocationAsRelative & RouteLocationOptions)
+  | (RouteQueryAndHash & LocationAsNameRaw & RouteLocationOptions)
+  | (RouteQueryAndHash & LocationAsRelativeRaw & RouteLocationOptions)
 
 export interface RouteLocationMatched extends RouteRecordNormalized {
   // components cannot be Lazy<RouteComponent>
index 9b074d02c9aabe44566f7c02015faec0ee9125b4..de75374bec573e32306203d7e768888fd009bc10 100644 (file)
@@ -1,4 +1,4 @@
-import { RouteParams, RouteComponent } from '../types'
+import { RouteParams, RouteComponent, RouteParamsRaw } from '../types'
 import { hasSymbol } from '../injectionSymbols'
 
 export * from './env'
@@ -8,8 +8,8 @@ export function isESModule(obj: any): obj is { default: RouteComponent } {
 }
 
 export function applyToParams(
-  fn: (v: string) => string,
-  params: RouteParams | undefined
+  fn: (v: string | number) => string,
+  params: RouteParamsRaw | undefined
 ): RouteParams {
   const newParams: RouteParams = {}