Skip to content

Commit 94feac9

Browse files
committed
Added tainted to formFieldProxy.
1 parent 8bff60b commit 94feac9

File tree

6 files changed

+123
-3
lines changed

6 files changed

+123
-3
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ 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]
8+
## [1.8.0]
99

1010
### Fixed
1111

12-
- [Array errors](https://superforms.rocks/concepts/error-handling#form-level-and-array-errors) were always added, even if the form wasn't tainted.
12+
- [Array errors](https://superforms.rocks/concepts/error-handling#form-level-and-array-errors) were always added, even if the array or any data in it hadn't tainted the form.
13+
14+
### Added
15+
16+
- [formFieldProxy](https://superforms.rocks/components#using-a-formfieldproxy) now contains a proxy for `tainted`.
1317

1418
## [1.7.4] - 2023-09-29
1519

src/lib/client/proxies.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,49 @@ export function formFieldProxy<
245245
value: Writable<FormPathType<z.infer<UnwrapEffects<T>>, Path>>;
246246
errors: Writable<string[] | undefined>;
247247
constraints: Writable<InputConstraint | undefined>;
248+
tainted: Writable<boolean | undefined>;
248249
} {
249250
const path2 = splitPath<z.infer<UnwrapEffects<T>>>(path);
250251
// Filter out array indices, the constraints structure doesn't contain these.
251252
const constraintsPath = (path2 as unknown[])
252253
.filter((p) => isNaN(parseInt(String(p))))
253254
.join('.');
254255

256+
const taintedProxy = derived<typeof form.tainted, boolean | undefined>(
257+
form.tainted,
258+
($tainted) => {
259+
if (!$tainted) return $tainted;
260+
const taintedPath = traversePath($tainted, path2);
261+
return taintedPath ? taintedPath.value : undefined;
262+
}
263+
);
264+
265+
const tainted = {
266+
subscribe: taintedProxy.subscribe,
267+
update(upd: Updater<boolean | undefined>) {
268+
form.tainted.update(($tainted) => {
269+
if (!$tainted) $tainted = {};
270+
const output = traversePath($tainted, path2, (path) => {
271+
if (!path.value) path.parent[path.key] = {};
272+
return path.parent[path.key];
273+
});
274+
if (output) output.parent[output.key] = upd(output.value);
275+
return $tainted;
276+
});
277+
},
278+
set(value: boolean | undefined) {
279+
form.tainted.update(($tainted) => {
280+
if (!$tainted) $tainted = {};
281+
const output = traversePath($tainted, path2, (path) => {
282+
if (!path.value) path.parent[path.key] = {};
283+
return path.parent[path.key];
284+
});
285+
if (output) output.parent[output.key] = value;
286+
return $tainted;
287+
});
288+
}
289+
};
290+
255291
return {
256292
path,
257293
value: fieldProxy(form.form, path),
@@ -261,7 +297,8 @@ export function formFieldProxy<
261297
constraints: fieldProxy(
262298
form.constraints,
263299
constraintsPath as never
264-
) as Writable<InputConstraint | undefined>
300+
) as Writable<InputConstraint | undefined>,
301+
tainted
265302
};
266303
}
267304

src/routes/Navigation.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<a href="/tests/flash-onerror">Flash onError</a>
2828
<a href="/tests/reset-component">Reset component 1</a>
2929
<a href="/tests/reset-component-2">Reset component 2</a>
30+
<a href="/tests/tainted-proxy">Tainted formFieldProxy</a>
3031
</nav>
3132

3233
<style lang="scss">
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { message, superValidate } from '$lib/server';
2+
import { schema } from './schema';
3+
import { fail } from '@sveltejs/kit';
4+
5+
export const load = async () => {
6+
const form = await superValidate(schema);
7+
return { form };
8+
};
9+
10+
export const actions = {
11+
default: async ({ request }) => {
12+
const formData = await request.formData();
13+
console.log(formData);
14+
15+
const form = await superValidate(formData, schema);
16+
console.log('POST', form);
17+
18+
if (!form.valid) return fail(400, { form });
19+
20+
return message(form, 'Posted OK!');
21+
}
22+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<script lang="ts">
2+
import { formFieldProxy, superForm } from '$lib/client';
3+
import type { PageData } from './$types';
4+
import SuperDebug from '$lib/client/SuperDebug.svelte';
5+
6+
export let data: PageData;
7+
8+
const form = superForm(data.form, {
9+
dataType: 'json'
10+
});
11+
12+
const { message, tainted: formTainted, enhance } = form;
13+
const { value, errors, tainted } = formFieldProxy(form, 'user.name');
14+
</script>
15+
16+
<SuperDebug data={$formTainted} />
17+
18+
{#if $message}<h4>{$message}</h4>{/if}
19+
20+
<p>Proxy: {$tainted} - Form: {$formTainted?.user?.name}</p>
21+
22+
<p>
23+
<br />
24+
<button on:click={() => ($tainted = false)}>Untaint user.name</button>
25+
</p>
26+
27+
<form method="POST" use:enhance>
28+
<label>
29+
Name: <input name="name" bind:value={$value} />
30+
{#if $errors}<span class="invalid">{$errors}</span>{/if}
31+
</label>
32+
<div>
33+
<button>Submit</button>
34+
</div>
35+
</form>
36+
37+
<style lang="scss">
38+
form {
39+
margin: 2rem 0;
40+
41+
input {
42+
background-color: #dedede;
43+
}
44+
45+
.invalid {
46+
color: crimson;
47+
}
48+
}
49+
</style>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { z } from 'zod';
2+
3+
export const schema = z.object({
4+
user: z.object({
5+
name: z.string().min(1)
6+
})
7+
});

0 commit comments

Comments
 (0)