From 8bd4decf0173de0bb2a42ce2cd1724a706f89c4d Mon Sep 17 00:00:00 2001 From: Svyatoslav Kryukov Date: Fri, 21 Jun 2024 19:41:12 +0300 Subject: [PATCH] Introduce `formDataArrayFormat` option --- packages/core/src/formData.ts | 37 ++++++++++++++++------ packages/core/src/router.ts | 5 ++- packages/core/src/types.ts | 5 ++- packages/core/src/url.ts | 4 +-- packages/react/src/Link.ts | 6 +++- packages/svelte/src/components/Link.svelte | 6 ++-- packages/vue2/src/link.ts | 11 +++++-- packages/vue3/src/link.ts | 20 ++++++++++-- 8 files changed, 73 insertions(+), 21 deletions(-) diff --git a/packages/core/src/formData.ts b/packages/core/src/formData.ts index 598c6904c..377c20f7e 100644 --- a/packages/core/src/formData.ts +++ b/packages/core/src/formData.ts @@ -1,28 +1,47 @@ -import { FormDataConvertible } from './types' +import { FormDataConvertible, SerializationArrayFormat } from './types' export function objectToFormData( source: Record, - 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) { @@ -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) } diff --git a/packages/core/src/router.ts b/packages/core/src/router.ts index c2294983c..07cd72320 100644 --- a/packages/core/src/router.ts +++ b/packages/core/src/router.ts @@ -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)) { @@ -313,6 +314,7 @@ export class Router { errorBag, forceFormData, queryStringArrayFormat, + formDataArrayFormat, cancelled: false, completed: false, interrupted: false, @@ -340,6 +342,7 @@ export class Router { onSuccess, onError, queryStringArrayFormat, + formDataArrayFormat, cancelToken: new AbortController(), } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 1f77ddbdd..d4fcd867c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -63,6 +63,8 @@ export type LocationVisit = { preserveScroll: boolean } +export type SerializationArrayFormat = 'indices' | 'brackets' + export type Visit = { method: Method data: RequestPayload @@ -74,7 +76,8 @@ export type Visit = { headers: Record errorBag: string | null forceFormData: boolean - queryStringArrayFormat: 'indices' | 'brackets' + queryStringArrayFormat: SerializationArrayFormat + formDataArrayFormat: SerializationArrayFormat } export type GlobalEventsMap = { diff --git a/packages/core/src/url.ts b/packages/core/src/url.ts index 76c532c7d..a74513e0c 100644 --- a/packages/core/src/url.ts +++ b/packages/core/src/url.ts @@ -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()) @@ -10,7 +10,7 @@ export function mergeDataIntoQueryString( method: Method, href: URL | string, data: Record, - qsArrayFormat: 'indices' | 'brackets' = 'brackets', + qsArrayFormat: SerializationArrayFormat = 'brackets', ): [string, Record] { const hasHost = /^https?:\/\//.test(href.toString()) const hasAbsolutePath = hasHost || href.toString().startsWith('/') diff --git a/packages/react/src/Link.ts b/packages/react/src/Link.ts index 5d29b01f8..5398c7a16 100755 --- a/packages/react/src/Link.ts +++ b/packages/react/src/Link.ts @@ -5,6 +5,7 @@ import { PreserveStateOption, Progress, router, + SerializationArrayFormat, shouldIntercept, } from '@inertiajs/core' import { createElement, forwardRef, useCallback } from 'react' @@ -31,7 +32,8 @@ interface BaseInertiaLinkProps { onCancel?: () => void onSuccess?: () => void onError?: () => void - queryStringArrayFormat?: 'indices' | 'brackets' + queryStringArrayFormat?: SerializationArrayFormat + formDataArrayFormat?: SerializationArrayFormat } export type InertiaLinkProps = BaseInertiaLinkProps & @@ -53,6 +55,7 @@ const Link = forwardRef( except = [], headers = {}, queryStringArrayFormat = 'brackets', + formDataArrayFormat = 'indices', onClick = noop, onCancelToken = noop, onBefore = noop, @@ -82,6 +85,7 @@ const Link = forwardRef( only, except, headers, + formDataArrayFormat, onCancelToken, onBefore, onStart, diff --git a/packages/svelte/src/components/Link.svelte b/packages/svelte/src/components/Link.svelte index 6b54b93e2..13d7d2c7b 100644 --- a/packages/svelte/src/components/Link.svelte +++ b/packages/svelte/src/components/Link.svelte @@ -1,5 +1,5 @@