@glueit/forms
v0.0.37
Published
Glue It - Dead Simple, but powerful forms
Downloads
31
Readme
Glue It - Forms ·
Build React forms declaratively.
Table of Contents
- Prerequisites
- Installation
- Sample forms
- Hydration
- Custom fields
- Custom observers
- Intuitive and simple submit handling
- Validators
- Custom validators
- Custom messages
- Form states
- Objects (scoping)
- Lists
- Visibility
- Scope
- SetField
- Hooks based state management
Prerequisites
Forms is built on top of React. At the minimum you will need:
Installation
$ npm i @glueit/forms
Sample forms
You can run a sample form by cloning this repo and running these commands.
npm i
npm run start
Then open your browser at localhost:3000
.
Hydration
You can hydrate the form (i.e. for editing data that was already saved).
<Form
data={{ age: '17' }}
>
<CustomInput
name="age"
label="Age"
helperText="What is your age?"
required
/>
</Form>
Custom fields
Custom fields are created using the asField
higher order component.
Imports
import React from 'react'
import Form, {
Form,
asField
} from '@glueit/forms'
The custom field
const CustomInput = asField({
onChange,
onBlur = () => {},
name,
value,
errors,
label,
helperText,
required = false
}) => {
return (
<input
id={name}
value={value}
onChange={e => onChange(e)}
onBlur={e => onBlur(e)}
label={label}
required={required}
/>
)
})
The form
<Form>
<CustomInput
name="age"
label="Age"
helperText="What is your age?"
required
/>
</Form>
Custom observers
You can have observers in your Form
that can then render based on the form state.
Imports
import React from 'react'
import Form, {
Form,
withFormState
} from '@glueit/forms'
The observer
const SimpleStates = withFormState(({ form, stateStrs }) => (
<>
The current state of the form is:
<span>{stateStrs[form.state]}</span>
</>
))
The form
<Form>
<SimpleStates />
</Form>
Submit handling
Create your submit handler function.
const handleSubmit = async (e, body, actions) => {
e.preventDefault()
console.log(body)
console.log(actions)
// This is just for example...
const result = await new Promise(res => {
setTimeout(() => {
// actions.pristine('success')
//actions.clear('success')
actions.error('server')
res('done')
}, 3000)
})
console.log(result)
}
Pass that function to your Form
.
<Form onSubmit={(e, form, acitons) => handleSubmit(e, form, actions)}>
Submit handler actions
Your submit handler gets three params.
- e: This is the synthetic event. Be sure to call
e.preventDefault()
if you want to handle to submission yourself. - body: This is the data for your form in field name and value pair.
- actions: This is an object containing functions that you can call to update the form state when you are done.
Optional update
- actions.updates([{ path: "...", value: "..." }]): Allows you to update a list of fields.
Expected to call one of the following
- actions.clear([type]): This will clear the form and all data.
- actions.pristine([type]): This will leave the data in place but will set the form to pristine. Which means that there are no unsaved changes. It is useful if you are allowing ongoing edits with periodic saves.
- actions.error([type]): When an error occurs.
For all three of these actions
you can specify your own type. For example, maybe you want to have error_server
as shown above. In this case you would call with actions.error('server')
.
Built-in validators
Apply any or all of the following to your fields as needed.
- required (any)
- maxLength (string, array)
- minLength (string, array)
- max (numeric)
- min (numeric)
- match (regexp)
Custom validators
Create a function that takes a value:
const range20to30 = ({ value }) => {
return value >= 20 && value <= 30
}
Pass that function to your Form
and apply that validator to a field.
<Form validators={{ range20to30 }}>
<Input
name="age"
label="Age"
helperText="What is your age?"
required
range20to30
/>
</Form>
Custom messages
Create an object with your messages.
const messages = {
range20to30: 'You must be between 20 and 30 years of age to participate.'
}
Pass that object to your Form
.
<Form messages={messages}>
Form states
There are three states.
- pristine: A form that is pristine means that it has no modifications to its state. It can have data, but there are no [unsaved] modifications to its data.
- error: A form that has errors such as a required field or invalid data. A form can also be in error state if the submission fails on the server side such as a 500 error.
- dirty: A form that has unsaved changes.
You can override the messages for any of these or create custom states.
Custom states
Create an object with your states. Make sure each state is prefixed with one of the allowed states.
- pristine_
- error_
const stateStrs = {
pristine_success: 'Your submission was a success!',
error_server: 'There was a server error.'
}
Pass that object to your Form
.
<Form stateStrs={stateStrs}>
Object Scoping
Object scoping allows your output to have nested objects.
<Input name="name" label="Name" required />
<Input name="skills.first" label="Your Best Skill" required />
<Input name="skills.second" label="Your Second Skill" required />
<Input name="references[0].name" label="Your Reference Name #1" required />
<Input name="references[0].phone" label="Phone" required />
<Input name="references[1].name" label="Your Reference Name #2" required />
<Input name="references[1].phone" label="Phone" required />
Will create the following in your form output:
{
name: 'John Doe',
skills: {
first: '...',
second: '...
},
references: [
{
name: "...",
phone: "...
},
{
name: "...",
phone: "...
}
]
}
Lists
You can create lists that are arbitrarily deep. For example, employment history would be a list.
Import lists
import { List, withListActions } from '@glueit/forms'
Add and Remove Components
These can be any component that you want. Just wrap with the withListActions
HOC.
const AddListItem = withListActions(({ text, to, listActions }) => {
return <Link onClick={() => listActions.addItemToList(to)}>{text}</Link>
})
const RemoveListItem = withListActions(({ text, from, index, listActions }) => {
return (
<Link onClick={() => listActions.removeItemFromList(from, index)}>
{text}
</Link>
)
})
Add the List to your form
Notice that we create a List
with name of employers
. The value
of this field will now be an array
instead of a string
. Each element in the employers
array will be an object containing employer, title, duration, and a sub list of managers.
<Form>
<h3>Your Employment History</h3>
<List minLength="2" maxLength="5" name="employers">
<h5>Employer Entry</h5>
<RemoveListItem from text="Remove Employer" />
<Input name="employer" label="Employer" required />
<Input name="title" label="Your Title" required />
<Input name="duration" label="Years / months duration" />
<h6>Your Managers</h6>
<List name="managers" minLength="1" maxLength="2">
<Input name="name" label="Manager" />
<RemoveListItem from text="Remove Manager" />
</List>
<br />
<br />
<AddListItem to="managers" text="Add Manager" />
<hr />
</List>
</Form>
Visibility
You can control visibility of Fields
by using the VisibilityGroup
component. Only fields that are visible will carry to the onSubmit
event of the form.
import Form, {
VisibilityGroup
} from '@glueit/forms'
Then wrap your field(s) as needed. Use the isVisible prop to tell the group if it is visible or not.
<VisibilityGroup isVisible={form => form.fields.age && form.fields.age.value > 21}>
...
</VisibilityGroup>
Scope
You can control scope of child Fields
by using the Scope
component.
import Form, {
Scope
} from '@glueit/forms'
Then wrap your field(s) and provide a path to your Scope
. In this example you are wrapping a user
object with name
and email
.
<Scope path="user">
<Input name="name" label="Name" required />
<Input name="email" label="Email" required />
</Scope>
Set Field
Use set field to declaratively set the value of a field. This will only run once unless you change the value attribute OR destroy the field and re-create it.
import Form, {
SetField
} from '@glueit/forms'
<SetField name="firstName" value="Jeff">
State management
State is handled using the context hook. You can access the form state from onSubmit
property on Form
or using observers
as described above.