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>
// 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>