taffit / svelte-use-form

The most compact reactive form controller (including Validation) that you'll ever see.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

  Svelte Use Form

A svelte form library that is easy to use and has no boilerplate. It helps you control and validate forms and their fields and check on the state of them.

npm i -D svelte-use-form

GitHub package.json version npm

Features:

  • Supports: Inputs, TextAreas, Selects, Radio Buttons, Checkboxes...
  • Uses single object to represent the state, instead of splitting it up (errors, values, controls...)
  • Requires no special binding on the Form Input Elements, only the name attribute
  • OOTB validators and custom validator support
  • No requirement to use custom components
  • Dynamic Inputs => Show / Hide Inputs at runtime.

Usage

It's pretty self-explanatory, just check out the examples below 😉
Just make sure to prefix the form with $, when accessing its state.

REPLs:

Minimal Example REPL

<script>
  import { useForm, Hint, validators, minLength } from "svelte-use-form";

  const form = useForm();
</script>

<form use:form>
  <input name="title" use:validators={[minLength(5)]} />
  <Hint for="title" on="minLength" let:value>
    The title requires at least {value} characters.
  </Hint>

  <button disabled={!$form.valid}>Submit</button> <br />
</form>

or you can also print the error message like this:

...
  {#if $form.title?.touched && $form.title?.errors.minLength}
    The title requires at least {$form.title.errors.minLength} characters.
  {/if}

Login Example (Styling omitted) REPL

<script>
  import { useForm, HintGroup, Hint, validators, email, required } from "svelte-use-form";

  const form = useForm();
</script>

<form use:form>
  <h1>Login</h1>

  <input type="email" name="email" use:validators={[required, email]} />
  <HintGroup for="email">
    <Hint on="required">This is a mandatory field</Hint>
    <Hint on="email" hideWhenRequired>Email is not valid</Hint>
  </HintGroup>

  <input type="password" name="password" use:validators={[required]} />
  <Hint for="password" on="required">This is a mandatory field</Hint>

  <button disabled={!$form.valid}>Login</button>
</form>

API

const form = useForm(properties)

useForm() returns a svelte store (Observable) that is also an action. (That's what I call svelte 😆)

properties

interface FormProperties {
  [control: string]: {
    /** Initial value of the form control */
    initial?: string;
    /** The validators that will be checked when the input changes */
    validators?: Validator[];
    /**
     * The map through which validation errors will be passed.
     *
     * You can either pass a string or a function returning a new error value
     */
    errorMap?: ErrorMap;
  };
}

form

Contains an action that can be used on a form. It binds the form state to the form element.

$form

Subscribe to the form with $ prefix to access the state of the form. It returns a Form instance.

Form

class Form {
  [formControlName: string]: FormControl;
  get valid(): boolean;
  get touched(): boolean;
  get values(): {
    [formControlName: string]: string;
  };
}

Every input in the form will be accessible through $form directly. e.g. <input name="email" /> === $form.email

FormControl

/** A FormControl represents the state of a form member like (input, textarea...) */
export declare class FormControl {
  validators: Validator[];
  /**
   * Returns an object containing possible ValidationErrors
   * ### Example (All validators are throwing an error)
   * `{ required: true, minLength: 4, maxLength: 20 }`
   * ### Example 2 (Only required is not valid)
   * `{ required: true }`
   */
  errors: {
    [errorName: string]: ValidationErrors;
  };
  /**
   * Contains a map of values, that will be shown
   * in place of the original validation error.
   */
  errorMap: ErrorMap = {};
  /** If the FormControl passed all given validators. */
  valid: boolean;
  /**
   * If the FormControl has been interacted with.
   * (triggered by blur event)
   */
  touched: boolean;
  /** The initial value of the FormControl. Defaults to `""` if not set via `useForm(params)`. */
  readonly initial: string;
  get value(): string;
  set value(value: string);
/**
   * Set an error manually.
   *
   * The error will be removed after changes to the value or on validate()
   *
   * Used for setting an error that would be difficult to implement with a validator.
   * e.g. Backend Response returning Login failed
   * ``` typescript
   * function submit() {
   *    apiLogin($form.values).then(response => {})
   *    .catch(error => {
   *        if (error.status === 403) {
   *            $form.password.error({ login: "Password or username is incorrect" });
   *        }
   *    })
   * }
   * ```
   */
  error(errors: ValidationErrors): void
  /** Validate the FormControl by querying through the given validators. */
  validate(): boolean;
}

validators (Action)

Takes in the validators that should be used on the form control. e.g.

<input name="email" use:validators={[required, email]}>

Hint

Properties:

  • for="name_of_input"
  • on="error" > the error which should trigger it
  • hideWhen="different_error" > hides the hint if the different error is throwing
  • hideWhenRequired > shortcut for hideWhen="required"
  • showWhenUntouched > hint will get displayed even if the field hasn't been touched yet.
  • class="classes"
  • let:value > returns the value of the error

HintGroup

Properties:

  • for="name_of_input"

You can omit the Hint "name" prop when wrapping it with a HintGroup.

Validators

  • required
  • minLength(n)
  • maxLength(n)
  • number
  • email
  • url

Custom Validator

A validator needs to be a function that returns null if valid else an object with the key being the name of the error. The value of the object will be accessible through the error. e.g. $form.title.errors.name_of_error -> 'info'.

function passwordMatch(value: string, form: Form): null | ValidationErrors {
  return value !== form.password.value
    ? null
    : { passwordMatch: "Passwords are not matching" };
}
... use:validators={[passwordMatch]}
  or
... passwordConfirmation: { validators: [passwordMatch] } }

... $form.title.errors.passwordMatch

An example with validator.js REPL

Note

Chrome Autofill

When Chrome autofills the form on page load, it will register all inputs as valid. After clicking anywhere on the site, pressing a key or pressing the submit button it will validate all fields and set the correct state of the form. Note that when the user triggers a submit event, it will not fire if the fields are invalid. This solution was needed due to Chromes way of autofilling forms without really filling the inputs with values, until the page gets a click or key event.

About

The most compact reactive form controller (including Validation) that you'll ever see.

License:MIT License


Languages

Language:TypeScript 78.4%Language:JavaScript 13.1%Language:Svelte 8.4%