Skip to content

Commit 0338a6d

Browse files
committed
Fixed client validation problems with multi-select fields.
1 parent ca2cbef commit 0338a6d

File tree

6 files changed

+136
-5
lines changed

6 files changed

+136
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- `capture` and `restore` are now hoisted functions.
1313
- Fixed timing issues with radio buttons and validation with side-effects.
1414
- Using standard array access to fix problems with iOS and iPadOS <= 15.3
15+
- Fixed client validation problems with multi-select fields.
1516

1617
## [0.8.4] - 2023-04-27
1718

src/lib/client/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,20 @@ async function validateField<T extends AnyZodObject, M>(
10961096
perFieldValidator.key
10971097
);
10981098
if (validator) {
1099+
// Check if validator is ZodArray and the path is an array access
1100+
// in that case validate the whole array.
1101+
if (
1102+
Context.currentData &&
1103+
validator._def.typeName == 'ZodArray' &&
1104+
!isNaN(parseInt(path[path.length - 1]))
1105+
) {
1106+
const validateArray = traversePath(
1107+
Context.currentData,
1108+
path.slice(0, -1) as FieldPath<typeof Context.currentData>
1109+
);
1110+
Context.value = validateArray?.value;
1111+
}
1112+
10991113
//console.log('🚀 ~ file: index.ts:972 ~ no effects:', validator);
11001114
const result = await validator.safeParseAsync(Context.value);
11011115
if (!result.success) {

src/routes/+layout.svelte

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@
2626
<a href="/super-debug">SuperDebug</a> |
2727
<a href="/nested">Nested</a> |
2828
<a href="/multiple">Multiple</a> |
29-
<a href="/multiple2">Multiple 2</a>
3029
<br />
30+
<a href="/multiple2">Multiple 2</a>
3131
<a href="/snapshot">Snapshot</a> |
3232
<a href="/dates">Dates</a> |
3333
<a href="/reset">Reset</a> |
3434
<a href="/url">URL</a> |
3535
<a href="/tainted">Tainted</a> |
36-
<a href="/proxies">Proxies</a> |
37-
<a href="/properly-nested">Properly nested</a>
36+
<a href="/proxies">Proxies</a>
3837
<br />
38+
<a href="/properly-nested">Properly nested</a>
3939
<a href="/files">Files</a> |
4040
<a href="/spa">SPA</a> |
4141
<a href="/nested-validation">Client-side validation</a> |
42-
<a href="/tests/rex">Side-effect validation</a> |
43-
<a href="/tests/superform-c">Side-effect 2</a>
42+
<a href="/tests/rex">Side-effect validation</a>
43+
<br />
44+
<a href="/tests/superform-c">Side-effect 2</a> |
45+
<a href="/tests/multiselect">Multi-select</a>
4446
</nav>
4547

4648
<slot />
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { fail } from '@sveltejs/kit';
2+
import { message, superValidate } from '$lib/server';
3+
import type { Actions, PageServerLoad } from './$types';
4+
5+
import { formSchema } from './schemas';
6+
7+
export const load: PageServerLoad = async () => {
8+
const form = await superValidate(formSchema);
9+
10+
return { form };
11+
};
12+
13+
export const actions: Actions = {
14+
default: async (request) => {
15+
const form = await superValidate(request, formSchema);
16+
console.log(form.valid);
17+
if (!form.valid) {
18+
return fail(400, { form });
19+
}
20+
21+
return message(form, 'Posted!');
22+
}
23+
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<script lang="ts">
2+
import { superForm } from '$lib/client';
3+
import SuperDebug from '$lib/client/SuperDebug.svelte';
4+
import { formSchema, optionsSchema } from './schemas';
5+
6+
export let data;
7+
8+
const { form, enhance, message, errors, allErrors, tainted } = superForm(
9+
data.form,
10+
{
11+
dataType: 'json',
12+
validators: formSchema,
13+
validationMethod: 'onblur',
14+
defaultValidator: 'clear'
15+
}
16+
);
17+
18+
$: console.log($allErrors);
19+
$: options = optionsSchema._def.innerType.options;
20+
</script>
21+
22+
<SuperDebug data={{ $form, $errors, $tainted }} />
23+
24+
{#if $message}<p>{$message}</p>{/if}
25+
26+
<form method="POST" use:enhance>
27+
<div>
28+
<label for="multiselect">Multiselect</label>
29+
<select
30+
multiple
31+
name="multiselect"
32+
bind:value={$form.multiselect}
33+
data-invalid={$errors.multiselect}
34+
>
35+
{#each options as option}
36+
<option value={option}>{option}</option>
37+
{/each}
38+
</select>
39+
</div>
40+
<div>
41+
<label for="select">Select</label>
42+
<select
43+
name="select"
44+
bind:value={$form.select}
45+
data-invalid={$errors.select}
46+
>
47+
{#each options as option}
48+
<option value={option}>{option}</option>
49+
{/each}
50+
</select>
51+
</div>
52+
{#if $allErrors && $allErrors.length}
53+
<ul class="invalid">
54+
{#each $allErrors as error}<li>
55+
{String(error.path)}: {error.message}
56+
</li>{/each}
57+
</ul>
58+
{/if}
59+
60+
<button type="submit">Submit</button>
61+
</form>
62+
63+
<style>
64+
label {
65+
display: block;
66+
margin-left: 5px;
67+
}
68+
input {
69+
margin: 5px;
70+
}
71+
.invalid {
72+
color: red;
73+
}
74+
button {
75+
background-color: black;
76+
color: white;
77+
padding: 5px;
78+
display: inline-block;
79+
margin: 5px;
80+
}
81+
</style>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { z } from 'zod';
2+
3+
export const optionsSchema = z
4+
.enum(['option_1', 'option_2', 'option_3', 'option_4'])
5+
.default('option_1');
6+
7+
export const formSchema = z.object({
8+
multiselect: optionsSchema.array(),
9+
select: optionsSchema
10+
});

0 commit comments

Comments
 (0)