Skip to content

Commit 2a80e2e

Browse files
author
Tim Marwick
committed
Add form fields, and form page
- Use svelte5 with typescript and sveltekit to make an app called "sveltekit-5-demo-app" - create a set of components for the most commonly used HTML form inputs - create a demo page that has an example form on it, with the overall form-state stored in a svelte state object, which is also saved to local storage on every change event
1 parent a21c92e commit 2a80e2e

File tree

7 files changed

+246
-0
lines changed

7 files changed

+246
-0
lines changed

src/components/Checkbox.svelte

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts">
2+
import { createEventDispatcher } from 'svelte';
3+
export let label: string = '';
4+
export let checked: boolean = false;
5+
export let name: string = '';
6+
const dispatch = createEventDispatcher();
7+
8+
function handleChange(event: Event) {
9+
dispatch('change', { name, checked: (event.target as HTMLInputElement).checked });
10+
}
11+
</script>
12+
13+
<label>
14+
<input type="checkbox" bind:checked on:change={handleChange} />
15+
{label}
16+
</label>

src/components/RadioButton.svelte

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script lang="ts">
2+
import { createEventDispatcher } from 'svelte';
3+
export let label: string = '';
4+
export let value: string = '';
5+
export let name: string = '';
6+
export let groupValue: string = '';
7+
const dispatch = createEventDispatcher();
8+
9+
function handleChange() {
10+
dispatch('change', { name, value });
11+
}
12+
</script>
13+
14+
<label>
15+
<input
16+
type="radio"
17+
name={name}
18+
value={value}
19+
bind:group={groupValue}
20+
on:change={handleChange}
21+
/>
22+
{label}
23+
</label>

src/components/Select.svelte

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts">
2+
import { createEventDispatcher } from 'svelte';
3+
export let label: string = '';
4+
export let options: { value: string; text: string }[] = [];
5+
export let selectedValue: string = '';
6+
export let name: string = '';
7+
const dispatch = createEventDispatcher();
8+
9+
function handleChange(event: Event) {
10+
dispatch('change', { name, value: (event.target as HTMLSelectElement).value });
11+
}
12+
</script>
13+
14+
<label>
15+
{label}
16+
<select bind:value={selectedValue} on:change={handleChange}>
17+
{#each options as option}
18+
<option value={option.value}>{option.text}</option>
19+
{/each}
20+
</select>
21+
</label>

src/components/TextArea.svelte

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts">
2+
import { createEventDispatcher } from 'svelte';
3+
export let label: string = '';
4+
export let value: string = '';
5+
export let name: string = '';
6+
const dispatch = createEventDispatcher();
7+
8+
function handleInput(event: Event) {
9+
dispatch('change', { name, value: (event.target as HTMLTextAreaElement).value });
10+
}
11+
</script>
12+
13+
<label>
14+
{label}
15+
<textarea bind:value on:input={handleInput}></textarea>
16+
</label>

src/components/TextInput.svelte

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts">
2+
import { createEventDispatcher } from 'svelte';
3+
export let label: string = '';
4+
export let value: string = '';
5+
export let name: string = '';
6+
const dispatch = createEventDispatcher();
7+
8+
function handleInput(event: Event) {
9+
dispatch('change', { name, value: (event.target as HTMLInputElement).value });
10+
}
11+
</script>
12+
13+
<label>
14+
{label}
15+
<input type="text" bind:value on:input={handleInput} />
16+
</label>

src/routes/Header.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
<li aria-current={$page.url.pathname.startsWith('/sverdle') ? 'page' : undefined}>
2626
<a href="/sverdle">Sverdle</a>
2727
</li>
28+
<li aria-current={$page.url.pathname === '/form' ? 'page' : undefined}>
29+
<a href="/form">Form</a>
30+
</li>
2831
</ul>
2932
<svg viewBox="0 0 2 3" aria-hidden="true">
3033
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />

src/routes/form/+page.svelte

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte';
3+
import { writable } from 'svelte/store';
4+
import TextInput from '../../components/TextInput.svelte';
5+
import Checkbox from '../../components/Checkbox.svelte';
6+
import RadioButton from '../../components/RadioButton.svelte';
7+
import Select from '../../components/Select.svelte';
8+
import TextArea from '../../components/TextArea.svelte';
9+
// import TextInput from '$lib/components/TextInput.svelte';
10+
// import Checkbox from '$lib/components/Checkbox.svelte';
11+
// import RadioButton from '$lib/components/RadioButton.svelte';
12+
// import Select from '$lib/components/Select.svelte';
13+
// import TextArea from '$lib/components/TextArea.svelte';
14+
15+
interface FormData {
16+
name: string;
17+
email: string;
18+
password: string;
19+
acceptTerms: boolean;
20+
gender: string;
21+
country: string;
22+
bio: string;
23+
}
24+
25+
const defaultFormData: FormData = {
26+
name: '',
27+
email: '',
28+
password: '',
29+
acceptTerms: false,
30+
gender: '',
31+
country: '',
32+
bio: ''
33+
};
34+
35+
const formData = writable<FormData>({ ...defaultFormData });
36+
37+
// Load form data from local storage on mount
38+
onMount(() => {
39+
const storedData = localStorage.getItem('formData');
40+
if (storedData) {
41+
formData.set(JSON.parse(storedData));
42+
}
43+
});
44+
45+
// Save form data to local storage on change
46+
formData.subscribe((data) => {
47+
localStorage.setItem('formData', JSON.stringify(data));
48+
});
49+
50+
function handleChange(event: any) {
51+
const { name, value, checked } = event.detail;
52+
formData.update((data) => ({
53+
...data,
54+
[name]: value !== undefined ? value : checked
55+
}));
56+
}
57+
</script>
58+
59+
<h1>Example Form</h1>
60+
61+
<form on:submit|preventDefault>
62+
{#if $formData}
63+
<!-- Name -->
64+
<TextInput
65+
label="Name"
66+
name="name"
67+
value={$formData.name}
68+
on:change={handleChange}
69+
/>
70+
71+
<!-- Email -->
72+
<TextInput
73+
label="Email"
74+
name="email"
75+
value={$formData.email}
76+
on:change={handleChange}
77+
/>
78+
79+
<!-- Password -->
80+
<TextInput
81+
label="Password"
82+
name="password"
83+
value={$formData.password}
84+
on:change={handleChange}
85+
/>
86+
87+
<!-- Accept Terms -->
88+
<Checkbox
89+
label="Accept Terms and Conditions"
90+
name="acceptTerms"
91+
checked={$formData.acceptTerms}
92+
on:change={handleChange}
93+
/>
94+
95+
<!-- Gender -->
96+
<fieldset>
97+
<legend>Gender</legend>
98+
<RadioButton
99+
label="Male"
100+
name="gender"
101+
value="male"
102+
groupValue={$formData.gender}
103+
on:change={handleChange}
104+
/>
105+
<RadioButton
106+
label="Female"
107+
name="gender"
108+
value="female"
109+
groupValue={$formData.gender}
110+
on:change={handleChange}
111+
/>
112+
<RadioButton
113+
label="Other"
114+
name="gender"
115+
value="other"
116+
groupValue={$formData.gender}
117+
on:change={handleChange}
118+
/>
119+
</fieldset>
120+
121+
<!-- Country -->
122+
<Select
123+
label="Country"
124+
name="country"
125+
options={[
126+
{ value: '', text: 'Select Country' },
127+
{ value: 'us', text: 'United States' },
128+
{ value: 'ca', text: 'Canada' },
129+
{ value: 'uk', text: 'United Kingdom' },
130+
{ value: 'au', text: 'Australia' }
131+
]}
132+
selectedValue={$formData.country}
133+
on:change={handleChange}
134+
/>
135+
136+
<!-- Bio -->
137+
<TextArea
138+
label="Bio"
139+
name="bio"
140+
value={$formData.bio}
141+
on:change={handleChange}
142+
/>
143+
144+
<!-- Submit Button -->
145+
<button type="submit">Submit</button>
146+
{/if}
147+
</form>
148+
149+
<!-- Display Form Data -->
150+
<pre>{JSON.stringify($formData, null, 2)}</pre>
151+

0 commit comments

Comments
 (0)