Skip to content

Commit

Permalink
Introduce formDataArrayFormat option
Browse files Browse the repository at this point in the history
  • Loading branch information
skryukov committed Oct 9, 2024
1 parent 39f6877 commit 8bd4dec
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 21 deletions.
37 changes: 28 additions & 9 deletions packages/core/src/formData.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
import { FormDataConvertible } from './types'
import { FormDataConvertible, SerializationArrayFormat } from './types'

export function objectToFormData(
source: Record<string, FormDataConvertible>,
form: FormData = new FormData(),
parentKey: string | null = null,
form: FormData,
parentKey: string | null,
formDataArrayFormat: SerializationArrayFormat,
): FormData {
source = source || {}

for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
append(form, composeKey(parentKey, key), source[key])
append(form, composeObjectKey(parentKey, key), source[key], formDataArrayFormat)
}
}

return form
}

function composeKey(parent: string | null, key: string): string {
return parent ? parent + '[' + key + ']' : key
function composeKey(parent: string | null, key: string, format: SerializationArrayFormat): string {
if (!parent) return key

switch (format) {
case 'indices':
return `${parent}[${key}]`
case 'brackets':
return `${parent}[]`
}
}

function composeObjectKey(parent: string | null, key: string): string {
return composeKey(parent, key, 'indices')
}

function append(form: FormData, key: string, value: FormDataConvertible): void {
function append(
form: FormData,
key: string,
value: FormDataConvertible,
formDataArrayFormat: SerializationArrayFormat,
): void {
if (Array.isArray(value)) {
return Array.from(value.keys()).forEach((index) => append(form, composeKey(key, index.toString()), value[index]))
return Array.from(value.keys()).forEach((index) =>
append(form, composeKey(key, index.toString(), formDataArrayFormat), value[index], formDataArrayFormat),
)
} else if (value instanceof Date) {
return form.append(key, value.toISOString())
} else if (value instanceof File) {
Expand All @@ -39,5 +58,5 @@ function append(form: FormData, key: string, value: FormDataConvertible): void {
return form.append(key, '')
}

objectToFormData(value, form, key)
objectToFormData(value, form, key, formDataArrayFormat)
}
5 changes: 4 additions & 1 deletion packages/core/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,13 @@ export class Router {
onSuccess = () => {},
onError = () => {},
queryStringArrayFormat = 'brackets',
formDataArrayFormat = 'indices',
}: VisitOptions = {},
): void {
let url = typeof href === 'string' ? hrefToUrl(href) : href

if ((hasFiles(data) || forceFormData) && !(data instanceof FormData)) {
data = objectToFormData(data)
data = objectToFormData(data, new FormData(), null, formDataArrayFormat)
}

if (!(data instanceof FormData)) {
Expand All @@ -313,6 +314,7 @@ export class Router {
errorBag,
forceFormData,
queryStringArrayFormat,
formDataArrayFormat,
cancelled: false,
completed: false,
interrupted: false,
Expand Down Expand Up @@ -340,6 +342,7 @@ export class Router {
onSuccess,
onError,
queryStringArrayFormat,
formDataArrayFormat,
cancelToken: new AbortController(),
}

Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export type LocationVisit = {
preserveScroll: boolean
}

export type SerializationArrayFormat = 'indices' | 'brackets'

export type Visit = {
method: Method
data: RequestPayload
Expand All @@ -74,7 +76,8 @@ export type Visit = {
headers: Record<string, string>
errorBag: string | null
forceFormData: boolean
queryStringArrayFormat: 'indices' | 'brackets'
queryStringArrayFormat: SerializationArrayFormat
formDataArrayFormat: SerializationArrayFormat
}

export type GlobalEventsMap = {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/url.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import deepmerge from 'deepmerge'
import * as qs from 'qs'
import { FormDataConvertible, Method } from './types'
import { FormDataConvertible, Method, SerializationArrayFormat } from './types'

export function hrefToUrl(href: string | URL): URL {
return new URL(href.toString(), window.location.toString())
Expand All @@ -10,7 +10,7 @@ export function mergeDataIntoQueryString(
method: Method,
href: URL | string,
data: Record<string, FormDataConvertible>,
qsArrayFormat: 'indices' | 'brackets' = 'brackets',
qsArrayFormat: SerializationArrayFormat = 'brackets',
): [string, Record<string, FormDataConvertible>] {
const hasHost = /^https?:\/\//.test(href.toString())
const hasAbsolutePath = hasHost || href.toString().startsWith('/')
Expand Down
6 changes: 5 additions & 1 deletion packages/react/src/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
PreserveStateOption,
Progress,
router,
SerializationArrayFormat,
shouldIntercept,
} from '@inertiajs/core'
import { createElement, forwardRef, useCallback } from 'react'
Expand All @@ -31,7 +32,8 @@ interface BaseInertiaLinkProps {
onCancel?: () => void
onSuccess?: () => void
onError?: () => void
queryStringArrayFormat?: 'indices' | 'brackets'
queryStringArrayFormat?: SerializationArrayFormat
formDataArrayFormat?: SerializationArrayFormat
}

export type InertiaLinkProps = BaseInertiaLinkProps &
Expand All @@ -53,6 +55,7 @@ const Link = forwardRef<unknown, InertiaLinkProps>(
except = [],
headers = {},
queryStringArrayFormat = 'brackets',
formDataArrayFormat = 'indices',
onClick = noop,
onCancelToken = noop,
onBefore = noop,
Expand Down Expand Up @@ -82,6 +85,7 @@ const Link = forwardRef<unknown, InertiaLinkProps>(
only,
except,
headers,
formDataArrayFormat,
onCancelToken,
onBefore,
onStart,
Expand Down
6 changes: 4 additions & 2 deletions packages/svelte/src/components/Link.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import type { FormDataConvertible, Method, PreserveStateOption } from '@inertiajs/core'
import type { FormDataConvertible, Method, PreserveStateOption, SerializationArrayFormat } from '@inertiajs/core'
import { inertia } from '../index'
export let href: string
Expand All @@ -12,7 +12,8 @@
export let only: string[] = []
export let except: string[] = []
export let headers: Record<string, string> = {}
export let queryStringArrayFormat: 'brackets' | 'indices' = 'brackets'
export let queryStringArrayFormat: SerializationArrayFormat = 'brackets'
export let formDataArrayFormat: SerializationArrayFormat = 'indices'
$: asProp = method !== 'get' ? 'button' : as.toLowerCase()
$: elProps =
Expand All @@ -36,6 +37,7 @@
except,
headers,
queryStringArrayFormat,
formDataArrayFormat,
}}
{...$$restProps}
{...elProps}
Expand Down
11 changes: 9 additions & 2 deletions packages/vue2/src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
PreserveStateOption,
Progress,
router,
SerializationArrayFormat,
shouldIntercept,
} from '@inertiajs/core'
import { FunctionalComponentOptions, PropType } from 'vue'
Expand All @@ -28,7 +29,8 @@ export interface InertiaLinkProps {
onFinish?: () => void
onCancel?: () => void
onSuccess?: () => void
queryStringArrayFormat?: 'brackets' | 'indices'
queryStringArrayFormat?: SerializationArrayFormat
formDataArrayFormat?: SerializationArrayFormat
}

type InertiaLink = FunctionalComponentOptions<InertiaLinkProps>
Expand Down Expand Up @@ -76,9 +78,13 @@ const Link: InertiaLink = {
default: () => ({}),
},
queryStringArrayFormat: {
type: String as PropType<'brackets' | 'indices'>,
type: String as PropType<SerializationArrayFormat>,
default: 'brackets',
},
formDataArrayFormat: {
type: String as PropType<SerializationArrayFormat>,
default: 'indices',
},
},
render(h, { props, data, children }) {
data.on = {
Expand Down Expand Up @@ -134,6 +140,7 @@ const Link: InertiaLink = {
only: props.only,
except: props.except,
headers: props.headers,
formDataArrayFormat: props.formDataArrayFormat,
// @ts-expect-error
onCancelToken: data.on.cancelToken,
// @ts-expect-error
Expand Down
20 changes: 17 additions & 3 deletions packages/vue3/src/link.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { mergeDataIntoQueryString, Method, PageProps, Progress, router, shouldIntercept } from '@inertiajs/core'
import {
mergeDataIntoQueryString,
Method,
PageProps,
Progress,
router,
SerializationArrayFormat,
shouldIntercept,
} from '@inertiajs/core'
import { defineComponent, DefineComponent, h, PropType } from 'vue'

export interface InertiaLinkProps {
Expand All @@ -20,7 +28,8 @@ export interface InertiaLinkProps {
onFinish?: () => void
onCancel?: () => void
onSuccess?: () => void
queryStringArrayFormat?: 'brackets' | 'indices'
queryStringArrayFormat?: SerializationArrayFormat
formDataArrayFormat?: SerializationArrayFormat
}

type InertiaLink = DefineComponent<InertiaLinkProps>
Expand Down Expand Up @@ -69,9 +78,13 @@ const Link: InertiaLink = defineComponent({
default: () => ({}),
},
queryStringArrayFormat: {
type: String as PropType<'brackets' | 'indices'>,
type: String as PropType<SerializationArrayFormat>,
default: 'brackets',
},
formDataArrayFormat: {
type: String as PropType<SerializationArrayFormat>,
default: 'indices',
},
},
setup(props, { slots, attrs }) {
return () => {
Expand Down Expand Up @@ -103,6 +116,7 @@ const Link: InertiaLink = defineComponent({
only: props.only,
except: props.except,
headers: props.headers,
formDataArrayFormat: props.formDataArrayFormat,
// @ts-expect-error
onCancelToken: attrs.onCancelToken || (() => ({})),
// @ts-expect-error
Expand Down

0 comments on commit 8bd4dec

Please sign in to comment.