0.0.2 • Published 2 years ago

formsvelte v0.0.2

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

Formsvelte

A complete form solution for Svelte, with an API inspired by Formik.

This project is a work in progress.

Install

# Install with npm
npm i formsvelte

# Or install with yarn
yarn add formsvelte

Why?

Svelte has great built in bindings that make working with inputs much simpler than doing so would be in React (for example). But it's missing a solution for validation and error handling.

That's where Formsvelte comes in! By creating a validation schema, you can describe the rules of your form and the error messages that should be displayed when those rules aren't met. Formsvelte then takes those rules, makes sure they're followed, and displays your error message when they're not.

Example

<script>
  import {Formsvelte, Form, Field, Error} from 'formsvelte'
  import { string, object } from 'yup'

  const schema = object().shape({
    username: string()
      .email()
      .required('Please enter username'),
    password: string()
      .min(12, 'Password must be at least 12 characters')
      .required('Please enter a password')
  })
</script>

<Formsvelte
  initialValues={{ username: '', password: '' }}
  yupSchema={schema}
  onSubmit={(values) => alert(`Submitted!\n${JSON.stringify(values, null, 2)}`)}
>
  <Form>
    <div>
      <Field type="text" name="username" />
      <Error name="username" />
    </div>

    <div>
      <Field type="password" name="password" />
      <Error name="password" />
    </div>

    <button>Submit</button>
  </Form>
</Formsvelte>

API

<Formsvelte>

The root of every Formsvelte form that supplies context to the rest of the components.

Prop nameTypeDefaultRequired?Description
initialValuesT-YesThe initial set of values for your form. The shape of this object should correspond to the names of the Fields in your form.
onSubmit(values: T) => void-YesCallback invoked when the form is submitted
yupSchemaAnySchemaundefinedNoA Yup schema used to validate your form. Schema libraries other than Yup will be supported in the future.

<Form>

A replacement for the native <form> element, this component is necessary to capture the submit event. It takes no props other than an optional class.

<Field>

Every instance of Field renders an input of some kind. The type of that input is determined by the type prop.

Prop nameTypeDefaultRequired?Description
namestring-YesThe name of the input. This should map to the shape of a key within Formsvelte.initialValues via dot notation.
type'text' \| 'checkbox' \| 'radio' \| 'select' \| 'email' \| 'password'-YesThe type of input to render. type == 'select' accepts a default slot of <option>s.
valuestringundefinedNoThe value of this input. Typically only necessary for radio groups and checkbox groups.

<Error>

Display an error message from your validation schema when the rules of the schema are not met.

Prop nameTypeDefaultRequired?Description
namestring-YesThe name of the input (or group) that this error corresponds to. This should map to the shape of a key within Formsvelte.initialValues via dot notation.

More Examples

Checkbox

The value for a checkbox is just a boolean.

<script>
  const schema = object().shape({terms: boolean().isTrue()})
</script>

<Formsvelte
  initialValues={{terms: false}}
  yupSchema={schema}
>
  <Form>
    <Field type="checkbox" name="terms" />
    <Error type="terms">
  </Form>
</Formsvelte>

Radio Button Group

Notice that each of the radio inputs has the same name prop but different value props. When submitted, values[name] will have the value of the selected radio button. In this example, values.scoops will have 1, 2, or 3 as its value (the validation schema prevents 4 from being chosen).

<script>
  const schema = object().shape({
    scoops: number().min(1).max(3).required(),
  })
</script>

<Formsvelte
  initialValues={{scoops: ''}}
  yupSchema={schema}
>
  <Form>
      <label>
        <Field type="radio" name="scoops" value="1" />
        One
      </label>
      <label>
        <Field type="radio" name="scoops" value="2" />
        Two
      </label>
      <label>
        <Field type="radio" name="scoops" value="3" />
        Three
      </label>
      <label>
        <Field type="radio" name="scoops" value="4" />
        Four
      </label>
      <Error name="scoops" />
  </Form>
</Formsvelte>

Select

For selects, the <option>s are passed to the default slot of <Field>.

<script>
  const schema = object().shape({
    car: string().oneOf(['mercedes', 'audi']).required(),
  })
</script>

<Formsvelte
  initialValues={{car: ''}}
  yupSchema={schema}
>
  <Form>
    <Field type="select" name="car">
      <option value="">-</option>
      <option value="volvo">Volvo</option>
      <option value="saab">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </Field>
    <Error name="car" />
  </Form>
</Formsvelte>

Checkbox Group

Notice that each of the checkboxes in the group has the same name prop but different value props. When submitted, values[name] will be an array of the values of the selected checkboxes; this is different from a single checkbox whose value is a single boolean.

<script>
  const schema = object().shape({
    flavors: array()
      .of(string().oneOf(['vanilla', 'strawberry']))
      .min(1)
      .required(),
  })
</script>

<Formsvelte
  initialValues={{flavors: []}}
  yupSchema={schema}
>
  <Form>
    <label>
      <Field type="checkbox" name="flavors" value="vanilla" />
      Vanilla
    </label>
    <label>
      <Field type="checkbox" name="flavors" value="chocolate" />
      Chocolate
    </label>
    <label>
      <Field type="checkbox" name="flavors" value="strawberry" />
      Strawberry
    </label>
    <Error name="flavors" />
  </Form>
</Formsvelte>

Nested objects

Arbitrary levels of nesting are supported via dot notation in the name prop of Fields and Errors.

<script>
  const schema = object().shape({
    foo: object().shape({
      name: string().required('You forgot this one'),
      terms: boolean().isTrue('Please accept terms').required('Accept my terms or else'),
    }),
  })
</script>

<Formsvelte
  initialValues={{foo: {name: '', terms: false}}}
  yupSchema={schema}
>
  <Form>
    <div>
      <label>
        What's your name?
        <Field type="text" name="foo.name" />
      </label>
      <Error name="foo.name" />
    </div>

    <div>
      <label>
        <Field type="checkbox" name="foo.terms" />
        Please accept the terms & conditions
      </label>
      <Error name="foo.terms" />
    </div>
  </Form>
</Formsvelte>

FAQ

Svelte throws a linting error about labels being associated with a control

A11y: A form label must be associated with a control.
svelte(a11y-label-has-associated-control)

Svelte doesn't know that Field renders an input, so it's mad that you're using a <label> without an input.

The easiest way I've found to address this is to add for={undefined} to the label.

<label for={undefined}>
  What is your name?
  <Field type="text" name="name" class="textbox" />
</label>
0.0.2

2 years ago

0.0.1

2 years ago

0.0.0

2 years ago