An open API service indexing awesome lists of open source software.

https://github.com/kevinast/svelte-native-forms

minimalist form validation with powerful results
https://github.com/kevinast/svelte-native-forms

Last synced: about 2 months ago
JSON representation

minimalist form validation with powerful results

Awesome Lists containing this project

README

          

# svelte-native-forms

*... minimalist form validation with powerful results*

Validating forms has notoriously been a painful development
experience. Implementing client side validation _in a user friendly
way **is a tedious and arduous process**_
• you want to validate fields only at the appropriate time _(when the
user has had the chance to enter the data)_
• you want to present validation errors in a pleasing way
• you may need to apply custom validation _(specific to your
application domain)_
• etc.

Even with the introduction of [HTML5's Form Validation], it is still
overly complex, and doesn't address many common scenarios _(mentioned
above)_. Without the proper approach, form validation can be one of
the most difficult tasks in web development.


**Overview:**

    **svelte-native-forms** _(aka **SNF**)_ is a [svelte] utility that
    facilitates field validation in your native html forms.

    The term _**native**_ refers to the utilization of the native HTML
    `

` tag and the corresponding form elements (``,
``, ``, etc.). In other words, **SNF** does NOT
introduce components for these abstractions, rather you use the native
html representations.

Here are some **key points** to understand:

- **SNF** is based on **svelte actions**. By applying a simple action
to your form elements, the basics of the form validation control is
defined right in your html markup _... see [Native HTML Forms]_.

- **SNF** improves user experience by validating fields at the
appropriate time _... see [Validation Timing]_.

- **SNF** provides two simple reactive error display components, that
"auto wires" themselves to the appropriate form/fields, dynamically
displaying form-based errors as needed _... see [Reactive Error
Display]_.

- **SNF** supports the native [HTML5 Form Validation], by using
the standard validation attributes in your form elements, such as
`type`, `required`, `minlength`, etc. _... see [Built-In Form
Validation]_.

- **SNF** allows you to easily apply **custom validations**, where your
JavaScript code can perform app-specific validation _(even
involving cross field validations)_
_... see [Custom Validation]_.

- **SNF** is customizable
**• don't like the error display format?** _... that is easily resolved_
**• want to perform a custom field validation?** _... easy peasy_
_... see [Customization]_.

**SNF** promotes a **clean and simple approach** to form
validation, that yields **powerful results**.

**Important NOTE**: **SNF** is intended to be used by applications
that employ native html forms and form elements. Because of
**SNF**'s usage of svelte actions, **this is a hard restriction**!
_Svelte actions may only be applied to native DOM elements - **not**
components_. **If you are a minimalist** _(using native html)_, you will
appreciate this abstraction of form/field validation. **If you are
using a more inclusive UI library**, it most likely already promotes
Form/Element component abstractions _(which will typically provide a
technique to handle form validation)_.


## At a Glance

- [Install]
- [Basic Example]
- [Concepts]
- [Native HTML Forms]
- [Validation Timing]
- [Reactive Error Display]
- [Built-In Form Validation]
- [Custom Validation]
- [Actions]
- [`formChecker`]
- [`fieldChecker`]
- [Components]
- [``]
- [``]
- [Controllers]
- [`formController`]
- [Advanced Concepts]
- [Svelte Bound Variables]
- [Customization]
- [Error Look and Feel]
- [Competition]


## Install

?? Installation


## Basic Example

Here is a very basic example of **SNF** usage:

?? AI: would be nice to highlight the SNF specifics (NOT POSSIBLE IN quoted stuff) ?? consider an image?

```html

import {formChecker, FormErr,
fieldChecker, FieldErr} from 'svelte-native-forms';

const submit = (event, fieldValues) => alert(`Successful submit (all fields are valid)!`);
const isIdUnique = ({id}) => id==='dup' ? 'ID must be unique' : '';


Name:



ID:







```

At it's core, this example is using HTML's standard [Built-In Form
Validation]. However with the simple injection **SNF**'s `Checker`
actions, and **SNF**'s **error display** components, a sophisticated
control structure has been applied that promotes a very usable form.
We have even seamlessly introduced a **custom field validation**!

?? IMG: show result

You can interact with this example in the following ?REPL.

Here are some points of interest:

- the `use:formChecker` action is applied to the ``

- a `submit` function is registered to this action. This is used in
lue of the standard `on:submit` event. This function is invoked
on a standard `submit` request, but **only when all fields pass
validation**. If there are field errors, the appropriate messages
are displayed, and no submit occurs.

- the `` form elements are employing some standard [Built-In
Form Validation] constraints _(`type`, `required`, `minlength`, etc.)_.

- the `use:fieldChecker` actions are applied to each `` form
element. This completes the knowledge transfer of your form
structure to **SNF**. (AI: can this be implied when all defaults are
used?)

- a **custom field validation** has been introduced _(for the `id`
field)_, by using the `validate` action parameter. This invokes a
function that can apply application-specific validations. The
function merely returns an error string _(if invalid)_, or an empty
string _(when valid)_.

- the `` components will dynamically bind to the input field
of interest, and conditionally display appropriate errors for that
field.

- the `` component dynamically binds to the form, and and
conditionally displays an appropriate error when a submit is
requested on an invalid form.

- a standard `submit` control is applied to the form. As mentioned
above, the **SNF** `submit` function is only invoked when all fields
pass validation.


## Concepts

**svelte-native-forms** _(aka **SNF**)_ is a [svelte]
utility that facilitates field validation in your native html forms.

The following sections discuss the basic concepts of **SNF**:

- [Native HTML Forms]
- [Validation Timing]
- [Reactive Error Display]
- [Built-In Form Validation]
- [Custom Validation]


## Native HTML Forms

    In **SNF**, your interactive forms continue to use the native HTML
    `

` tag along with the corresponding form elements (``,
``, ``, etc.). In other words, **SNF** does NOT
introduce components for these abstractions, rather you use the native
html representations.

This is accomplished by using **svelte actions**. By applying a simple
action to your form elements, the basics of the form validation
control is defined right in your html markup.

This represents a different approach from other form validation
libraries. Some may require you to define a separate JavaScript
control structure that either drives your html markup, or duplicates
the html hierarchy in some way. Others may introduce their own
component layer on top of the native form elements.

Because **SNF** is action-based, it can utilize your DOM hierarchy
to implicitly define the validation control structure, _making it a
simple and understandable **single source of truth**_. In other words, _**your form
validation control is defined right in your html markup!**_


## Validation Timing

    Validation timing is an important characteristic in achieving a better
    user experience. You don't want to overwhelm your users by validating
    every field from the start, yet once a field has been seen, validation
    is more appropriate.

    **SNF** employs a validation heuristic, where fields are validated
    at the appropriate time:

    - only when they have been seen by the user _(i.e. touched)_,
    - or when a submit has been attempted.

    The "touched" determination is made through focus semantics. A field is
    considered to have been touched when it has gone in and out of focus
    (i.e. blur).

    This heuristic is a common technique that is tedious to accomplish,
    _when attempted in application code_.


## Reactive Error Display

    **SNF** provides two reactive error display components that
    dynamically display appropriate errors as needed. The beauty of these
    components are that they auto-wire themselves to the appropriate
    error. As a result, they are very simple to use.

    In the following example, the mere inclusion of `` will
    dynamically display any **name-field** errors at the appropriate time,
    simply because it is contained in the `` that holds the
    **name** `` _(this is not the only way to bind errors, but a
    very common one)_:

    ```html

    Name:

    ```

    There are two reactive error display components:

    - [``]: for displaying form errors
    - [``]: for displaying field errors


## Built-In Form Validation

    Web standards provide a native way to achieve client-side validation
    _(see [HTML5 Form Validation])_. This is accomplished by simply
    applying validation attributes to your form elements, such as `type`,
    `required`, `minlength`, `maxlength`, `min`, `max`, `pattern`, etc.

    Roughly speaking, this standard is broken up into two parts:

    - **validation** _(i.e. built-in validation)_:

    **SNF** supports the continued use of built-in validation, _in
    addition to the ability to easily inject [Custom Validation]_. In
    other words, you may continue to use the HTML validation attributes
    _(mentioned above)_. All built-in validation messages are presented
    to the user before any custom validations.

    - **presentation** _(of validation errors)_:

    The error presentation provided by standard HTML is somewhat
    primitive and unrefined. It is for this reason that **SNF** takes
    over the the presentation of validation errors. This _(in
    conjunction with **SNF**'s **powerful validation heuristic**)_,
    provides a **much improved user experience**.

    **SNF** accomplishes this by applying the `novalidate` attribute to
    your `

` element. While this disables the **presentation**
aspects of the standard, it leaves the constraint validation API
intact _(along with CSS pseudo-classes like `:valid` etc.)_. This
allows **SNF** to continue to interpret and support the built-in form
validations!


## Custom Validation

    You can easily apply **custom validations**, where your JavaScript
    code provides app-specific validation. This even includes more
    complex things, such as cross field validation!

    This is accomplished through the `validate` parameter of the
    [`fieldChecker`] action.

    The following simple example confirms that the `id` field is unique,
    _based on an app-specific `isUnique()` function_.

    ```html
    isUnique(id) ? '' : 'ID must be unique''}}>
    ```

    The `validate` hook can employ any logic _(as complex or simple as
    needed)_, and can reason about multiple fields _(not just one)_.
    Ultimately it returns a string - which is either an **error** _(for
    non-empty strings)_ or **valid** _(for an empty string)_.


## Actions

The primary instrument that **SNF** uses are **svelte actions**! There are two:

1. [`formChecker`]: to be applied to your `` element
2. [`fieldChecker`]: to be applied to your interactive form elements (``, ``, ``, etc.)


## `formChecker`

    The `formChecker` svelte action is applied to your html `

`
element. It registers the form to **SNF** control, allowing it
orchestrate form validation.

**Action Usage:**

```html

import {formChecker} from 'svelte-native-forms';

const submit = (event, fieldValues) => alert(`Successful submit (all fields are valid)!`);

... snip snip

```

**Action Parameters:**

The `formChecker` action supports the following parameters:

- **`submit`**: a required submit function that executes a
client-specific process. This is invoked when a submit occurs, only
when all fields pass validation.

**submit() API:**
```
+ submit(event, fieldValues): void
NOTE: All field values (monitored by SNF) are passed as named parameters.
```

- **`errStyles`**: an optional object containing the in-line styles to
apply to input elements that are in error.

- use an object with in-line styling pairs: camelCaseKey/value
- or `null` to disable

**DEFAULT**:
```
{
border: '2px solid #900', ... solid red border
backgroundColor: '#FDD', ... pink background
}
```

**formController:**

The `formChecker` action promotes a `formController` API through
which your application can programmatically make various form-specific
requests _(like resetting the form)_. Please refer to the
[`formController`] section for a discussion of this API _(and how to
access it)_.


## `fieldChecker`

    The `fieldChecker` svelte action is applied to your interactive form
    elements (``, ``, ``, etc.). It registers
    the form element to **SNF** control, allowing it to participate in the
    form's validation. In addition it injects the form value into the set
    of `formFields` passed to your submit function.

    If your field has **no validation** constraints, this action is
    completely optional. The only down-side to omitting the action is
    that the field value will NOT be included in the set of `formFields`
    passed to your submit function.

    Form elements containing this action **must have** a `name` or `id`
    attribute. While this is a common form requirement, in the case of
    `fieldChecker`, it derives it's `fieldName` from these attributes
    _(the `name` attribute takes precedence over `id`)_.

    AI: ?? per web standards I think a form submission REQUIRES a `name` attribute
    - however **SNF** may need id to sync up various elements
    - this may need to be tweaked (however we need to play with ALL the form elements)

    **Action Usage:**

    ```html

    import {formChecker} from 'svelte-native-forms';

    ... in the context of a form:

    ... NO action parameters (all defaulted)

    ... WITH action parameters


    ... snip snip
    ```

    **Action Parameters:**

    The `fieldChecker` action supports the following parameters _(all optional)_:

    - **`validate`**: an optional function that applies custom
    client-specific validation to this field.

    **DEFAULT**: NO custom validation is applied ... only [Built-In Form
    Validation] _(e.g. `type`, `required`, `minlength`, etc.)_

    **validate() API:**
    ```
    + validate(fieldValues): errMsgStr (use '' for valid)
    NOTE: All field values are passed as named parameters,
    supporting complex inner-dependent field validation
    EX: validate({address, zip}) ...
    Typically you only access the single field being validated
    EX: validate({address}) ...
    ```

    **validate() example:** ?? more realistic
    ```js
    function validate({foo}) {
    return foo==='dup' ? 'foo must be unique' : '';
    }
    ```

    - **`initialValue`**: optionally, the initial value to apply to this
    field, both on DOM creation and `reset()` functionality.

    **DEFAULT**: NO initial value is programmatically applied. In other
    words the initial value is strictly defined by your html.

    - **`boundValue`**: optionally, the application variable bound to this
    fieldNode. This is required when svelte's `bind:value` is in affect
    _(due to a web limitation, see: [Svelte Bound Variables])_.

    - **`changeBoundValue`**: optionally, a client function that changes
    it's bound value. This is required when **SNF**'s `initialValue`
    and `boundValue` are in affect _(also due to the same web limitation,
    see: [more when `initialValue` in use ...])_.

    **changeBoundValue() API:**
    ```
    + changeBoundValue(initialValue): void
    ... the implementation should update the client boundValue to the supplied initialValue
    ```


## Components

**SNF** promotes two reactive components that display form-based error
messages in your application:

- [``]: for displaying form errors
- [``]: for displaying field errors

The beauty of these components are that they auto-wire themselves to
the appropriate error. As a result, they are very simple to use.


## ``

    The `` component dynamically displays a generalized message
    when something is wrong with one or more form fields. The default
    message is: Please correct the highlighted field errors but can
    easily be overwritten through the `errMsg` property.

    `` must be a descendant of a `` that is
    controlled by a `formChecker` action. This is how it implicitly
    auto-wires itself to the form's error status.

    **Usage:**

    ```html

    ... snip snip

    ```

    **Visual:**

    ?? screen shot

    **API:**
    ```
    ... the display component that renders the error
    ACCEPTS: errMsg property ... an empty string ('') represents NO error
    DEFAULT: the standard internal error display component
    ```

    **Customization:**

    Please refer to the [Error Look and Feel] section to see how you can
    customize the display of your error messages _(using the `DispErr`
    property)_.


## ``

    The `` component dynamically displays a field-specific
    message when the field it is monitoring is invalid.

    This component auto-wires itself to the monitored field of interest
    either through the `forName` property, or implicitly through
    it's placement within a `` container _(see **Usage** below)_.

    **Usage** _(via `forName`)_:

      ```html

      ```

      In this case, the monitored field is identified through the **SNF**
      defined `fieldName` _(which can be the DOM `name` or `id` attributes,
      `name` taking precedence)_.

      Example: ``

    **Usage** _(via implicit `label` containment)_:

      ```html

      Zip:

      ```

      In this case, the monitored field is implicitly identified through
      it's placement in a `` container. Both the field and
      `` are nested in a `` element. This is similar to
      how an `` element can be implicitly associated to it's
      ``.

    **Visual:**

    ?? screen shot

    **API:**
    ```
    containment
    [DispErr={}]/> ... the display component that renders the error
    ACCEPTS: errMsg property ... an empty string ('') represents NO error
    DEFAULT: the standard internal error display component
    ```

    **Customization:**

    Please refer to the [Error Look and Feel] section to see how you can
    customize the display of your error messages _(using the `DispErr`
    property)_.


## Controllers

**SNF** promotes public APIs through which application code can
programmatically make various requests.

- These API's are created and promoted by **SNF** actions, and
therefore are scoped to the DOM controlled by the action.

- Access to these API's are provided through event handlers. The
reason for this is there is **no direct way to return content from
svelte actions**. Please refer to content _(below)_ for details on
each controller.

- _**While this probably goes without saying**_, these APIs should not
be used outside the scope of the action that created them. As an
"internal note", there is no process by which an event notification
can mark them as stale, because the action-attached DOM element has
already been removed prior to any action life-cycle notification.

If the application violates this tenet, the API will throw an Error
such as:

```
***ERROR*** formController.reset(): stale reference, the formCheckerAction DOM has been removed!
```

- These APIs promote **true functions (not methods)**. In other words
their execution does **not** require a `controller` dereference. As
an example, the following snippet retains a "function only"
_(without invocation)_ ... so on event handler invocation, it is
truly a function:

```html
Reset
```


## `formController`

    The `formController` is specific to a `

` _(i.e. the
[`formChecker`] action)_.

**`formController` API:**

_Currently, this API promotes only one function (but provides a place
for future expansion)_.

```
{
+ reset(): void ... reset the form back to it's original state.
}
```

Here are the specifics of each function:

- `+ formController.reset(): void`

This resets the form back to it's original state, including:

- initial field values _(as supplied through the [`fieldChecker`]
`initialValue` action parameter)_
- clear all error state _(both form and fields)_
* form error message
* field error messages
- reset **Validation Timing** heuristics _(both form and fields)_
* form as "submit NOT attempted"
* fields as "not been touched"

A reset is useful when a form is re-used by retaining it's DOM
representation _(say when used in a form-based dialog)_.

**Accessing `formController`:**

Because svelte actions have **no direct way to return content to the
client**, a `form-controller` event is emitted that contains the
`formController` _(found in `event.detail.formController`)_. The
application can simply monitor this event _(on the `` element)_,
and retain the `formController` for it's use. Here is an example:

```html

let formController;
... snip snip

formController = e.detail.formController }>
... snip snip

```


## Advanced Concepts

The following sections discuss more advanced concepts of **SNF**:

- [Svelte Bound Variables]


## Svelte Bound Variables

    A very powerful feature of svelte is it's ability to provide a two-way
    binding between form elements and JS variables. By simply using the
    `bind:` directive these two aspects are kept "in sync". For example:

    ```html

    let name = 'World';

    Hello {name}!


    ```

    For **SNF** usage, when two-way bindings are in affect, the
    `boundValue` parameter must be specified in the [`fieldChecker`]
    action. For example:

    ```html

    ```

    _**Why is this required? It seems a bit cumbersome!**_

      As it turns out, this has nothing to do with svelte or the **SNF**
      library. Rather it is a limitation of the web itself, where updates
      to `fieldNode.value` **do not** emit any web events _(such as the
      `on:input` event)_.

      - Svelte manages it's two-way binding by directly managing the
      `fieldNode.value`.

      - By default, **SNF** monitors form element changes through the
      `on:input` event.

      Because of this web limitation, **SNF** requires visibility to
      `boundValues`, in order to have access to a "single source of truth".
      This is unfortunate, but there is nothing we can do about it :-(

    ### more when `initialValue` in use ...

      This limitation gets worse, when you specify an `initialValue` for
      bound elements. In this scenario **SNF** must update the "single
      source of truth", which _(in this case)_ is the `boundValue`. This
      cannot be accomplished by **SNF** directly, because the svelte
      compiler must have visibility of this change. As a result, when BOTH
      `initialValue` and `boundValue` are in affect, the client must also
      specify a `changeBoundValue` function. For example:

      ```html
      name = initialValue
      }}>
      ```

      **changeBoundValue() API:**
      ```
      + changeBoundValue(initialValue): void
      ... the implementation should update the client boundValue to the supplied initialValue
      ```


## Customization

**SNF** is customizable!

- **don't like the error display format?** _... that is easily resolved_
- **want to perform a custom field validation?** _... easy peasy_

The following sections discuss various **customization features**:

- [Error Look and Feel]


## Error Look and Feel

    **SNF** provides two reactive error display components that
    dynamically display appropriate errors as needed ([``] and
    [``]).

    **The "Look and Feel" of these components can easily be overridden.**
    They merely monitor the appropriate reflective error state, and defer
    their display to an internal `` component _(passing the
    `errMsg` to display ... where an empty string (`''`) represents no error)_.

    This display component is extremely simple! It styles and displays
    the message using animation. Here is the default implementation:

    **DispErr.svelte** _... the **DEFAULT** internal display component_
    ```html

    import {fade} from 'svelte/transition';
    export let errMsg;

    {#if errMsg}

    {errMsg}

    {/if}

    .error {
    font-size: 80%;
    font-weight: bold;
    font-style: italic;
    color: #900;
    }

    ```

    Because of it's simplicity, you can easily implement your own display
    component and pass it to [``]/[``] through the
    `DispErr` property. Simply use the `DispErr.svelte` _(above)_ as a
    pattern. _**You can tailor the style and animation to your
    application needs!**_

    Here is an example that overrides the default:

    ```html

    ```

    AI: ?? Provide a full-blown example that overrides the internal DispErr component. Here are some ideas:

    - NOTE: this covers BOTH FieldErr and FormErr
    - change the animation
    - change the error display into a red box via the following form style:
    ```

    .formError {
    padding: 0.3em;
    font-size: 80%;
    color: white;
    background-color: #900;
    border-radius: 5px;
    box-sizing: border-box;
    }

    ```
    - here is the corresponding field style (a red box with square top so it looks like part of the input):
    ```

    .fieldError {
    width: 100%;
    padding: 0.3em;
    font-size: 80%;
    color: white;
    background-color: #900;
    border-radius: 0 0 5px 5px;
    box-sizing: border-box;
    }

    ```


## Competition

Quick review of current validation utils on ["made with svelte"] ...

**BOTTOM LINE**
- think twice about joining the mix
- there is a lot to think about here
- may be too time consuming
- there are a couple of form/validation libs that look robust

**sveltejs-forms**


    Declarative Forms (KJB: NO NO NO: full form library)

    - https://madewithsvelte.com/sveltejs-forms
    - https://github.com/mdauner/sveltejs-forms
    - https://www.npmjs.com/package/sveltejs-forms <<< 400 weekly downloads

**Sveltik**


    Form Library inspired by Formik (KJB: MAY BE minimalist COMPETITION <<< looks robust)

    - https://madewithsvelte.com/sveltik
    - https://nathancahill.com/sveltik/introducing
    - https://github.com/nathancahill/sveltik
    - https://www.npmjs.com/package/sveltik <<< 323 weekly downloads
    - NOT sure I like the `let:xxx` usage, HOWEVER this may open it up to BOTH native/component forms

    ```
    * seems lightweight
    * communicates with let:xxx directives
    * can use native form/formElm
    * has a component that can wrap the entire native form (again communicating via let:xxx)
    * also has option of / components
    KOOL: they can actually wrap native elms (using slots) <<< this is what I should do in my ErrorMsg
    * have component ... must tell it what it's used for (just like mine)

    ```

**Svelte Forms Lib**


    Lightweight Library for Managing Forms - inspired by Formik (KJB: MAY BE minimalist COMPETITION <<< looks robust)

    - https://madewithsvelte.com/svelte-forms-lib
    - https://svelte-forms-lib-sapper-docs.tjin.now.sh/introduction <<< extensive docs
    - https://github.com/tjinauyeung/svelte-forms-lib
    - https://www.npmjs.com/package/svelte-forms-lib <<< 700 weekly downloads

    ```
    * must setup some structure
    - uses what it calls observables WHICH I think are Svelte stores
    - BASED ON THIS, it seems a bit complicated to use
    * but then use native

too
- it also has "Helper components"
- it has styling overrides (via global classes)
* custom validation is very similar to mine (is procedural),
however it has ONE form validator function that must set errors for all fields <<< hmmmm
```

**Svelte Svelte Formly**


    A Form Generator (KJB: NO NO NO)

    - https://madewithsvelte.com/svelte-formly
    - https://github.com/arabdevelop/svelte-formly
    - https://www.npmjs.com/package/svelte-formly <<< 50 downloads a week

    ```
    * must setup some structure
    - seems kinda clunky
    * THEN you have a single <<< suspect is too intrusive/opinionated
    - i.e. it is a generator
    ```


[Install]: #install
[Basic Example]: #basic-example
[Concepts]: #concepts
[Native HTML Forms]: #native-html-forms
[Validation Timing]: #validation-timing
[Reactive Error Display]: #reactive-error-display
[Built-In Form Validation]: #built-in-form-validation
[Custom Validation]: #custom-validation
[Actions]: #actions
[`formChecker`]: #formchecker
[`fieldChecker`]: #fieldchecker
[Components]: #components
[``]: #formerr
[``]: #fielderr
[Controllers]: #controllers
[`formController`]: #formcontroller
[Advanced Concepts]: #advanced-concepts
[Svelte Bound Variables]: #svelte-bound-variables
[more when `initialValue` in use ...]: #more-when-initialvalue-in-use-
[Customization]: #customization
[Error Look and Feel]: #error-look-and-feel
[Competition]: #competition


[svelte]: https://svelte.dev/
["made with svelte"]: https://madewithsvelte.com/form
[HTML5 Form Validation]: https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation
[HTML5's Form Validation]: https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation