Skip to content

Commit 8bd4dec

Browse files
committed
Introduce formDataArrayFormat option
1 parent 39f6877 commit 8bd4dec

File tree

8 files changed

+73
-21
lines changed

8 files changed

+73
-21
lines changed

packages/core/src/formData.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,47 @@
1-
import { FormDataConvertible } from './types'
1+
import { FormDataConvertible, SerializationArrayFormat } from './types'
22

33
export function objectToFormData(
44
source: Record<string, FormDataConvertible>,
5-
form: FormData = new FormData(),
6-
parentKey: string | null = null,
5+
form: FormData,
6+
parentKey: string | null,
7+
formDataArrayFormat: SerializationArrayFormat,
78
): FormData {
89
source = source || {}
910

1011
for (const key in source) {
1112
if (Object.prototype.hasOwnProperty.call(source, key)) {
12-
append(form, composeKey(parentKey, key), source[key])
13+
append(form, composeObjectKey(parentKey, key), source[key], formDataArrayFormat)
1314
}
1415
}
1516

1617
return form
1718
}
1819

19-
function composeKey(parent: string | null, key: string): string {
20-
return parent ? parent + '[' + key + ']' : key
20+
function composeKey(parent: string | null, key: string, format: SerializationArrayFormat): string {
21+
if (!parent) return key
22+
23+
switch (format) {
24+
case 'indices':
25+
return `${parent}[${key}]`
26+
case 'brackets':
27+
return `${parent}[]`
28+
}
29+
}
30+
31+
function composeObjectKey(parent: string | null, key: string): string {
32+
return composeKey(parent, key, 'indices')
2133
}
2234

23-
function append(form: FormData, key: string, value: FormDataConvertible): void {
35+
function append(
36+
form: FormData,
37+
key: string,
38+
value: FormDataConvertible,
39+
formDataArrayFormat: SerializationArrayFormat,
40+
): void {
2441
if (Array.isArray(value)) {
25-
return Array.from(value.keys()).forEach((index) => append(form, composeKey(key, index.toString()), value[index]))
42+
return Array.from(value.keys()).forEach((index) =>
43+
append(form, composeKey(key, index.toString(), formDataArrayFormat), value[index], formDataArrayFormat),
44+
)
2645
} else if (value instanceof Date) {
2746
return form.append(key, value.toISOString())
2847
} else if (value instanceof File) {
@@ -39,5 +58,5 @@ function append(form: FormData, key: string, value: FormDataConvertible): void {
3958
return form.append(key, '')
4059
}
4160

42-
objectToFormData(value, form, key)
61+
objectToFormData(value, form, key, formDataArrayFormat)
4362
}

packages/core/src/router.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,13 @@ export class Router {
286286
onSuccess = () => {},
287287
onError = () => {},
288288
queryStringArrayFormat = 'brackets',
289+
formDataArrayFormat = 'indices',
289290
}: VisitOptions = {},
290291
): void {
291292
let url = typeof href === 'string' ? hrefToUrl(href) : href
292293

293294
if ((hasFiles(data) || forceFormData) && !(data instanceof FormData)) {
294-
data = objectToFormData(data)
295+
data = objectToFormData(data, new FormData(), null, formDataArrayFormat)
295296
}
296297

297298
if (!(data instanceof FormData)) {
@@ -313,6 +314,7 @@ export class Router {
313314
errorBag,
314315
forceFormData,
315316
queryStringArrayFormat,
317+
formDataArrayFormat,
316318
cancelled: false,
317319
completed: false,
318320
interrupted: false,
@@ -340,6 +342,7 @@ export class Router {
340342
onSuccess,
341343
onError,
342344
queryStringArrayFormat,
345+
formDataArrayFormat,
343346
cancelToken: new AbortController(),
344347
}
345348

packages/core/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ export type LocationVisit = {
6363
preserveScroll: boolean
6464
}
6565

66+
export type SerializationArrayFormat = 'indices' | 'brackets'
67+
6668
export type Visit = {
6769
method: Method
6870
data: RequestPayload
@@ -74,7 +76,8 @@ export type Visit = {
7476
headers: Record<string, string>
7577
errorBag: string | null
7678
forceFormData: boolean
77-
queryStringArrayFormat: 'indices' | 'brackets'
79+
queryStringArrayFormat: SerializationArrayFormat
80+
formDataArrayFormat: SerializationArrayFormat
7881
}
7982

8083
export type GlobalEventsMap = {

packages/core/src/url.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import deepmerge from 'deepmerge'
22
import * as qs from 'qs'
3-
import { FormDataConvertible, Method } from './types'
3+
import { FormDataConvertible, Method, SerializationArrayFormat } from './types'
44

55
export function hrefToUrl(href: string | URL): URL {
66
return new URL(href.toString(), window.location.toString())
@@ -10,7 +10,7 @@ export function mergeDataIntoQueryString(
1010
method: Method,
1111
href: URL | string,
1212
data: Record<string, FormDataConvertible>,
13-
qsArrayFormat: 'indices' | 'brackets' = 'brackets',
13+
qsArrayFormat: SerializationArrayFormat = 'brackets',
1414
): [string, Record<string, FormDataConvertible>] {
1515
const hasHost = /^https?:\/\//.test(href.toString())
1616
const hasAbsolutePath = hasHost || href.toString().startsWith('/')

packages/react/src/Link.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
PreserveStateOption,
66
Progress,
77
router,
8+
SerializationArrayFormat,
89
shouldIntercept,
910
} from '@inertiajs/core'
1011
import { createElement, forwardRef, useCallback } from 'react'
@@ -31,7 +32,8 @@ interface BaseInertiaLinkProps {
3132
onCancel?: () => void
3233
onSuccess?: () => void
3334
onError?: () => void
34-
queryStringArrayFormat?: 'indices' | 'brackets'
35+
queryStringArrayFormat?: SerializationArrayFormat
36+
formDataArrayFormat?: SerializationArrayFormat
3537
}
3638

3739
export type InertiaLinkProps = BaseInertiaLinkProps &
@@ -53,6 +55,7 @@ const Link = forwardRef<unknown, InertiaLinkProps>(
5355
except = [],
5456
headers = {},
5557
queryStringArrayFormat = 'brackets',
58+
formDataArrayFormat = 'indices',
5659
onClick = noop,
5760
onCancelToken = noop,
5861
onBefore = noop,
@@ -82,6 +85,7 @@ const Link = forwardRef<unknown, InertiaLinkProps>(
8285
only,
8386
except,
8487
headers,
88+
formDataArrayFormat,
8589
onCancelToken,
8690
onBefore,
8791
onStart,

packages/svelte/src/components/Link.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import type { FormDataConvertible, Method, PreserveStateOption } from '@inertiajs/core'
2+
import type { FormDataConvertible, Method, PreserveStateOption, SerializationArrayFormat } from '@inertiajs/core'
33
import { inertia } from '../index'
44
55
export let href: string
@@ -12,7 +12,8 @@
1212
export let only: string[] = []
1313
export let except: string[] = []
1414
export let headers: Record<string, string> = {}
15-
export let queryStringArrayFormat: 'brackets' | 'indices' = 'brackets'
15+
export let queryStringArrayFormat: SerializationArrayFormat = 'brackets'
16+
export let formDataArrayFormat: SerializationArrayFormat = 'indices'
1617
1718
$: asProp = method !== 'get' ? 'button' : as.toLowerCase()
1819
$: elProps =
@@ -36,6 +37,7 @@
3637
except,
3738
headers,
3839
queryStringArrayFormat,
40+
formDataArrayFormat,
3941
}}
4042
{...$$restProps}
4143
{...elProps}

packages/vue2/src/link.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
PreserveStateOption,
66
Progress,
77
router,
8+
SerializationArrayFormat,
89
shouldIntercept,
910
} from '@inertiajs/core'
1011
import { FunctionalComponentOptions, PropType } from 'vue'
@@ -28,7 +29,8 @@ export interface InertiaLinkProps {
2829
onFinish?: () => void
2930
onCancel?: () => void
3031
onSuccess?: () => void
31-
queryStringArrayFormat?: 'brackets' | 'indices'
32+
queryStringArrayFormat?: SerializationArrayFormat
33+
formDataArrayFormat?: SerializationArrayFormat
3234
}
3335

3436
type InertiaLink = FunctionalComponentOptions<InertiaLinkProps>
@@ -76,9 +78,13 @@ const Link: InertiaLink = {
7678
default: () => ({}),
7779
},
7880
queryStringArrayFormat: {
79-
type: String as PropType<'brackets' | 'indices'>,
81+
type: String as PropType<SerializationArrayFormat>,
8082
default: 'brackets',
8183
},
84+
formDataArrayFormat: {
85+
type: String as PropType<SerializationArrayFormat>,
86+
default: 'indices',
87+
},
8288
},
8389
render(h, { props, data, children }) {
8490
data.on = {
@@ -134,6 +140,7 @@ const Link: InertiaLink = {
134140
only: props.only,
135141
except: props.except,
136142
headers: props.headers,
143+
formDataArrayFormat: props.formDataArrayFormat,
137144
// @ts-expect-error
138145
onCancelToken: data.on.cancelToken,
139146
// @ts-expect-error

packages/vue3/src/link.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { mergeDataIntoQueryString, Method, PageProps, Progress, router, shouldIntercept } from '@inertiajs/core'
1+
import {
2+
mergeDataIntoQueryString,
3+
Method,
4+
PageProps,
5+
Progress,
6+
router,
7+
SerializationArrayFormat,
8+
shouldIntercept,
9+
} from '@inertiajs/core'
210
import { defineComponent, DefineComponent, h, PropType } from 'vue'
311

412
export interface InertiaLinkProps {
@@ -20,7 +28,8 @@ export interface InertiaLinkProps {
2028
onFinish?: () => void
2129
onCancel?: () => void
2230
onSuccess?: () => void
23-
queryStringArrayFormat?: 'brackets' | 'indices'
31+
queryStringArrayFormat?: SerializationArrayFormat
32+
formDataArrayFormat?: SerializationArrayFormat
2433
}
2534

2635
type InertiaLink = DefineComponent<InertiaLinkProps>
@@ -69,9 +78,13 @@ const Link: InertiaLink = defineComponent({
6978
default: () => ({}),
7079
},
7180
queryStringArrayFormat: {
72-
type: String as PropType<'brackets' | 'indices'>,
81+
type: String as PropType<SerializationArrayFormat>,
7382
default: 'brackets',
7483
},
84+
formDataArrayFormat: {
85+
type: String as PropType<SerializationArrayFormat>,
86+
default: 'indices',
87+
},
7588
},
7689
setup(props, { slots, attrs }) {
7790
return () => {
@@ -103,6 +116,7 @@ const Link: InertiaLink = defineComponent({
103116
only: props.only,
104117
except: props.except,
105118
headers: props.headers,
119+
formDataArrayFormat: props.formDataArrayFormat,
106120
// @ts-expect-error
107121
onCancelToken: attrs.onCancelToken || (() => ({})),
108122
// @ts-expect-error

0 commit comments

Comments
 (0)