Skip to content

Commit 9b7becf

Browse files
committed
onUpdate didn't cancel applyAction and invalidateAll.
added formEl to onUpdate signature
1 parent f971bc0 commit 9b7becf

File tree

4 files changed

+191
-96
lines changed

4 files changed

+191
-96
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ 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]
9+
10+
### Fixed
11+
12+
- `onUpdate` didn't cancel `applyAction` and `invalidateAll`.
13+
14+
### Added
15+
16+
- `onUpdate` now has `formEl` in its signature.
17+
818
## [0.8.6] - 2023-05-06
919

1020
### Fixed

src/lib/client/index.ts

Lines changed: 111 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export type FormOptions<T extends ZodValidation<AnyZodObject>, M> = Partial<{
111111
}) => MaybePromise<unknown | void>;
112112
onUpdate: (event: {
113113
form: Validation<UnwrapEffects<T>, M>;
114+
formEl: HTMLFormElement;
114115
cancel: () => void;
115116
}) => MaybePromise<unknown | void>;
116117
onUpdated: (event: {
@@ -492,21 +493,6 @@ export function superForm<
492493
form: Validation<T2, M>,
493494
untaint: boolean
494495
) {
495-
let cancelled = false;
496-
const data = {
497-
form,
498-
cancel: () => (cancelled = true)
499-
};
500-
501-
for (const event of formEvents.onUpdate) {
502-
await event(data);
503-
}
504-
505-
if (cancelled) {
506-
if (options.flashMessage) cancelFlash(options);
507-
return;
508-
}
509-
510496
if (
511497
form.valid &&
512498
options.resetForm &&
@@ -792,49 +778,45 @@ export function superForm<
792778
});
793779

794780
// Need to subscribe to catch page invalidation.
795-
if (options.applyAction) {
796-
Unsubscriptions_add(
797-
page.subscribe(async (pageUpdate) => {
798-
function error(type: string) {
799-
throw new SuperFormError(
800-
`No form data found in ${type}. Make sure you return { form } in form actions and load functions.`
801-
);
802-
}
781+
Unsubscriptions_add(
782+
page.subscribe(async (pageUpdate) => {
783+
if (!options.applyAction) return;
803784

804-
const untaint =
805-
pageUpdate.status >= 200 && pageUpdate.status < 300;
785+
function error(type: string) {
786+
throw new SuperFormError(
787+
`No form data found in ${type}. Make sure you return { form } in form actions and load functions.`
788+
);
789+
}
806790

807-
if (pageUpdate.form && typeof pageUpdate.form === 'object') {
808-
const forms = Context_findValidationForms(pageUpdate.form);
809-
if (!forms.length) error('$page.form (ActionData)');
791+
const untaint = pageUpdate.status >= 200 && pageUpdate.status < 300;
810792

811-
for (const newForm of forms) {
812-
//console.log('🚀~ ActionData ~ newForm:', newForm.id);
813-
if (/*newForm === form ||*/ newForm.id !== _formId) continue;
793+
if (pageUpdate.form && typeof pageUpdate.form === 'object') {
794+
const forms = Context_findValidationForms(pageUpdate.form);
795+
if (!forms.length) error('$page.form (ActionData)');
814796

815-
await Form_updateFromValidation(
816-
newForm as Validation<T2, M>,
817-
untaint
818-
);
819-
}
820-
} else if (
821-
pageUpdate.data &&
822-
typeof pageUpdate.data === 'object'
823-
) {
824-
const forms = Context_findValidationForms(pageUpdate.data);
825-
826-
// It's a page reload, redirect or error/failure,
827-
// so don't trigger any events, just update the data.
828-
for (const newForm of forms) {
829-
//console.log('🚀 ~ PageData ~ newForm:', newForm.id);
830-
if (/*newForm === form ||*/ newForm.id !== _formId) continue;
831-
832-
rebind(newForm as Validation<T2, M>, untaint);
833-
}
797+
for (const newForm of forms) {
798+
//console.log('🚀~ ActionData ~ newForm:', newForm.id);
799+
if (/*newForm === form ||*/ newForm.id !== _formId) continue;
800+
801+
await Form_updateFromValidation(
802+
newForm as Validation<T2, M>,
803+
untaint
804+
);
834805
}
835-
})
836-
);
837-
}
806+
} else if (pageUpdate.data && typeof pageUpdate.data === 'object') {
807+
const forms = Context_findValidationForms(pageUpdate.data);
808+
809+
// It's a page reload, redirect or error/failure,
810+
// so don't trigger any events, just update the data.
811+
for (const newForm of forms) {
812+
//console.log('🚀 ~ PageData ~ newForm:', newForm.id);
813+
if (/*newForm === form ||*/ newForm.id !== _formId) continue;
814+
815+
rebind(newForm as Validation<T2, M>, untaint);
816+
}
817+
}
818+
})
819+
);
838820
}
839821

840822
const Fields = Object.fromEntries(
@@ -938,7 +920,8 @@ export function superForm<
938920
Meta,
939921
Constraints,
940922
Tainted,
941-
LastChanges
923+
LastChanges,
924+
Context_findValidationForms
942925
);
943926
},
944927

@@ -1256,7 +1239,10 @@ function formEnhance<T extends AnyZodObject, M>(
12561239
meta: Readable<Entity<T>['meta'] | undefined>,
12571240
constraints: Readable<Entity<T>['constraints']>,
12581241
tainted: Writable<TaintedFields<T> | undefined>,
1259-
lastChanges: Writable<string[][]>
1242+
lastChanges: Writable<string[][]>,
1243+
Context_findValidationForms: (
1244+
data: Record<string, unknown>
1245+
) => Validation<AnyZodObject>[]
12601246
) {
12611247
// Now we know that we are upgraded, so we can enable the tainted form option.
12621248
enableTaintedForm();
@@ -1728,60 +1714,89 @@ function formEnhance<T extends AnyZodObject, M>(
17281714
}
17291715

17301716
if (!cancelled) {
1731-
if (result.type !== 'error') {
1732-
if (result.type === 'success' && options.invalidateAll) {
1733-
await invalidateAll();
1717+
if (
1718+
(result.type === 'success' || result.type == 'failure') &&
1719+
result.data
1720+
) {
1721+
const forms = Context_findValidationForms(result.data);
1722+
if (!forms.length) {
1723+
throw new SuperFormError(
1724+
'No form data returned from ActionResult. Make sure you return { form } in the form actions.'
1725+
);
17341726
}
17351727

1736-
if (options.applyAction) {
1737-
// This will trigger the page subscription in superForm,
1738-
// which will in turn call Data_update.
1739-
await applyAction(result);
1740-
} else {
1741-
// Call Data_update directly to trigger events
1742-
await Data_update(result);
1728+
for (const newForm of forms) {
1729+
if (newForm.id !== get(id)) continue;
1730+
1731+
const data = {
1732+
form: newForm as Validation<T>,
1733+
formEl,
1734+
cancel: () => (cancelled = true)
1735+
};
1736+
1737+
for (const event of formEvents.onUpdate) {
1738+
await event(data);
1739+
}
17431740
}
1744-
} else {
1745-
// Error result
1746-
if (options.applyAction) {
1747-
if (options.onError == 'apply') {
1741+
}
1742+
1743+
if (!cancelled) {
1744+
if (result.type !== 'error') {
1745+
if (result.type === 'success' && options.invalidateAll) {
1746+
await invalidateAll();
1747+
}
1748+
1749+
if (options.applyAction) {
1750+
// This will trigger the page subscription in superForm,
1751+
// which will in turn call Data_update.
17481752
await applyAction(result);
17491753
} else {
1750-
// Transform to failure, to avoid data loss
1751-
await applyAction({
1752-
type: 'failure',
1753-
status: Math.floor(result.status || 500)
1754-
});
1754+
// Call Data_update directly to trigger events
1755+
await Data_update(result);
1756+
}
1757+
} else {
1758+
// Error result
1759+
if (options.applyAction) {
1760+
if (options.onError == 'apply') {
1761+
await applyAction(result);
1762+
} else {
1763+
// Transform to failure, to avoid data loss
1764+
await applyAction({
1765+
type: 'failure',
1766+
status: Math.floor(result.status || 500)
1767+
});
1768+
}
17551769
}
1756-
}
17571770

1758-
// Check if the error message should be replaced
1759-
if (options.onError !== 'apply') {
1760-
// TODO: Omit 'apply' and undefined from the type
1761-
// They are already filtered out, but type shouldn't be any.
1762-
const data = { result, message };
1771+
// Check if the error message should be replaced
1772+
if (options.onError !== 'apply') {
1773+
// TODO: Omit 'apply' and undefined from the type
1774+
// They are already filtered out, but type shouldn't be any.
1775+
const data = { result, message };
17631776

1764-
for (const event of formEvents.onError) {
1765-
if (event !== 'apply') await event(data);
1777+
for (const event of formEvents.onError) {
1778+
if (event !== 'apply') await event(data);
1779+
}
17661780
}
17671781
}
1768-
}
17691782

1770-
// Set flash message, which should be set in all cases, even
1771-
// if we have redirected (which is the point of the flash message!)
1772-
if (options.flashMessage) {
1773-
if (result.type == 'error' && options.flashMessage.onError) {
1774-
await options.flashMessage.onError({
1775-
result,
1776-
message: options.flashMessage.module.getFlash(page)
1777-
});
1778-
} else if (result.type != 'error') {
1779-
await options.flashMessage.module.updateFlash(page);
1783+
// Set flash message, which should be set in all cases, even
1784+
// if we have redirected (which is the point of the flash message!)
1785+
if (options.flashMessage) {
1786+
if (result.type == 'error' && options.flashMessage.onError) {
1787+
await options.flashMessage.onError({
1788+
result,
1789+
message: options.flashMessage.module.getFlash(page)
1790+
});
1791+
} else if (result.type != 'error') {
1792+
await options.flashMessage.module.updateFlash(page);
1793+
}
17801794
}
17811795
}
1782-
} else {
1783-
// Cancelled
1784-
if (options.flashMessage) cancelFlash(options);
1796+
}
1797+
1798+
if (cancelled && options.flashMessage) {
1799+
cancelFlash(options);
17851800
}
17861801

17871802
htmlForm.completed(cancelled);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<script lang="ts">
2+
import { superForm } from '$lib/client';
3+
import type { PageData } from './$types';
4+
import SuperDebug from '$lib/client/SuperDebug.svelte';
5+
import { _schema } from './+page';
6+
7+
export let data: PageData;
8+
let isLoading = false;
9+
10+
const { form, errors, enhance, message } = superForm(data.form, {
11+
SPA: true,
12+
validators: _schema,
13+
dataType: 'json',
14+
applyAction: false,
15+
async onUpdate({ form }) {
16+
if (!form.valid) {
17+
return;
18+
}
19+
20+
isLoading = true;
21+
await new Promise((resolve) => setTimeout(resolve, 1000));
22+
form.message = 'Posted ok!';
23+
isLoading = false;
24+
}
25+
});
26+
</script>
27+
28+
<h3>SPA onUpdate</h3>
29+
30+
<SuperDebug data={$form} />
31+
32+
{#if $message}
33+
<h4>{$message}</h4>
34+
{/if}
35+
36+
<form method="POST" use:enhance>
37+
<label>
38+
Name<br />
39+
<input
40+
name="name"
41+
data-invalid={$errors.name}
42+
bind:value={$form.name}
43+
disabled={isLoading}
44+
/>
45+
{#if $errors.name}<span class="invalid">{$errors.name}</span>{/if}
46+
</label>
47+
48+
<button>Submit</button>
49+
</form>
50+
51+
<style>
52+
.invalid {
53+
color: red;
54+
}
55+
56+
input {
57+
background-color: #ddd;
58+
}
59+
</style>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { superValidate } from '$lib/client';
2+
import { z } from 'zod';
3+
4+
export const _schema = z.object({
5+
name: z.string().min(1)
6+
});
7+
8+
export const load = async () => {
9+
const form = await superValidate(_schema);
10+
return { form };
11+
};

0 commit comments

Comments
 (0)