Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce formDataArrayFormat option #1901

Open
wants to merge 1 commit into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading