Skip to content

Commit aac2764

Browse files
Add global configuration support for withAllErrors in form components (#2865)
* Add global configuration support for withAllErrors in form components * Default `withAllErrors` to null --------- Co-authored-by: Pascal Baljet <pascal@pascalbaljet.nl>
1 parent 3057d0a commit aac2764

File tree

15 files changed

+372
-15
lines changed

15 files changed

+372
-15
lines changed

packages/core/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const config = new Config<InertiaAppConfig>({
7474
form: {
7575
recentlySuccessfulDuration: 2_000,
7676
forceIndicesArrayFormatInFormData: true,
77+
withAllErrors: false,
7778
},
7879
future: {
7980
preserveEqualProps: false,

packages/core/src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ export type InertiaAppConfig = {
532532
form: {
533533
recentlySuccessfulDuration: number
534534
forceIndicesArrayFormatInFormData: boolean
535+
withAllErrors: boolean
535536
}
536537
// experimental: {
537538
// /* not guaranteed */
@@ -661,7 +662,7 @@ export type FormComponentProps = Partial<
661662
setDefaultsOnSuccess?: boolean
662663
validateFiles?: boolean
663664
validationTimeout?: number
664-
withAllErrors?: boolean
665+
withAllErrors?: boolean | null
665666
}
666667

667668
export type FormComponentMethods = {

packages/react/src/Form.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
config,
23
FormComponentProps,
34
FormComponentRef,
45
FormComponentResetSymbol,
@@ -75,7 +76,7 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
7576
invalidateCacheTags = [],
7677
validateFiles = false,
7778
validationTimeout = 1500,
78-
withAllErrors = false,
79+
withAllErrors = null,
7980
children,
8081
...props
8182
},
@@ -97,7 +98,7 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
9798
form.validateFiles()
9899
}
99100

100-
if (withAllErrors) {
101+
if (withAllErrors ?? config.get('form.withAllErrors')) {
101102
form.withAllErrors()
102103
}
103104

packages/react/src/useForm.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export default function useForm<TForm extends FormDataType<TForm>>(
158158
const [validating, setValidating] = useState(false)
159159
const [touchedFields, setTouchedFields] = useState<string[]>([])
160160
const [validFields, setValidFields] = useState<string[]>([])
161-
const withAllErrors = useRef(false)
161+
const withAllErrors = useRef<boolean | null>(null)
162162

163163
useEffect(() => {
164164
isMounted.current = true
@@ -533,9 +533,10 @@ export default function useForm<TForm extends FormDataType<TForm>>(
533533
setTouchedFields(validator.touched())
534534
})
535535
.on('errorsChanged', () => {
536-
const validationErrors = withAllErrors.current
537-
? validator.errors()
538-
: toSimpleValidationErrors(validator.errors())
536+
const validationErrors =
537+
(withAllErrors.current ?? config.get('form.withAllErrors'))
538+
? validator.errors()
539+
: toSimpleValidationErrors(validator.errors())
539540

540541
setErrors(validationErrors as FormDataErrors<TForm>)
541542
setHasErrors(Object.keys(validationErrors).length > 0)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { config, Form } from '@inertiajs/react'
2+
3+
export default () => {
4+
// Set global config for withAllErrors (no prop on the Form component)
5+
config.set('form.withAllErrors', true)
6+
7+
return (
8+
<div>
9+
<h1>Form Precognition - All Errors via Config</h1>
10+
11+
<Form action="/precognition/with-all-errors" method="post" validationTimeout={100}>
12+
{({ invalid, errors, validate, valid, validating }) => (
13+
<>
14+
<div>
15+
<input name="name" placeholder="Name" onBlur={() => validate('name')} />
16+
{invalid('name') && (
17+
<div>
18+
{Array.isArray(errors.name) ? (
19+
errors.name.map((error, index) => (
20+
<p key={index} id={`name-error-${index}`}>
21+
{error}
22+
</p>
23+
))
24+
) : (
25+
<p id="name-error-0">{errors.name}</p>
26+
)}
27+
</div>
28+
)}
29+
{valid('name') && <p>Name is valid!</p>}
30+
</div>
31+
32+
<div>
33+
<input name="email" placeholder="Email" onBlur={() => validate('email')} />
34+
{invalid('email') && (
35+
<div>
36+
{Array.isArray(errors.email) ? (
37+
errors.email.map((error, index) => (
38+
<p key={index} id={`email-error-${index}`}>
39+
{error}
40+
</p>
41+
))
42+
) : (
43+
<p id="email-error-0">{errors.email}</p>
44+
)}
45+
</div>
46+
)}
47+
{valid('email') && <p>Email is valid!</p>}
48+
</div>
49+
50+
{validating && <p>Validating...</p>}
51+
</>
52+
)}
53+
</Form>
54+
</div>
55+
)
56+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { config, useForm } from '@inertiajs/react'
2+
3+
export default () => {
4+
// Set global config for withAllErrors (no .withAllErrors() call on the form)
5+
config.set('form.withAllErrors', true)
6+
7+
const form = useForm({
8+
name: '',
9+
email: '',
10+
})
11+
.withPrecognition('post', '/precognition/with-all-errors')
12+
.setValidationTimeout(100)
13+
14+
return (
15+
<div>
16+
<div>
17+
<input
18+
value={form.data.name}
19+
name="name"
20+
placeholder="Name"
21+
onChange={(e) => form.setData('name', e.target.value)}
22+
onBlur={() => form.validate('name')}
23+
/>
24+
{form.invalid('name') && (
25+
<div>
26+
{Array.isArray(form.errors.name) ? (
27+
form.errors.name.map((error, index) => (
28+
<p key={index} id={`name-error-${index}`}>
29+
{error}
30+
</p>
31+
))
32+
) : (
33+
<p id="name-error-0">{form.errors.name}</p>
34+
)}
35+
</div>
36+
)}
37+
{form.valid('name') && <p>Name is valid!</p>}
38+
</div>
39+
40+
<div>
41+
<input
42+
value={form.data.email}
43+
name="email"
44+
placeholder="Email"
45+
onChange={(e) => form.setData('email', e.target.value)}
46+
onBlur={() => form.validate('email')}
47+
/>
48+
{form.invalid('email') && (
49+
<div>
50+
{Array.isArray(form.errors.email) ? (
51+
form.errors.email.map((error, index) => (
52+
<p key={index} id={`email-error-${index}`}>
53+
{error}
54+
</p>
55+
))
56+
) : (
57+
<p id="email-error-0">{form.errors.email}</p>
58+
)}
59+
</div>
60+
)}
61+
{form.valid('email') && <p>Email is valid!</p>}
62+
</div>
63+
64+
{form.validating && <p>Validating...</p>}
65+
</div>
66+
)
67+
}

packages/svelte/src/components/Form.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script lang="ts">
22
import {
3+
config,
34
formDataToObject,
45
FormComponentResetSymbol,
56
resetFormFields,
@@ -46,7 +47,7 @@
4647
export let setDefaultsOnSuccess: FormComponentProps['setDefaultsOnSuccess'] = false
4748
export let validateFiles: FormComponentProps['validateFiles'] = false
4849
export let validationTimeout: FormComponentProps['validationTimeout'] = 1500
49-
export let withAllErrors: FormComponentProps['withAllErrors'] = false
50+
export let withAllErrors: FormComponentProps['withAllErrors'] = null
5051
5152
type FormSubmitOptions = Omit<VisitOptions, 'data' | 'onPrefetched' | 'onPrefetching'>
5253
type FormSubmitter = HTMLElement | null
@@ -67,7 +68,7 @@
6768
form.validateFiles()
6869
}
6970
70-
if (withAllErrors) {
71+
if (withAllErrors ?? config.get('form.withAllErrors')) {
7172
form.withAllErrors()
7273
}
7374

packages/svelte/src/useForm.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export default function useForm<TForm extends FormDataType<TForm>>(
182182
const formWithPrecognition = () =>
183183
getStore(store) as any as InertiaPrecognitiveForm<TForm> & InternalPrecognitionState
184184

185-
let withAllErrors = false
185+
let withAllErrors: boolean | null = null
186186

187187
if (!validatorRef) {
188188
const validator = createValidator((client) => {
@@ -205,7 +205,10 @@ export default function useForm<TForm extends FormDataType<TForm>>(
205205
setFormState('__touched', validator.touched())
206206
})
207207
.on('errorsChanged', () => {
208-
const validationErrors = withAllErrors ? validator.errors() : toSimpleValidationErrors(validator.errors())
208+
const validationErrors =
209+
(withAllErrors ?? config.get('form.withAllErrors'))
210+
? validator.errors()
211+
: toSimpleValidationErrors(validator.errors())
209212

210213
setFormState('errors', {} as FormDataErrors<TForm>)
211214
formWithPrecognition().setError(validationErrors as FormDataErrors<TForm>)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<script lang="ts">
2+
import { config, Form } from '@inertiajs/svelte'
3+
4+
// Set global config for withAllErrors (no prop on the Form component)
5+
config.set('form.withAllErrors', true)
6+
</script>
7+
8+
<div>
9+
<h1>Form Precognition - All Errors via Config</h1>
10+
11+
<Form
12+
action="/precognition/with-all-errors"
13+
method="post"
14+
validationTimeout={100}
15+
let:invalid
16+
let:errors
17+
let:validate
18+
let:valid
19+
let:validating
20+
>
21+
<div>
22+
<input name="name" placeholder="Name" on:blur={() => validate('name')} />
23+
{#if invalid('name')}
24+
<div>
25+
{#if Array.isArray(errors.name)}
26+
{#each errors.name as error, index (index)}
27+
<p id="name-error-{index}">{error}</p>
28+
{/each}
29+
{:else}
30+
<p id="name-error-0">{errors.name}</p>
31+
{/if}
32+
</div>
33+
{/if}
34+
{#if valid('name')}
35+
<p>Name is valid!</p>
36+
{/if}
37+
</div>
38+
39+
<div>
40+
<input name="email" placeholder="Email" on:blur={() => validate('email')} />
41+
{#if invalid('email')}
42+
<div>
43+
{#if Array.isArray(errors.email)}
44+
{#each errors.email as error, index (index)}
45+
<p id="email-error-{index}">{error}</p>
46+
{/each}
47+
{:else}
48+
<p id="email-error-0">{errors.email}</p>
49+
{/if}
50+
</div>
51+
{/if}
52+
{#if valid('email')}
53+
<p>Email is valid!</p>
54+
{/if}
55+
</div>
56+
57+
{#if validating}
58+
<p>Validating...</p>
59+
{/if}
60+
</Form>
61+
</div>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script lang="ts">
2+
import { config, useForm } from '@inertiajs/svelte'
3+
4+
// Set global config for withAllErrors (no .withAllErrors() call on the form)
5+
config.set('form.withAllErrors', true)
6+
7+
const form = useForm({
8+
name: '',
9+
email: '',
10+
})
11+
.withPrecognition('post', '/precognition/with-all-errors')
12+
.setValidationTimeout(100)
13+
</script>
14+
15+
<div>
16+
<div>
17+
<input bind:value={$form.name} name="name" placeholder="Name" on:blur={() => form.validate('name')} />
18+
{#if $form.invalid('name')}
19+
<div>
20+
{#if Array.isArray($form.errors.name)}
21+
{#each $form.errors.name as error, index (index)}
22+
<p id="name-error-{index}">{error}</p>
23+
{/each}
24+
{:else}
25+
<p id="name-error-0">{$form.errors.name}</p>
26+
{/if}
27+
</div>
28+
{/if}
29+
{#if $form.valid('name')}
30+
<p>Name is valid!</p>
31+
{/if}
32+
</div>
33+
34+
<div>
35+
<input bind:value={$form.email} name="email" placeholder="Email" on:blur={() => form.validate('email')} />
36+
{#if $form.invalid('email')}
37+
<div>
38+
{#if Array.isArray($form.errors.email)}
39+
{#each $form.errors.email as error, index (index)}
40+
<p id="email-error-{index}">{error}</p>
41+
{/each}
42+
{:else}
43+
<p id="email-error-0">{$form.errors.email}</p>
44+
{/if}
45+
</div>
46+
{/if}
47+
{#if $form.valid('email')}
48+
<p>Email is valid!</p>
49+
{/if}
50+
</div>
51+
52+
{#if $form.validating}
53+
<p>Validating...</p>
54+
{/if}
55+
</div>

0 commit comments

Comments
 (0)