Svelte data validation and structuring library. The main motivation is to make dynamic forms, where amount of fields and their structure may change depending on previous input, easily to work with, but can be used for forms of any complexity. Approach is do not distinguish between fields and form, everything is a value, can be a part of another value and have sub-values.
Simple schema for primitive values, result value is the same as value in the store.
<script lang="ts">
import { field } from 'svelte-schemas';
import { min } from 'svelte-schemas/rules';
const username = field('', [min(3)]);
const errorMessages = {
min: 'Cannot be less than 3 characters'
};
</script>
<input type="text" bind:value={$username.value} class:error={!$username.valid} />
{#each $username.errors as error}
{errorMessages[error]}
{/each}
<style>
input {
border: 1px solid #000000;
}
.error {
border-color: #ff0000;
}
</style>
Value in the store contains an array of schemas, has 'add()' and 'delete(index)' methods, 'result' method will collect and return results from schemas inside.
Example (MainForm.svelte):
<script lang="ts">
import { field } from 'svelte-schemas';
import { email, max } from 'svelte-schemas/rules';
import NestedForm from '$lib/NestedForm.svelte';
function createEmailField(value: string): Field<email> {
return field(value, [email()]);
}
const emails = list(createEmailField, '', [], [min(1), max(5)]);
</script>
<button on:click={emails.add}>Add email:</button>
{#each emails as mail, i}
<NestedForm {mail} index={i} onDelete={emails.delete} />
{/each}
Example (NestedForm.svelte):
<script lang="ts">
import type { Field } from 'svelte-schemas';
export let email: Field;
export let index: number;
export let onDelete: (index: number) => void;
</script>
...
Value in the store is an object with schemas, 'result' method will return an object with results by keys.
Example:
<script lang="ts">
import { field, struct } from 'svelte-schemas';
const firstname = field('');
const lastname = field('');
const person = struct({
firstname,
lastname
});
</script>
...
mix and max rules will compare length if value is an array or string.
Use constructor function to create a custom rule.
Example:
export function regex(pattern: RegExp): Rule<string> {
return {
name: 'regex',
validate: async function (value: string): Promise<boolean> {
return !!value.match(pattern);
}
};
}