Skip to content

Commit b535dae

Browse files
committed
SPA action form improved.
1 parent 3d72607 commit b535dae

File tree

5 files changed

+117
-35
lines changed

5 files changed

+117
-35
lines changed

src/lib/client/superForm.ts

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,13 @@ export function superForm<
521521
onDestroy(() => {
522522
Unsubscriptions_unsubscribe();
523523
NextChange_clear();
524+
EnhancedForm_destroy();
524525

525526
for (const events of Object.values(formEvents)) {
526527
events.length = 0;
527528
}
528529

529530
formIds.get(_currentPage)?.delete(_initialFormId);
530-
ActionForm_remove();
531531
});
532532

533533
// Check for nested objects, throw if datatype isn't json
@@ -770,7 +770,7 @@ export function superForm<
770770

771771
if (skipValidation || !event || !options.validators || options.validators == 'clear') {
772772
if (event?.paths) {
773-
const formElement = event?.formElement ?? EnhancedForm;
773+
const formElement = event?.formElement ?? EnhancedForm_get();
774774
if (formElement) Form__clearCustomValidity(formElement, event.paths);
775775
}
776776
return;
@@ -820,7 +820,7 @@ export function superForm<
820820
const output: Record<string, unknown> = {};
821821
let validity = new Map<string, { el: HTMLElement; message: string }>();
822822

823-
const formElement = event.formElement ?? EnhancedForm;
823+
const formElement = event.formElement ?? EnhancedForm_get();
824824
if (formElement) validity = Form__clearCustomValidity(formElement, event.paths);
825825

826826
traversePaths(errors, (error) => {
@@ -1246,28 +1246,34 @@ export function superForm<
12461246

12471247
//#endregion
12481248

1249-
//#region ActionForm
1249+
//#region EnhancedForm
12501250

1251-
// SPA action mode
1252-
let ActionForm: HTMLFormElement | undefined = undefined;
1251+
/**
1252+
* Used for SPA action mode and options.customValidity to display errors, even if programmatically set
1253+
*/
1254+
let EnhancedForm: HTMLFormElement | undefined;
12531255

1254-
function ActionForm_create(action: string) {
1255-
ActionForm = document.createElement('form');
1256-
ActionForm.method = 'POST';
1257-
ActionForm.action = action;
1258-
superFormEnhance(ActionForm);
1259-
document.body.appendChild(ActionForm);
1256+
function EnhancedForm_get() {
1257+
return EnhancedForm;
12601258
}
12611259

1262-
function ActionForm_setAction(action: string) {
1263-
if (ActionForm) ActionForm.action = action;
1260+
function EnhancedForm_createFromSPA(action: string) {
1261+
EnhancedForm = document.createElement('form');
1262+
EnhancedForm.method = 'POST';
1263+
EnhancedForm.action = action;
1264+
superFormEnhance(EnhancedForm);
1265+
document.body.appendChild(EnhancedForm);
12641266
}
12651267

1266-
function ActionForm_remove() {
1267-
if (ActionForm?.parentElement) {
1268-
ActionForm.remove();
1269-
ActionForm = undefined;
1268+
function EnhancedForm_setAction(action: string) {
1269+
if (EnhancedForm) EnhancedForm.action = action;
1270+
}
1271+
1272+
function EnhancedForm_destroy() {
1273+
if (EnhancedForm?.parentElement) {
1274+
EnhancedForm.remove();
12701275
}
1276+
EnhancedForm = undefined;
12711277
}
12721278

12731279
//#endregion
@@ -1277,9 +1283,6 @@ export function superForm<
12771283
($errors: ValidationErrors<T> | undefined) => ($errors ? flattenErrors($errors) : [])
12781284
);
12791285

1280-
// Used for options.customValidity to display errors, even if programmatically set
1281-
let EnhancedForm: HTMLFormElement | undefined;
1282-
12831286
///// End of Roles //////////////////////////////////////////////////////////
12841287

12851288
// Need to clear this and set it again when use:enhance has run, to avoid showing the
@@ -1438,7 +1441,7 @@ export function superForm<
14381441
);
14391442

14401443
if (typeof options.SPA === 'string') {
1441-
ActionForm_create(options.SPA);
1444+
EnhancedForm_createFromSPA(options.SPA);
14421445
}
14431446
}
14441447

@@ -1449,8 +1452,15 @@ export function superForm<
14491452
* @DCI-context
14501453
*/
14511454
function superFormEnhance(FormElement: HTMLFormElement, events?: SuperFormEvents<T, M>) {
1452-
ActionForm_remove();
1453-
EnhancedForm = FormElement;
1455+
if (FormElement.method == 'get') FormElement.method = 'post';
1456+
1457+
if (typeof options.SPA === 'string') {
1458+
if (options.SPA.length && FormElement.action == document.location.href) {
1459+
FormElement.action = options.SPA;
1460+
}
1461+
} else {
1462+
EnhancedForm = FormElement;
1463+
}
14541464

14551465
if (events) {
14561466
if (events.onError) {
@@ -1523,7 +1533,6 @@ export function superForm<
15231533
onDestroy(() => {
15241534
FormElement.removeEventListener('focusout', onBlur);
15251535
FormElement.removeEventListener('input', onInput);
1526-
EnhancedForm = undefined;
15271536
});
15281537

15291538
///// SvelteKit enhance function //////////////////////////////////
@@ -1716,7 +1725,7 @@ export function superForm<
17161725
}
17171726

17181727
if (typeof options.SPA === 'string') {
1719-
ActionForm_setAction(options.SPA);
1728+
EnhancedForm_setAction(options.SPA);
17201729
}
17211730
}
17221731
}
@@ -2045,14 +2054,15 @@ export function superForm<
20452054
)
20462055
: Form_validate({ adapter: opts.schema });
20472056

2048-
if (opts.update && EnhancedForm) {
2057+
const enhancedForm = EnhancedForm_get();
2058+
if (opts.update && enhancedForm) {
20492059
// Focus on first error field
20502060
setTimeout(() => {
2051-
if (EnhancedForm)
2052-
scrollToFirstError(EnhancedForm, {
2053-
...options,
2054-
scrollToError: opts.focusOnError === false ? 'off' : options.scrollToError
2055-
});
2061+
if (!enhancedForm) return;
2062+
scrollToFirstError(enhancedForm, {
2063+
...options,
2064+
scrollToError: opts.focusOnError === false ? 'off' : options.scrollToError
2065+
});
20562066
}, 1);
20572067
}
20582068

@@ -2072,8 +2082,8 @@ export function superForm<
20722082
},
20732083

20742084
submit(submitter?: HTMLElement | Event | EventTarget | null | undefined) {
2075-
const form = EnhancedForm
2076-
? EnhancedForm
2085+
const form = EnhancedForm_get()
2086+
? EnhancedForm_get()
20772087
: submitter && submitter instanceof HTMLElement
20782088
? submitter.closest<HTMLFormElement>('form')
20792089
: undefined;

src/routes/(v2)/v2/multistep-client/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
<h3>Step {step}</h3>
4545

46-
<form method="POST" use:enhance>
46+
<form use:enhance>
4747
{#if step == 1}
4848
<label>
4949
Name<br />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const load = async () => {
2+
const entries = [
3+
{ id: 1, name: 'Number one' },
4+
{ id: 2, name: 'Number two' },
5+
{ id: 3, name: 'Number three' }
6+
];
7+
return { entries };
8+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script lang="ts">
2+
import { superForm } from '$lib/client/index.js';
3+
import SuperDebug from '$lib/client/SuperDebug.svelte';
4+
5+
export let data;
6+
7+
const { enhance } = superForm(
8+
{},
9+
{
10+
SPA: '/v2/spa-action-2/classify',
11+
taintedMessage: false,
12+
onUpdate({ form }) {
13+
const entry = data.entries.find((e) => e.id == form.message.id);
14+
if (entry) entry.name = 'Modified';
15+
}
16+
}
17+
);
18+
</script>
19+
20+
<SuperDebug data={data.entries} />
21+
22+
<table>
23+
<thead>
24+
<tr>
25+
<th>Id</th>
26+
<th>Name</th>
27+
<th>Action</th>
28+
</tr>
29+
</thead>
30+
<tbody>
31+
{#each data.entries as entry}
32+
<tr>
33+
<td>{entry.id}</td>
34+
<td>{entry.name}</td>
35+
<td>
36+
<form use:enhance>
37+
<input type="hidden" name="id" value={entry.id} />
38+
<input type="hidden" name="name" value={entry.name} />
39+
<button>Change name</button>
40+
</form>
41+
</td>
42+
</tr>
43+
{/each}
44+
</tbody>
45+
</table>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { valibot } from '$lib/adapters/valibot.js';
2+
import { fail, message, superValidate } from '$lib/index.js';
3+
import * as v from 'valibot';
4+
5+
export const _classifySchema = v.object({
6+
id: v.number([v.minValue(1)]),
7+
name: v.string()
8+
});
9+
10+
export const actions = {
11+
default: async ({ request }) => {
12+
const form = await superValidate(request, valibot(_classifySchema));
13+
console.log(form);
14+
15+
if (!form.valid) return fail(400, { form });
16+
17+
return message(form, { id: form.data.id });
18+
}
19+
};

0 commit comments

Comments
 (0)