Skip to content

Commit ff3caa9

Browse files
Add onSubmitComplete prop to Form component (#2503)
* vue after submit * react after submit * Update Form.ts * after submit svelte * afterSubmit -> onSubmitComplete * Restore syntax * wip * wip --------- Co-authored-by: Pascal Baljet <[email protected]>
1 parent 3df837b commit ff3caa9

File tree

9 files changed

+185
-69
lines changed

9 files changed

+185
-69
lines changed

packages/core/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ export type FormComponentProps = Partial<
430430
action?: string | { url: string; method: Method }
431431
transform?: (data: Record<string, FormDataConvertible>) => Record<string, FormDataConvertible>
432432
options?: FormComponentOptions
433+
onSubmitComplete?: (props: FormComponentonSubmitCompleteArguments) => void
433434
disableWhileProcessing?: boolean
434435
}
435436

@@ -442,6 +443,11 @@ export type FormComponentMethods = {
442443
submit: () => void
443444
}
444445

446+
export type FormComponentonSubmitCompleteArguments = Pick<
447+
FormComponentMethods,
448+
'clearErrors' | 'resetAndClearErrors' | 'reset'
449+
>
450+
445451
export type FormComponentState = {
446452
errors: Record<string, string>
447453
hasErrors: boolean

packages/react/src/Form.ts

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
5252
onSuccess = noop,
5353
onError = noop,
5454
onCancelToken = noop,
55+
onSubmitComplete = noop,
5556
disableWhileProcessing = false,
5657
children,
5758
...props
@@ -88,6 +89,15 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
8889
return () => formEvents.forEach((e) => formElement.current?.removeEventListener(e, updateDirtyState))
8990
}, [])
9091

92+
const reset = (...fields: string[]) => {
93+
resetFormFields(formElement.current, defaults.current, fields)
94+
}
95+
96+
const resetAndClearErrors = (...fields: string[]) => {
97+
form.clearErrors(...fields)
98+
reset(...fields)
99+
}
100+
91101
const submit = () => {
92102
const [url, _data] = mergeDataIntoQueryString(
93103
resolvedMethod,
@@ -104,7 +114,14 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
104114
onBefore,
105115
onStart,
106116
onProgress,
107-
onFinish,
117+
onFinish: (...args) => {
118+
onFinish(...args)
119+
onSubmitComplete({
120+
reset,
121+
resetAndClearErrors,
122+
clearErrors: form.clearErrors,
123+
})
124+
},
108125
onCancel,
109126
onSuccess,
110127
onError,
@@ -115,27 +132,22 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
115132
form.submit(resolvedMethod, url, submitOptions)
116133
}
117134

118-
useImperativeHandle(
119-
ref,
120-
() => ({
121-
errors: form.errors,
122-
hasErrors: form.hasErrors,
123-
processing: form.processing,
124-
progress: form.progress,
125-
wasSuccessful: form.wasSuccessful,
126-
recentlySuccessful: form.recentlySuccessful,
127-
clearErrors: form.clearErrors,
128-
resetAndClearErrors: (...fields: string[]) => {
129-
form.clearErrors(...fields)
130-
resetFormFields(formElement.current, defaults.current, fields)
131-
},
132-
setError: form.setError,
133-
isDirty,
134-
reset: (...fields) => resetFormFields(formElement.current, defaults.current, fields),
135-
submit,
136-
}),
137-
[form, isDirty, submit],
138-
)
135+
const exposed = () => ({
136+
errors: form.errors,
137+
hasErrors: form.hasErrors,
138+
processing: form.processing,
139+
progress: form.progress,
140+
wasSuccessful: form.wasSuccessful,
141+
recentlySuccessful: form.recentlySuccessful,
142+
isDirty,
143+
clearErrors: form.clearErrors,
144+
resetAndClearErrors,
145+
setError: form.setError,
146+
reset,
147+
submit,
148+
})
149+
150+
useImperativeHandle(ref, exposed, [form, isDirty, submit])
139151

140152
return createElement(
141153
'form',
@@ -150,25 +162,7 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
150162
},
151163
inert: disableWhileProcessing && form.processing,
152164
},
153-
typeof children === 'function'
154-
? children({
155-
errors: form.errors,
156-
hasErrors: form.hasErrors,
157-
processing: form.processing,
158-
progress: form.progress,
159-
wasSuccessful: form.wasSuccessful,
160-
recentlySuccessful: form.recentlySuccessful,
161-
setError: form.setError,
162-
clearErrors: form.clearErrors,
163-
resetAndClearErrors: (...fields: string[]) => {
164-
form.clearErrors(...fields)
165-
resetFormFields(formElement.current, defaults.current, fields)
166-
},
167-
isDirty,
168-
reset: (...fields) => resetFormFields(formElement.current, defaults.current, fields),
169-
submit,
170-
})
171-
: children,
165+
typeof children === 'function' ? children(exposed()) : children,
172166
)
173167
},
174168
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Form } from '@inertiajs/react'
2+
3+
export default () => {
4+
return (
5+
<div>
6+
<h1>After Submit Test</h1>
7+
8+
<Form method="post" onSubmitComplete={(props) => props.reset('name')}>
9+
{({ errors }) => (
10+
<>
11+
<div>
12+
<input type="text" name="name" id="name" placeholder="Name" defaultValue="John Doe" />
13+
{errors.name && <p id="error_name">{errors.name}</p>}
14+
</div>
15+
16+
<div>
17+
<input type="email" name="email" id="email" placeholder="Email" defaultValue="[email protected]" />
18+
{errors.email && <p id="error_email">{errors.email}</p>}
19+
</div>
20+
21+
<div>
22+
<button type="submit">Submit</button>
23+
</div>
24+
</>
25+
)}
26+
</Form>
27+
</div>
28+
)
29+
}

packages/svelte/src/components/Form.svelte

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
export let onCancel: FormComponentProps['onCancel'] = noop
3131
export let onSuccess: FormComponentProps['onSuccess'] = noop
3232
export let onError: FormComponentProps['onError'] = noop
33+
export let onSubmitComplete: FormComponentProps['onSubmitComplete'] = noop
3334
export let disableWhileProcessing: boolean = false
3435
3536
type FormSubmitOptions = Omit<VisitOptions, 'data' | 'onPrefetched' | 'onPrefetching'>
@@ -68,7 +69,19 @@
6869
onBefore,
6970
onStart,
7071
onProgress,
71-
onFinish,
72+
onFinish: (visit) => {
73+
if (onFinish) {
74+
onFinish(visit)
75+
}
76+
77+
if (onSubmitComplete) {
78+
onSubmitComplete({
79+
reset,
80+
resetAndClearErrors,
81+
clearErrors,
82+
})
83+
}
84+
},
7285
onCancel,
7386
onSuccess,
7487
onError,
@@ -93,6 +106,7 @@
93106
resetFormFields(formElement, defaults, fields)
94107
}
95108
109+
96110
export function clearErrors(...fields: string[]) {
97111
// @ts-expect-error
98112
$form.clearErrors(...fields)
@@ -101,15 +115,14 @@
101115
export function resetAndClearErrors(...fields: string[]) {
102116
// @ts-expect-error
103117
$form.clearErrors(...fields)
104-
reset(fields)
118+
reset(...fields)
105119
}
106120
107121
export function setError(field: string | object, value?: string) {
108122
if (typeof field === 'string') {
109123
// @ts-expect-error
110124
$form.setError(field, value)
111125
} else {
112-
// @ts-expect-error
113126
$form.setError(field)
114127
}
115128
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script lang="ts">
2+
import { Form } from '@inertiajs/svelte'
3+
</script>
4+
5+
<div>
6+
<h1>After Submit Test</h1>
7+
8+
<Form method="post" let:errors onSubmitComplete={(props) => props.reset('name')}>
9+
<div>
10+
<input type="text" name="name" id="name" placeholder="Name" value="John Doe" />
11+
{#if errors.name}
12+
<p id="error_name">{errors.name}</p>
13+
{/if}
14+
</div>
15+
16+
<div>
17+
<input type="email" name="email" id="email" placeholder="Email" value="[email protected]" />
18+
{#if errors.email}
19+
<p id="error_email">{errors.email}</p>
20+
{/if}
21+
</div>
22+
23+
<div>
24+
<button type="submit">Submit</button>
25+
</div>
26+
</Form>
27+
</div>

packages/vue3/src/form.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
FormComponentProps,
3+
FormComponentRef,
34
FormComponentSlotProps,
45
FormDataConvertible,
56
formDataToObject,
@@ -84,6 +85,10 @@ const Form: InertiaForm = defineComponent({
8485
type: Function as PropType<FormComponentProps['onError']>,
8586
default: noop,
8687
},
88+
onSubmitComplete: {
89+
type: Function as PropType<FormComponentProps['onSubmitComplete']>,
90+
default: noop,
91+
},
8792
disableWhileProcessing: {
8893
type: Boolean,
8994
default: false,
@@ -140,7 +145,10 @@ const Form: InertiaForm = defineComponent({
140145
onBefore: props.onBefore,
141146
onStart: props.onStart,
142147
onProgress: props.onProgress,
143-
onFinish: props.onFinish,
148+
onFinish: (...args) => {
149+
props.onFinish(...args)
150+
props.onSubmitComplete(exposed)
151+
},
144152
onCancel: props.onCancel,
145153
onSuccess: props.onSuccess,
146154
onError: props.onError,
@@ -151,7 +159,16 @@ const Form: InertiaForm = defineComponent({
151159
form.transform(() => props.transform(data)).submit(method.value, action, submitOptions)
152160
}
153161

154-
expose({
162+
const reset = (...fields: string[]) => {
163+
resetFormFields(formElement.value, defaults.value, fields)
164+
}
165+
166+
const resetAndClearErrors = (...fields: string[]) => {
167+
form.clearErrors(...fields)
168+
reset(...fields)
169+
}
170+
171+
const exposed = {
155172
get errors() {
156173
return form.errors
157174
},
@@ -171,18 +188,17 @@ const Form: InertiaForm = defineComponent({
171188
return form.recentlySuccessful
172189
},
173190
clearErrors: (...fields: string[]) => form.clearErrors(...fields),
174-
resetAndClearErrors: (...fields: string[]) => {
175-
form.clearErrors(...fields)
176-
resetFormFields(formElement.value, defaults.value, fields)
177-
},
191+
resetAndClearErrors,
178192
setError: (fieldOrFields: string | Record<string, string>, maybeValue?: string) =>
179193
form.setError(typeof fieldOrFields === 'string' ? { [fieldOrFields]: maybeValue } : fieldOrFields),
180194
get isDirty() {
181195
return isDirty.value
182196
},
183-
reset: (...fields: string[]) => resetFormFields(formElement.value, defaults.value, fields),
197+
reset,
184198
submit,
185-
})
199+
}
200+
201+
expose<FormComponentRef>(exposed)
186202

187203
return () => {
188204
return h(
@@ -198,23 +214,7 @@ const Form: InertiaForm = defineComponent({
198214
},
199215
inert: props.disableWhileProcessing && form.processing,
200216
},
201-
slots.default
202-
? slots.default(<FormComponentSlotProps>{
203-
errors: form.errors,
204-
hasErrors: form.hasErrors,
205-
processing: form.processing,
206-
progress: form.progress,
207-
wasSuccessful: form.wasSuccessful,
208-
recentlySuccessful: form.recentlySuccessful,
209-
setError: (fieldOrFields: string | Record<string, string>, maybeValue?: string) =>
210-
form.setError(typeof fieldOrFields === 'string' ? { [fieldOrFields]: maybeValue } : fieldOrFields),
211-
clearErrors: (...fields: string[]) => form.clearErrors(...fields),
212-
resetAndClearErrors: (...fields: string[]) => form.resetAndClearErrors(...fields),
213-
isDirty: isDirty.value,
214-
reset: (...fields) => resetFormFields(formElement.value, defaults.value, fields),
215-
submit,
216-
})
217-
: [],
217+
slots.default ? slots.default(<FormComponentSlotProps>exposed) : [],
218218
)
219219
}
220220
},
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<script setup lang="ts">
2+
import { Form } from '@inertiajs/vue3'
3+
</script>
4+
5+
<template>
6+
<div>
7+
<h1>After Submit Test</h1>
8+
9+
<Form method="post" #default="{ errors }" @submit-complete="(props) => props.reset('name')">
10+
<div>
11+
<input type="text" name="name" id="name" placeholder="Name" value="John Doe" />
12+
<p v-if="errors.name" id="error_name">{{ errors.name }}</p>
13+
</div>
14+
15+
<div>
16+
<input type="email" name="email" id="email" placeholder="Email" value="[email protected]" />
17+
<p v-if="errors.email" id="error_email">{{ errors.email }}</p>
18+
</div>
19+
20+
<div>
21+
<button type="submit">Submit</button>
22+
</div>
23+
</Form>
24+
</div>
25+
</template>

tests/app/server.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,9 @@ app.get('/form-component/progress', (req, res) => inertia.render(req, res, { com
568568
app.post('/form-component/progress', async (req, res) =>
569569
setTimeout(() => inertia.render(req, res, { component: 'FormComponent/Progress' }), 500),
570570
)
571+
app.get('/form-component/after-submit', (req, res) =>
572+
inertia.render(req, res, { component: 'FormComponent/AfterSubmit' }),
573+
)
571574
app.get('/form-component/state', (req, res) => inertia.render(req, res, { component: 'FormComponent/State' }))
572575
app.get('/form-component/dotted-keys', (req, res) =>
573576
inertia.render(req, res, { component: 'FormComponent/DottedKeys' }),

0 commit comments

Comments
 (0)