|
import { useApi } from '@/composables/useApi'; |
|
import { FormDataConvertible } from '@inertiajs/core/types'; |
|
import { InertiaFormProps, useForm } from '@inertiajs/vue3'; |
|
import { AxiosProgressEvent } from 'axios'; |
|
import cloneDeep from 'lodash.clonedeep'; |
|
|
|
const api = useApi(); |
|
|
|
interface FormOptions { |
|
onBefore?: () => void; |
|
onStart?: () => void; |
|
onProgress?: (event: AxiosProgressEvent) => void; |
|
onSuccess?: (data: any) => void; |
|
onError?: (error: { |
|
status: number; |
|
}) => void; |
|
onFinish?: () => void; |
|
} |
|
|
|
export type FormDataType = Record<string, FormDataConvertible>; |
|
export type InertiaForm<TForm extends FormDataType> = TForm & InertiaFormProps<TForm>; |
|
|
|
export function useApiForm<TForm extends FormDataType>(initialData?: TForm | (() => TForm)): InertiaForm<TForm> { |
|
const form = useForm(initialData || {}); |
|
|
|
let transform = (data: TForm) => data; |
|
let recentlySuccessfulTimeoutId: number | null = null; |
|
|
|
const overriders = { |
|
transform: (receiver: any) => (callback: (data: TForm) => any) => { |
|
transform = callback; |
|
return receiver; |
|
}, |
|
submit: |
|
() => |
|
(method: string, url: string, options: FormOptions = {}) => { |
|
form.wasSuccessful = false; |
|
form.recentlySuccessful = false; |
|
form.clearErrors(); |
|
if (recentlySuccessfulTimeoutId) clearTimeout(recentlySuccessfulTimeoutId); |
|
if (options.onBefore) { |
|
options.onBefore(); |
|
} |
|
|
|
form.processing = true; |
|
if (options.onStart) { |
|
options.onStart(); |
|
} |
|
|
|
const data = transform(form.data() as any); |
|
// @ts-expect-error Error in accessing method using square brackets |
|
api[method](url, data, { |
|
headers: { |
|
'Content-Type': hasFiles(data) ? 'multipart/form-data' : 'application/json', |
|
}, |
|
onUploadProgress: (event: any) => { |
|
form.progress = event; |
|
if (options.onProgress) { |
|
options.onProgress(event); |
|
} |
|
}, |
|
}) |
|
.then((response: any) => { |
|
form.processing = false; |
|
form.progress = null; |
|
form.clearErrors(); |
|
form.wasSuccessful = true; |
|
form.recentlySuccessful = true; |
|
recentlySuccessfulTimeoutId = setTimeout(() => (form.recentlySuccessful = false), 2000); |
|
|
|
if (options.onSuccess) { |
|
options.onSuccess(response.data); |
|
} |
|
|
|
form.defaults(cloneDeep(form.data())); |
|
form.isDirty = false; |
|
}) |
|
.catch((error: any) => { |
|
form.processing = false; |
|
form.progress = null; |
|
|
|
form.clearErrors(); |
|
if (error.response?.status === 422) { |
|
Object.keys(error.response.data.errors).forEach((key) => { |
|
// @ts-expect-error types mismatch |
|
form.setError(key, error.response.data.errors[key][0]); |
|
}); |
|
} |
|
|
|
if (options.onError) { |
|
options.onError(error); |
|
} |
|
}) |
|
.finally(() => { |
|
form.processing = false; |
|
form.progress = null; |
|
|
|
if (options.onFinish) { |
|
options.onFinish(); |
|
} |
|
}); |
|
}, |
|
}; |
|
|
|
return new Proxy(form, { |
|
get: (target, prop, receiver) => { |
|
if (Object.keys(overriders).indexOf(prop.toString()) < 0) { |
|
// @ts-expect-error Using prop |
|
return target[prop]; |
|
} |
|
|
|
return overriders[prop as keyof typeof overriders](receiver); |
|
}, |
|
}) as any; |
|
} |
|
|
|
function hasFiles(data: any): boolean { |
|
return data instanceof FormData; |
|
} |
@scramatte - I've updated some comments and things above. See if that works for you now. It's working very well in the project now for me. It can also be very simple to hit an api endpoint - as simple as:
useApiForm().delete(route('photos.edit'));