Skip to content

Commit 8fb0c04

Browse files
committed
Nested Superforms validators didn't work when a field was missing compared to the schema.
1 parent 783aefa commit 8fb0c04

File tree

5 files changed

+166
-6
lines changed

5 files changed

+166
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ Headlines: Added, Changed, Deprecated, Removed, Fixed, Security
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased] - 2023-09-11
8+
## [1.7.0] - 2023-09-16
99

1010
### Fixed
1111

1212
- Type error with `formFieldProxy` when using a strongly typed status message. ([#260](https://github.com/ciscoheat/sveltekit-superforms/issues/260))
13+
- Nested Superforms validators didn't work when a field was missing compared to the schema.
1314

1415
### Added
1516

src/lib/client/clientValidation.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -504,16 +504,12 @@ async function _validateField<T extends ZodValidation<AnyZodObject>, M>(
504504
}
505505
} else {
506506
// SuperForms validator
507-
508507
const validator = traversePath(
509508
validators,
510509
Context.validationPath as FieldPath<typeof validators>
511510
);
512511

513-
if (!validator) {
514-
// Path didn't exist
515-
throw new SuperFormError('No Superforms validator found: ' + path);
516-
} else if (validator.value === undefined) {
512+
if (!validator || validator.value === undefined) {
517513
// No validator, use default
518514
return defaultValidate();
519515
} else {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { superValidate, message } from '$lib/server';
2+
import { fail } from '@sveltejs/kit';
3+
import { schema } from './schema';
4+
5+
import type { Actions, PageServerLoad } from './$types';
6+
7+
///// Load function /////
8+
9+
export const load: PageServerLoad = async () => {
10+
const form = await superValidate(schema);
11+
return { form };
12+
};
13+
14+
///// Form actions /////
15+
16+
export const actions: Actions = {
17+
default: async ({ request }) => {
18+
const form = await superValidate(request, schema);
19+
20+
console.log('POST', form);
21+
22+
if (!form.valid) return fail(400, { form });
23+
24+
return message(form, 'Form posted successfully!');
25+
}
26+
};
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<script lang="ts">
2+
import { page } from '$app/stores';
3+
import { superForm } from '$lib/client';
4+
import SuperDebug from '$lib/client/SuperDebug.svelte';
5+
6+
export let data;
7+
8+
const { form, errors, message, enhance } = superForm(data.form, {
9+
dataType: 'json',
10+
taintedMessage: null,
11+
validators: {
12+
business_id: (input) =>
13+
input ? undefined : 'Please fill business name first'
14+
/*
15+
// Uncomment for testing purposes, but will fail browser test
16+
shareholders: {
17+
id_issuance_date: (date) => (date ? 'Nope' : null)
18+
}
19+
*/
20+
},
21+
onError({ result, message }) {
22+
console.log('error');
23+
console.log(result);
24+
console.log(message);
25+
}
26+
});
27+
</script>
28+
29+
<h3>Add shareholder</h3>
30+
31+
{#if $message}
32+
<div
33+
class="status"
34+
class:error={$page.status >= 400}
35+
class:success={$page.status == 200}
36+
>
37+
{$message}
38+
</div>
39+
{/if}
40+
41+
<form method="POST" use:enhance>
42+
<label>
43+
Business name
44+
<input bind:value={$form.business_id} />
45+
{#if $errors.business_id}<span class="invalid"
46+
>{$errors.business_id}</span
47+
>{/if}
48+
</label>
49+
50+
<button
51+
type="button"
52+
on:click={() =>
53+
($form.shareholders = [
54+
...$form.shareholders,
55+
{ id_issuance_date: new Date() }
56+
])}>Add shareholder</button
57+
>
58+
59+
{#if $errors?.shareholders?._errors}
60+
<div class="invalid">{$errors.shareholders._errors}</div>
61+
{/if}
62+
63+
{#each $form.shareholders as _, i}
64+
{@const error = $errors?.shareholders?.[i]?.id_issuance_date}
65+
<label>
66+
Issuance date<br />
67+
<input
68+
aria-invalid={error ? 'true' : undefined}
69+
bind:value={$form.shareholders[i].id_issuance_date}
70+
/>
71+
{#if error}
72+
<span class="invalid"
73+
>{$errors?.shareholders?.[i].id_issuance_date}</span
74+
>
75+
{/if}
76+
</label>
77+
{/each}
78+
79+
<div>
80+
<br />
81+
<button>Submit</button>
82+
</div>
83+
</form>
84+
85+
<SuperDebug data={{ $form, $errors }} />
86+
87+
<style>
88+
.invalid {
89+
color: red;
90+
}
91+
92+
.status {
93+
color: white;
94+
padding: 4px;
95+
padding-left: 8px;
96+
border-radius: 2px;
97+
font-weight: 500;
98+
}
99+
100+
.status.success {
101+
background-color: seagreen;
102+
}
103+
104+
.status.error {
105+
background-color: #ff2a02;
106+
}
107+
108+
input {
109+
background-color: #ddd;
110+
}
111+
112+
a {
113+
text-decoration: underline;
114+
}
115+
116+
hr {
117+
margin-top: 4rem;
118+
}
119+
120+
form {
121+
padding-top: 1rem;
122+
padding-bottom: 1rem;
123+
}
124+
</style>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { z } from 'zod';
2+
3+
export const schema = z.object({
4+
business_id: z.string(),
5+
shareholders: z
6+
.object({
7+
first_name: z.string().optional(),
8+
middle_name: z.string().optional(),
9+
id_issuance_date: z.coerce.date().max(new Date()).optional()
10+
})
11+
.array()
12+
.min(1)
13+
});

0 commit comments

Comments
 (0)