Declarative forms for Svelte.
- optional schema-based validation through Yup
- access to nested properties using paths
- supports custom components
- provides
Input
,Select
,Choice
components to reduce boilerplate
$ npm i sveltejs-forms
or
$ yarn add sveltejs-forms
<script>
import { Form, Input, Select, Choice } from 'sveltejs-forms';
import yup from 'yup@0.27';
function handleSubmit({ detail: { values, setSubmitting, resetForm } }) {
setTimeout(() => {
console.log(values);
setSubmitting(false);
resetForm({
user: { email: 'test@user.com' }, // optional
});
}, 2000);
/**
* {
* user: {
* email: 'email@example.com'
* },
* password: '123456',
* language: 'svelte',
* os: 'osx,linux'
* }
*/
}
function handleReset() {
console.log('form has been reset');
}
const schema = yup.object().shape({
user: yup.object().shape({
email: yup
.string()
.required()
.email(),
}),
password: yup.string().min(4),
language: yup.string().required(),
os: yup.string(),
});
const langOptions = [
{ id: 'svelte', title: 'Svelte' },
{ id: 'react', title: 'React' },
{ id: 'angular', title: 'Angular' },
];
const osOptions = [
{ id: 'macos', title: 'macOS' },
{ id: 'linux', title: 'Linux 🐧' },
{ id: 'windows', title: 'Windows' },
];
const initialValues = {
language: 'svelte',
};
</script>
<style>
:global(.sveltejs-forms) {
background-color: #f8f8f8;
display: inline-block;
padding: 1rem;
border: 1px solid #ccc;
border-radius: 5px;
}
:global(label) {
font-size: 0.8rem;
color: #888;
margin-bottom: 0.2rem;
}
:global(.message) {
font-size: 0.8rem;
color: #888;
margin: 0.2rem 0;
color: #f56565;
}
:global(input[type='text']),
:global(textarea),
:global(select) {
width: 100%;
background-color: white;
margin: 0;
}
:global(input[type='checkbox'] + label) {
display: inline-block;
margin-right: 2rem;
}
:global(.field) {
margin-bottom: 1rem;
}
button {
border-radius: 5px;
padding: 0.5rem 1rem;
margin-right: 1rem;
color: white;
}
button[type='reset'] {
background-color: #f56565;
}
button[type='submit'] {
background-color: #48bb78;
width: 80px;
}
</style>
<Form
{schema} <!-- optional -->
{initialValues} <!-- optional -->
validateOnBlur={false} <!-- optional, default: true -->
validateOnChange={false} <!-- optional, default: true -->
on:submit={handleSubmit}
on:reset={handleReset}
let:isSubmitting
let:isValid
>
<Input
name="user.email" <!-- nested field -->
label="Email Address"
value="test@user.com" <!-- initial value -->
placeholder="e.g. user@example.com" />
<Input name="password" type="password" placeholder="Password" />
<Select name="language" options={langOptions} />
<Choice
name="os"
options={osOptions}
disabled
multiple />
<button type="reset">Reset</button>
<button type="submit" disabled={isSubmitting}>Sign in</button>
<div>The form is valid: {isValid}</div>
</Form>
<script>
import { Form } from 'sveltejs-forms';
import Select from 'svelte-select';
import yup from 'yup@0.27';
let svelteSelect;
function handleSubmit({ detail: { values, setSubmitting, resetForm } }) {
setTimeout(() => {
console.log(values);
setSubmitting(false);
svelteSelect.handleClear();
resetForm();
}, 2000);
}
const schema = yup.object().shape({
food: yup.string().required()
});
let items = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'pizza', label: 'Pizza' },
{ value: 'cake', label: 'Cake' },
{ value: 'chips', label: 'Chips' },
{ value: 'ice-cream', label: 'Ice Cream' },
];
</script>
<Form
{schema}
on:submit={handleSubmit}
let:isSubmitting
let:setValue
let:values
let:errors
let:touched>
<Select
{items}
bind:this={svelteSelect}
inputAttributes="{{ name: 'food' }}"
hasError="{touched['food'] && errors['food']}"
on:select="{({ detail }) => setValue('food', detail.value)}"
on:clear="{() => setValue('food', '')}"
selectedValue="{items.find(item => item.value === values['food'])}"/>
<button type="submit" disabled={isSubmitting}>Submit</button>
</Form>
Name | Type |
---|---|
isSubmitting | boolean |
isValid | boolean |
setValue(path, value) | function |
touchField(path) | function |
validate() | function |
values | object |
errors | object |
touched | object |
All contributions are welcome.