Skip to content

Commit

Permalink
Add support for form arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
unlocomqx committed Apr 11, 2024
1 parent 992b693 commit 37b7b93
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 2 deletions.
30 changes: 28 additions & 2 deletions src/lib/client/superForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,30 @@ try {
// No Storybook
}

const lifeCycleCallbacks = {
onDestroy: new Set<() => void>(),
beforeNavigate: new Set<(nav: BeforeNavigate) => Promise<void>>()
}
let componentInitialized = false;
function initLifeCycleCallbacks() {
if (componentInitialized) return;
componentInitialized = true;

onDestroy(() => {
for (const callback of lifeCycleCallbacks.onDestroy) {
callback();
}
lifeCycleCallbacks.onDestroy.clear();
});

beforeNavigate((nav: BeforeNavigate) => {
for (const callback of lifeCycleCallbacks.beforeNavigate) {
callback(nav);
}
lifeCycleCallbacks.beforeNavigate.clear();
});
}

/////////////////////////////////////////////////////////////////////

/**
Expand All @@ -410,6 +434,8 @@ export function superForm<
// To check if a full validator is used when switching options.validators dynamically
let initialValidator: FormOptions<T, M, In>['validators'] | undefined = undefined;

initLifeCycleCallbacks();

{
if (options.legacy ?? LEGACY_MODE) {
if (options.resetForm === undefined) options.resetForm = false;
Expand Down Expand Up @@ -525,7 +551,7 @@ export function superForm<

///// From here, form is properly initialized /////

onDestroy(() => {
lifeCycleCallbacks.onDestroy.add(() => {
Unsubscriptions_unsubscribe();
NextChange_clear();
EnhancedForm_destroy();
Expand Down Expand Up @@ -1397,7 +1423,7 @@ export function superForm<
///// Store subscriptions ///////////////////////////////////////////////////

if (browser) {
beforeNavigate(Tainted_check);
lifeCycleCallbacks.beforeNavigate.add(Tainted_check);

// Need to subscribe to catch page invalidation.
Unsubscriptions_add(
Expand Down
1 change: 1 addition & 0 deletions src/routes/(v2)/v2/Navigation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'issue-337-checkboxes',
'issue-345',
'letters',
'multiple-forms',
'multiple-files',
'multistep-client',
'multistep-server',
Expand Down
58 changes: 58 additions & 0 deletions src/routes/(v2)/v2/multiple-forms/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { zod } from '$lib/adapters/zod.js';
import { message, superValidate } from '$lib/server/index.js';
import { type Actions, fail } from '@sveltejs/kit';
import { schema } from './schema.js';

const items = [
{
id: 1,
label: 'One'
},
{
id: 2,
label: 'Two'
},
{
id: 3,
label: 'Three'
}
];

export const load = async () => {
const item_forms = await Promise.all(
items.map((item) =>
superValidate(item, zod(schema), {
id: item.id.toString()
})
)
);

return { item_forms };
};

export const actions: Actions = {
async create() {
items.push({
id: items.length + 1,
label: (items.length + 1).toString()
});

return { success: true };
},

async save({ request }) {
const form = await superValidate(request, zod(schema));

if (!form.valid) {
// Again, return { form } and things will just work.
return fail(400, { form });
}

const index = items.findIndex((item) => item.id === form.data.id);
if(index !== -1) {
items[index].label = form.data.label;
}

return message(form, `Item ${form.data.id} updated`);
}
};
26 changes: 26 additions & 0 deletions src/routes/(v2)/v2/multiple-forms/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script lang="ts">
import type { PageData } from './$types.js';
import { superForm } from '$lib/client/index.js';
import Form from './Form.svelte';
import { enhance } from '$app/forms';
let {
data
}: {
data: PageData;
} = $props();
const superforms = $derived(data.item_forms.map(item_form => superForm(item_form, {
id: item_form.id,
dataType: 'json'
})));
</script>

{#each superforms as superform (superform.formId)}
<Form {superform} />
<hr>
{/each}

<form action="?/create" method=post use:enhance>
<button type=submit>Add</button>
</form>
28 changes: 28 additions & 0 deletions src/routes/(v2)/v2/multiple-forms/Form.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import type { SuperForm } from '$lib/index.js';
import type { schema } from './schema.js';
import { z } from 'zod';
import SuperDebug from '$lib/client/SuperDebug.svelte';
let { superform }: {
superform: SuperForm<z.infer<typeof schema>>;
} = $props();
let { form, message, enhance } = superform;
</script>

<form action="?/save" method="post" use:enhance>
<input type="hidden" name="id" value={$form.id} />

<SuperDebug data={$form} />

<label>
<input type="text" name="label" bind:value={$form.label} />
</label>

<button type="submit">Save</button>

{#if $message}
<p>{$message}</p>
{/if}
</form>
6 changes: 6 additions & 0 deletions src/routes/(v2)/v2/multiple-forms/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { z } from 'zod';

export const schema = z.object({
id: z.number(),
label: z.string()
});

0 comments on commit 37b7b93

Please sign in to comment.