Skip to content

Commit 21e9281

Browse files
TommertomTommertom
Tommertom
authored and
Tommertom
committed
Demo app - forms update
1 parent 6f13fac commit 21e9281

File tree

4 files changed

+273
-114
lines changed

4 files changed

+273
-114
lines changed

demo-app/src/routes/components/Inputs/+page.svelte

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,40 @@
33
import { alertController, IonPage } from 'ionic-svelte';
44
55
import { accountSchema as schema } from './account.interface';
6-
import { enhance, getFormWritable } from './spa-enhance';
6+
import { enhance, getFormWritable, validateField } from './spa-enhance';
77
88
const form = getFormWritable();
99
10-
$: console.log('Form received', $form);
10+
// $: console.log('Form received', $form);
1111
12-
$: if ($form?.success) {
13-
const controller = alertController
14-
.create({
15-
header: 'Account Created',
16-
message: `Created account for: <b>${$form.data.firstName} ${$form.data.lastName}</b>`,
17-
buttons: [
18-
{
19-
text: 'OK'
20-
}
21-
]
22-
})
23-
.then((alert) => alert.present());
24-
}
12+
function submit() {
13+
if ($form?.success) {
14+
const controller = alertController
15+
.create({
16+
header: 'Account Created',
17+
message: `Created account for: <b>${$form.data.firstName} ${$form.data.lastName}</b>`,
18+
buttons: [
19+
{
20+
text: 'OK'
21+
}
22+
]
23+
})
24+
.then((alert) => alert.present());
25+
}
2526
26-
$: if ($form !== null && !$form?.success) {
27-
const controller = alertController
28-
.create({
29-
header: 'Account Not Created',
30-
message: `There were some errors - see console.log`,
31-
buttons: [
32-
{
33-
text: 'OK'
34-
}
35-
]
36-
})
37-
.then((alert) => alert.present());
27+
if ($form !== null && !$form?.success) {
28+
const controller = alertController
29+
.create({
30+
header: 'Account Not Created',
31+
message: `There were some errors - see console.log`,
32+
buttons: [
33+
{
34+
text: 'OK'
35+
}
36+
]
37+
})
38+
.then((alert) => alert.present());
39+
}
3840
}
3941
</script>
4042

@@ -56,14 +58,18 @@
5658
</ion-header>
5759

5860
<ion-content fullscreen class="ion-padding">
59-
<form use:enhance={{ form, schema }} id="accountform">
61+
<form use:enhance={{ form, schema }} id="accountform" on:submit={submit}>
6062
<ion-list lines="full" class="ion-no-margin ion-no-padding">
6163
<ion-item class:ion-invalid={$form?.errors?.firstName}>
6264
<ion-label position="stacked">
6365
First Name
6466
<ion-text color="danger">*</ion-text>
6567
</ion-label>
66-
<ion-input name="firstName" type="text" value={$form?.data.firstName ?? ''} />
68+
<ion-input
69+
name="firstName"
70+
type="text"
71+
value={$form?.data.firstName ?? ''}
72+
on:ionChange={validateField} />
6773
<ion-note slot="error">First name must be not empty and valid</ion-note>
6874
</ion-item>
6975

@@ -72,8 +78,8 @@
7278
Last Name
7379
<ion-text color="danger">*</ion-text>
7480
</ion-label>
75-
<ion-input name="lastName" required type="text" />
76-
<ion-note slot="error">FLast name must be not empty and valid</ion-note>
81+
<ion-input name="lastName" required type="text" on:ionChange={validateField} />
82+
<ion-note slot="error">Last name must be not empty and valid</ion-note>
7783
</ion-item>
7884

7985
<ion-item>

demo-app/src/routes/components/Inputs/spa-enhance.ts

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,114 @@ export function getFormWritable() {
2020
return writable<FormType | null>(null);
2121
}
2222

23-
// export let validate: () => void;
23+
let current_form_node: HTMLFormElement;
24+
let current_schema: ZodTypeAny | undefined;
25+
let current_writable_form: Writable<FormType | null>;
26+
let current_validate_function: any;
27+
28+
function formToObject(node_id: number | string) {
29+
//@ts-ignore
30+
const form = document.forms[node_id];
31+
const formData = new FormData(form);
32+
const keys = Array.from(formData.keys());
33+
34+
const output: { [key: string]: string } = {};
35+
keys.forEach((key) => {
36+
output[key] = formData.get(key) as string;
37+
});
38+
39+
return output;
40+
}
2441

25-
// export function validateFromEvent(ev) {
26-
// console.log('sss', ev);
27-
// return true;
28-
// }
42+
//@ts-ignore
43+
export function validateField(ev) {
2944

30-
export function enhance(
31-
formNode: HTMLFormElement,
32-
bar: { form: Writable<FormType | null>; schema?: ZodTypeAny }
33-
) {
34-
const { form, schema } = bar;
45+
const name = ev.target.name;
46+
const value = ev.detail.value;
3547

36-
function validateForm() {
48+
if (name && value && current_form_node && current_schema) {
49+
50+
const data = formToObject(current_form_node.id);
51+
52+
const result = current_schema.safeParse(data);
53+
54+
if (!result.success) {
55+
const errors = result.error.errors
56+
.filter(error => error.path[0] === name)
57+
.map((error) => error.message);
58+
59+
// we update errors for this field when there was an error
60+
if (errors.length > 0) {
61+
62+
current_writable_form.update((formStatus: FormType | null) => {
63+
const newStatus = formStatus ?? {
64+
success: false,
65+
errors: undefined,
66+
data: {},
67+
validate: undefined
68+
};
69+
70+
if (!newStatus.errors) newStatus.errors = {};
71+
//@ts-ignore
72+
newStatus.errors[name] = errors;
73+
//@ts-ignore
74+
newStatus.data[name] = value;
75+
newStatus.success = false;
76+
newStatus.validate = current_validate_function
77+
78+
return newStatus
79+
})
80+
}
3781

38-
function formToObject() {
39-
//@ts-ignore
40-
const form = document.forms[formNode.id];
41-
const formData = new FormData(form);
42-
const keys = Array.from(formData.keys());
82+
// we update to ok when there was no error for this field
83+
if (errors.length === 0) {
84+
current_writable_form.update((formStatus: FormType | null) => {
85+
const newStatus = formStatus ?? {
86+
success: false,
87+
errors: undefined,
88+
data: {},
89+
validate: undefined
90+
};
91+
92+
// let's remove any earlier errors related to this field
93+
if (!newStatus.errors) newStatus.errors = {};
94+
95+
delete newStatus.errors[name]
96+
97+
// we copy formstatus success state
98+
if (formStatus?.success) newStatus.success = formStatus.success;
99+
// but if there is no error, we put it to true
100+
if (Object.keys(newStatus.errors).length === 0)
101+
newStatus.success = true;
43102

44-
const output: { [key: string]: string } = {};
45-
keys.forEach((key) => {
46-
output[key] = formData.get(key) as string;
47-
});
103+
newStatus.data[name] = value;
104+
newStatus.validate = current_validate_function
48105

49-
return output;
106+
return newStatus
107+
})
108+
}
50109
}
51110

111+
// if there was nothing wrong, we make sure the form success state reset to initial success
112+
if (result.success) current_validate_function()
113+
}
114+
}
115+
116+
export function enhance(
117+
formNode: HTMLFormElement,
118+
params: { form: Writable<FormType | null>; schema?: ZodTypeAny }
119+
) {
120+
const { form, schema } = params;
121+
122+
current_form_node = formNode;
123+
current_schema = schema;
124+
current_writable_form = form;
125+
current_validate_function = validateForm;
126+
127+
function validateForm() {
52128
let success = false;
53129
let errors: undefined | ErrorObject = undefined;
54-
let data = formToObject();
130+
let data = formToObject(formNode.id);
55131

56132
if (schema) {
57133
const result = schema.safeParse(data);
@@ -66,6 +142,7 @@ export function enhance(
66142
};
67143
});
68144

145+
// transform in object with list of arrays
69146
errors = errorsArray.reduce((acc, obj) => {
70147
//@ts-ignore
71148
if (!acc[obj.field]) {
@@ -95,8 +172,6 @@ export function enhance(
95172
};
96173

97174
formNode.addEventListener('submit', submitHandler);
98-
99-
100175
return {
101176
destroy() {
102177
formNode.removeEventListener('submit', submitHandler);

demo-app/static/assets/src/components/Inputs/+page.svelte

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,43 @@
11
<script lang="ts">
22
import SourceButton from '$lib/components/SourceButton.svelte';
33
import { alertController, IonPage } from 'ionic-svelte';
4-
import { onMount } from 'svelte';
54
65
import { accountSchema as schema } from './account.interface';
7-
import { enhance, getFormWritable, validate } from './spa-enhance';
6+
import { enhance, getFormWritable, validateField } from './spa-enhance';
87
98
const form = getFormWritable();
109
11-
$: console.log('Form received', $form);
10+
// $: console.log('Form received', $form);
1211
13-
$: if ($form?.success) {
14-
const controller = alertController
15-
.create({
16-
header: 'Account Created',
17-
message: `Created account for: <b>${$form.data.firstName} ${$form.data.lastName}</b>`,
18-
buttons: [
19-
{
20-
text: 'OK'
21-
}
22-
]
23-
})
24-
.then((alert) => alert.present());
25-
}
12+
function submit() {
13+
if ($form?.success) {
14+
const controller = alertController
15+
.create({
16+
header: 'Account Created',
17+
message: `Created account for: <b>${$form.data.firstName} ${$form.data.lastName}</b>`,
18+
buttons: [
19+
{
20+
text: 'OK'
21+
}
22+
]
23+
})
24+
.then((alert) => alert.present());
25+
}
2626
27-
$: if ($form !== null && !$form?.success) {
28-
const controller = alertController
29-
.create({
30-
header: 'Account Not Created',
31-
message: `There were some errors - see console.log`,
32-
buttons: [
33-
{
34-
text: 'OK'
35-
}
36-
]
37-
})
38-
.then((alert) => alert.present());
27+
if ($form !== null && !$form?.success) {
28+
const controller = alertController
29+
.create({
30+
header: 'Account Not Created',
31+
message: `There were some errors - see console.log`,
32+
buttons: [
33+
{
34+
text: 'OK'
35+
}
36+
]
37+
})
38+
.then((alert) => alert.present());
39+
}
3940
}
40-
41-
onMount(() => {
42-
console.log('Validate', validate);
43-
});
4441
</script>
4542

4643
<svelte:head>
@@ -68,7 +65,11 @@
6865
First Name
6966
<ion-text color="danger">*</ion-text>
7067
</ion-label>
71-
<ion-input name="firstName" type="text" value={$form?.data.firstName ?? ''} />
68+
<ion-input
69+
name="firstName"
70+
type="text"
71+
value={$form?.data.firstName ?? ''}
72+
on:ionChange={validateField} />
7273
<ion-note slot="error">First name must be not empty and valid</ion-note>
7374
</ion-item>
7475

@@ -77,8 +78,8 @@
7778
Last Name
7879
<ion-text color="danger">*</ion-text>
7980
</ion-label>
80-
<ion-input name="lastName" required type="text" />
81-
<ion-note slot="error">FLast name must be not empty and valid</ion-note>
81+
<ion-input name="lastName" required type="text" on:ionChange={validateField} />
82+
<ion-note slot="error">Last name must be not empty and valid</ion-note>
8283
</ion-item>
8384

8485
<ion-item>

0 commit comments

Comments
 (0)