Skip to content

Commit 829a279

Browse files
committed
Added README.md
1 parent 17994bb commit 829a279

File tree

2 files changed

+303
-1
lines changed

2 files changed

+303
-1
lines changed

README.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# Welcome to @cloudedots/svelte-forms 👋
2+
3+
[![NPM Version](https://img.shields.io/npm/v/@cloudedots/svelte-forms.svg?orange=blue)](https://npmjs.org/package/@cloudedots/svelte-forms)
4+
5+
> Svelte Forms Library
6+
7+
### 🏠 [Homepage](https://github.com/cloudedots-projects/svelte-forms#readme)
8+
9+
## Install
10+
11+
```sh
12+
npm install @cloudedots/svelte-forms yup
13+
```
14+
15+
> This package uses **[Yup](https://github.com/jquense/yup)** for validation.
16+
17+
## Basic Usage Example
18+
19+
_App.svelte_ :
20+
21+
```html
22+
<script>
23+
import { createForm } from "@cloudedots/svelte-forms";
24+
import * as yup from "yup";
25+
26+
// Create Form Instance
27+
const { form, formControl, isValid } = createForm({
28+
// Initial Form Data
29+
initialValues: {
30+
email: ''
31+
password: '',
32+
},
33+
// Form Validation using Yup
34+
validationSchema: yup.object().shape({
35+
email: yup.string().email().required(),
36+
password: yup.string().min(6).required()
37+
})
38+
});
39+
40+
const onSubmit = () => {
41+
// "$form" contains current Form Data
42+
console.log($form);
43+
}
44+
</script>
45+
46+
<form on:submit|preventDefault="{onSubmit}">
47+
<input
48+
type="text"
49+
name="email"
50+
bind:value="{$form.email}"
51+
use:formControl
52+
/>
53+
<input
54+
type="password"
55+
name="password"
56+
bind:value="{$form.password}"
57+
use:formControl
58+
/>
59+
60+
<button type="submit" disabled="{!$isValid}">Submit</button>
61+
</form>
62+
```
63+
64+
## Full Usage Example
65+
66+
_App.svelte_ :
67+
68+
```html
69+
<script lang="ts">
70+
import { createForm } from "@cloudedots/svelte-forms";
71+
import * as yup from "yup";
72+
import UserForm from "./UserForm.svelte"; // Components
73+
74+
// (Optional) Form's Data type will be automatically inferred from "initialValues" in "createForm" if type of Data is not specified
75+
type FormData = {
76+
title: string;
77+
description: string;
78+
users: {
79+
name: string;
80+
email: string;
81+
address: {
82+
state: string;
83+
city: string;
84+
};
85+
}[];
86+
};
87+
88+
// Create Form Instance
89+
const {
90+
form, // Svelte Store<FormData> containing Form Data
91+
state, // Svelte Store<FormState> containing Form State - { [every_property]: { _touched: boolean, _errors: string[] }}
92+
isValid, // Svelte Store<boolean> containing entire Form's validation status
93+
isTouched, // Svelte Store<boolean> containing entire Form's touched status
94+
validateForm, // Function(highlight: 'none' | 'errors' | 'all' = 'none') for manually validting entire form
95+
handleChange, // Function(event: Event) to manually updating individual form control's state - can be used in place of "formControl" Action
96+
setTouched, // Function() for manually setting Form state as "touched"
97+
updateForm, // Function() for updating Form's Structure after Form Controls are Added or Removed in cases like Form Arrays
98+
formControl, // Svelte Action to be used with <input>, <select>, <textarea> or similar HTML input elements
99+
} = createForm({
100+
// Initial Values of Form
101+
initialValues: {
102+
title: "", // Simple String
103+
description: "", // Simple String
104+
users: [], // Complex Form Array
105+
},
106+
// Yup Validation Schema
107+
validationSchema: yup.object().shape({
108+
title: yup.string().min(8).required(),
109+
description: yup.string(),
110+
users: yup.array().of({
111+
name: yup.string().required(),
112+
email: yup.string().email().required(),
113+
address: yup.object().shape({
114+
state: yup.string().required(),
115+
city: yup.string(),
116+
}),
117+
}),
118+
}),
119+
validationClasses: {
120+
valid: "is-valid", // CSS class added to valid form controls
121+
invalid: "is-invalid", // CSS class added to invalid form controls
122+
showValid: true, // Add CSS classes to valid form controls
123+
showInvalid: true, // Add CSS classes to invalid form controls
124+
},
125+
});
126+
127+
// Add new user to Users Form Array
128+
const addUser = () => {
129+
// Update Form Data
130+
$form.users = [
131+
...$form.users,
132+
{
133+
name: "",
134+
email: "",
135+
address: {
136+
state: "",
137+
city: "",
138+
},
139+
},
140+
];
141+
updateForm(); // Manually trigger Form Update - Required
142+
};
143+
144+
// Remove user from Users Form Array
145+
const removeUser = (index) => () => {
146+
$form.users = $form.users.filter((_, i) => i !== index); // Update Form Data
147+
$state.users = $state.users.filter((_, i) => i !== index); // Updating State is required after removing Form Controls
148+
updateForm(); // Manually trigger Form Update - Required
149+
};
150+
151+
// Submit Form
152+
const onSubmit = () => {
153+
console.log($form); // Get Form Data
154+
};
155+
156+
$: console.log({ $form, $state }); // Log Form Data and Form State on every Change
157+
</script>
158+
159+
<form on:submit|preventDefault="{onSubmit}">
160+
161+
<input
162+
placeholder="Title"
163+
name="title"
164+
bind:value="{$form.title}"
165+
use:formControl
166+
/>
167+
{#if $state.title._errors?.length}
168+
{#each $state.title._errors as error}
169+
<span class="error">{error}</span>
170+
{/each}
171+
{/if}
172+
173+
<input
174+
placeholder="Description"
175+
name="description"
176+
bind:value="{$form.description}"
177+
use:formControl
178+
/>
179+
{#if $state.description._errors?.length}
180+
{#each $state.description._errors as error}
181+
<span class="error">{error}</span>
182+
{/each}
183+
{/if}
184+
185+
{#each $form.users as user, index}
186+
<h2>
187+
User {user.name}
188+
<button type="button" on:click="{removeUser(i)}">
189+
Remove User
190+
</button>
191+
</h2>
192+
193+
<input
194+
placeholder="name"
195+
name="users[{i}].name"
196+
bind:value="{user.name}"
197+
use:formControl
198+
/>
199+
{#if $state.users[index].name._errors?.length}
200+
{#each $state.users[index].name._errors as error}
201+
<span class="error">{error}</span>
202+
{/each}
203+
{/if}
204+
205+
<input
206+
placeholder="email"
207+
name="users[{i}].email"
208+
bind:value="{user.email}"
209+
use:formControl
210+
/>
211+
{#if $state.users[index].email._errors?.length}
212+
{#each $state.users[index].email._errors as error}
213+
<span class="error">{error}</span>
214+
{/each}
215+
{/if}
216+
217+
<!-- Using with Components -->
218+
<UserAddressForm {form} {state} {formControl} {index} />
219+
220+
{/each}
221+
222+
<button type="button" on:click={addUser}>
223+
Add User
224+
</button>
225+
226+
<button type="button" on:click={() => validateForm('errors')}>
227+
Validate Form
228+
</button>
229+
230+
<button type="submit" disabled={!$isValid}>
231+
Submit
232+
</button>
233+
234+
</form>
235+
236+
<style>
237+
.valid {
238+
border: 1px solid green;
239+
}
240+
241+
.invalid {
242+
border: 1px solid red;
243+
}
244+
245+
.error {
246+
color: red;
247+
}
248+
</style>
249+
```
250+
251+
_UserAddressForm.svelte_ :
252+
253+
```html
254+
<script lang="ts">
255+
export let form: any;
256+
export let state: any;
257+
export let formControl;
258+
export let index: number;
259+
</script>
260+
261+
<input
262+
type="text"
263+
placeholder="State"
264+
bind:value="{$form.users[index].address.state}"
265+
name="users[{index}].address.state"
266+
use:formControl
267+
/>
268+
{#if $state.users[index].address.state._errors?.length}
269+
{#each $state.users[index].address.state._errors as error}
270+
<span class="error">{error}</span>
271+
{/each}
272+
{/if}
273+
274+
<input
275+
type="text"
276+
placeholder="City"
277+
bind:value="{$form.users[index].address.city}"
278+
name="users[{index}].address.city"
279+
use:formControl
280+
/>
281+
{#if $state.users[index].address.city._errors?.length}
282+
{#each $state.users[index].address.city._errors as error}
283+
<span class="error">{error}</span>
284+
{/each}
285+
{/if}
286+
```
287+
288+
## 🤝 Contributing
289+
290+
Contributions, issues and feature requests are welcome!
291+
292+
Feel free to check [issues page](https://github.com/cloudedots-projects/svelte-forms/issues).
293+
294+
## Show your support
295+
296+
Give a ⭐️ if this project helped you!
297+
298+
## 📝 License
299+
300+
Copyright © 2021 [Cloudedots <contact@cloudedots.com>](https://github.com/cloudedots-projects).
301+
302+
This project is [ISC](https://github.com/cloudedots-projects/svelte-forms/blob/master/LICENSE) licensed.

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ const clearErrors = (state: Writable<FormState> | any, touch = false, inner = fa
9999
try {
100100
let data = !inner ? get(state) : state;
101101
for (let key in data) {
102-
if (key !== '_touched' && key !== '_errors' && key !== '_index') {
102+
if (key !== '_touched' && key !== '_errors') {
103103
if (Array.isArray(data[key])) {
104104
let values = (data[key] || []) as any[];
105105
data[key] = values.map(

0 commit comments

Comments
 (0)