diff --git a/src/lib/client/superForm.ts b/src/lib/client/superForm.ts index cc61b04d..8712b00c 100644 --- a/src/lib/client/superForm.ts +++ b/src/lib/client/superForm.ts @@ -389,6 +389,30 @@ try { // No Storybook } +const lifeCycleCallbacks = { + onDestroy: new Set<() => void>(), + beforeNavigate: new Set<(nav: BeforeNavigate) => Promise>() +} +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(); + }); +} + ///////////////////////////////////////////////////////////////////// /** @@ -410,6 +434,8 @@ export function superForm< // To check if a full validator is used when switching options.validators dynamically let initialValidator: FormOptions['validators'] | undefined = undefined; + initLifeCycleCallbacks(); + { if (options.legacy ?? LEGACY_MODE) { if (options.resetForm === undefined) options.resetForm = false; @@ -465,7 +491,12 @@ export function superForm< form = form as SuperValidated; // Assign options.id to form, if it exists - const _initialFormId = (form.id = options.id ?? form.id); + // Avoid reassigning id in runes mode to avoid ERR_SVELTE_UNSAFE_MUTATION + // TODO: detect runes mode if possible & warn if id is different + let _initialFormId = form.id + if (options.id && form.id !== options.id) { + _initialFormId = options.id; + } const _currentPage = get(page) ?? (STORYBOOK_MODE ? {} : undefined); // Check multiple id's @@ -525,7 +556,7 @@ export function superForm< ///// From here, form is properly initialized ///// - onDestroy(() => { + lifeCycleCallbacks.onDestroy.add(() => { Unsubscriptions_unsubscribe(); NextChange_clear(); EnhancedForm_destroy(); @@ -1397,7 +1428,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( diff --git a/src/routes/(v2)/v2/Navigation.svelte b/src/routes/(v2)/v2/Navigation.svelte index 1aeebb3f..184754e8 100644 --- a/src/routes/(v2)/v2/Navigation.svelte +++ b/src/routes/(v2)/v2/Navigation.svelte @@ -14,6 +14,7 @@ 'issue-337-checkboxes', 'issue-345', 'letters', + 'multiple-forms', 'multiple-files', 'multistep-client', 'multistep-server', diff --git a/src/routes/(v2)/v2/multiple-forms/+page.server.ts b/src/routes/(v2)/v2/multiple-forms/+page.server.ts new file mode 100644 index 00000000..6321d1ac --- /dev/null +++ b/src/routes/(v2)/v2/multiple-forms/+page.server.ts @@ -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`); + } +}; diff --git a/src/routes/(v2)/v2/multiple-forms/+page.svelte b/src/routes/(v2)/v2/multiple-forms/+page.svelte new file mode 100644 index 00000000..932b88c3 --- /dev/null +++ b/src/routes/(v2)/v2/multiple-forms/+page.svelte @@ -0,0 +1,27 @@ + + +{#each superforms as superform (get(superform.formId))} +
+
+{/each} + + + +
\ No newline at end of file diff --git a/src/routes/(v2)/v2/multiple-forms/Form.svelte b/src/routes/(v2)/v2/multiple-forms/Form.svelte new file mode 100644 index 00000000..57d927ba --- /dev/null +++ b/src/routes/(v2)/v2/multiple-forms/Form.svelte @@ -0,0 +1,28 @@ + + +
+ + + + + + + + + {#if $message} +

{$message}

+ {/if} + \ No newline at end of file diff --git a/src/routes/(v2)/v2/multiple-forms/schema.ts b/src/routes/(v2)/v2/multiple-forms/schema.ts new file mode 100644 index 00000000..97ada301 --- /dev/null +++ b/src/routes/(v2)/v2/multiple-forms/schema.ts @@ -0,0 +1,6 @@ +import { z } from 'zod'; + +export const schema = z.object({ + id: z.number(), + label: z.string() +});