Tutorial: Non-Input Components

Non-Input Components

Forms

Form classList

The classList property of the form inherits both the dirtyCssClass and the invalidCssClass based on the overall form state. If any form input is dirty and the dirtyCssClass is configured, the <form> tag will receive the specified class. Same goes for the invalidCssClass depending if any of the form inputs are invalid.

Form id

All forms have a unique ID. It's a randomly generated, 5 character, hexadecimal based ID prefix with f-. This can be overridden using the configure method, by setting the instance configuration's id property manually, or by specifying one via the Form component's id.

Setting an ID using the configure method:

const F = ValidatedForm({
  username () {}
}).configure({
  id: 'custom-form-id'
})

Using the instance properties:

const F = ValidatedForm({
  username () {}
})

F.instance.id = 'custom-form-id'

Setting an ID on the form component:

<F.Form id='custom-form-id'>
  <F.Text name='username' />
</F.Form>
<!-- Generated DOM -->
<form id="custom-form-id">
  <label for="custom-form-id-username">Username</label>
  <input id="custom-form-id-username" name="username" />
</form>

Form values

(Since v1.3.0)
Initial values can be specified using the values property on the Form component. This, in combination with resources, will trigger Suspense to fallback.

Passing an object of strings as initial values:

<F.Form values={{ username: 'user@domain.tld' }}>
  <F.Text name='username' />
</F.Form>

Using resources and Suspense:

// Mock resource that emulates a two second API call
const [payload] = createResource(() => {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ username: 'user@domain.tld' }), 2000)
  })
})
<Suspense fallback={<div>Loading...</div>}>
  <F.Form values={payload()}>
    <F.Text name='username' />
  </F.Form>
</Suspense>

Form Events

Unlike input events, form events are forwarded before they change the state. The form's onReset and onSubmit events call event.preventDefault(). This can be disabled globally or per instance with the preventReset and preventSubmit configurations respectively. Note that disabling this can lead to unexpected behaviour with the Reset and Submit buttons.

Form onReset

Calls reset.

Form onSubmit

Calls save if the form is valid(using validate) or if the validateSave configuration is set to false.

Form preventReset

Disabling the call to event.preventDefault() during an onReset event will cause the default HTML behaviour to conflict with the bound behaviour(e.g. one tries to set a value but the other tries to clear it).

// Disable globally
ValidatedForm.preventReset = false

// Disable per form `instance`
F.instance.preventReset = false

// Disable using the `configure()` method
F.configure({ preventReset: false })

Disable using JSX:

<F.Form preventReset={false} />

Form preventSubmit

Disabling the call to event.preventDefault() during an onSubmit event will cause the form's default behaviour to redirect user's browser. Unless specified via the form's action and method properties, forms will make a GET request to the current URL.

// Disable globally
ValidatedForm.preventSubmit = false

// Disable per form `instance`
F.instance.preventSubmit = false

// Disable using the `configure()` method
F.configure({ preventSubmit: false })
<F.Form preventSubmit={false} />

Buttons

The button labels can be specified via the label property or as a child contents.

<F.Button label='Checkout' />
<F.Button>Checkout</F.Button>

Note that the Submit and Reset button functionality is bound to the form's onSubmit and onReset events respectively.

Labels

Displays the auto-generated username label:

<F.Label name='username' />
<!-- Generated DOM -->
<label for="f-00000-username">Username</label>

Specifying a custom label:

<F.Label name='username' label='Username or Email' />
<F.Label name='username'>Username or Email</F.Label>
<!-- Generated DOM -->
<label for="f-00000-username">Username or Email</label>

Errors

Displays the username error message:

<F.Error name='username' />
<!-- Generated DOM when input is invalid -->
<span class="error">Username is required</span>

Specifying a custom error label:

<F.Error name='username' label='Username or Email' />
<F.Error name='username'>Username or Email</F.Error>
<!-- Generated DOM when input is invalid -->
<span class="error">Username or Email is required</span>

Groups

The Group component can be bound to an input name to make its class property reactive to the input state. The configuration classes used are dirtyCssClass, groupCssClass, and invalidCssClass. These classes can be set globally or per instance.

Default Group Behaviour

The default Group behaviour is to wrap its contents with the above mentioned classes only if the groupCssClass is not set to false when the name property is a string.

const F = ValidatedForm({
  username (value) {
    if (value === '') return 'is required'
  }
}).configure({
  dirtyCssClass: 'dirty',
  groupCssClass: 'group',
  invalidCssClass: 'invalid'
})

Example of reactive grouping:

<F.Group name='username'>
  <p>Reactively wrapped content!</p>
</F.Group>
<!-- Generated DOM when input is not dirty -->
<div class="group">
  <p>Reactively wrapped content!</p>
</div>

<!-- Generated DOM when input is dirty but valid -->
<div class="group dirty">
  <p>Reactively wrapped content!</p>
</div>

<!-- Generated DOM when input is invalid -->
<div class="group dirty invalid">
  <p>Reactively wrapped content!</p>
</div>

Extended Group Behaviour

The Group component can be bound to multiple inputs by specifying an array of names as the name property. The content is wrapped regardless of the groupCssClass value.

const F = ValidatedForm({
  username (value) {
    if (value === '') return 'is required'
  }
}).configure({
  dirtyCssClass: 'dirty',
  invalidCssClass: 'invalid'
})

Example of reactivity to multiple inputs:

<F.Group name={['username', 'nickname']}>
  <p>Reactively wrapped content!</p>
</F.Group>
<!-- Generated DOM when username and nickname are valid -->
<div>
  <p>Reactively wrapped content!</p>
</div>

<!-- Generated DOM when username or nickname is dirty but neither are invalid -->
<div class="dirty">
  <p>Reactively wrapped content!</p>
</div>

<!-- Generated DOM when username or nickname is dirty and invalid -->
<div class="dirty invalid">
  <p>Reactively wrapped content!</p>
</div>