form-builder
can be installed with NPM or Yarn.
# Installing with NPM
npm i --save @langleyfoxall/react-dynamic-form-builder
# Installing with Yarn
yarn add @langleyfoxall/react-dynamic-form-builder
Check out the demo.
// ./forms/create-user.js
export default [
{
name: 'first_name',
label: 'First Name'
},
{
name: 'last_name',
label: 'Last Name'
},
{
name: 'email_address',
label: 'Email Address',
type: 'email'
},
{
name: 'password',
label: 'Password',
type: 'password'
},
{
name: 'confirmation_password',
label: 'Confirm Password',
type: 'password'
},
];
// ./components/create-user-modal.jsx
import React from 'react';
import axios from 'axios';
import FormBuilder from '@langleyfoxall/react-dynamic-form-builder';
import Modal from './modal';
import Form from '../forms/create-user';
class CreateUserModal extends React.Component {
onSubmit(submission) {
return new Promise(resolve => (
axios
.post('/api/register', submission.data.form)
.then(() => console.log('Successfully signed up'))
.catch(() => console.log('Unable to sign up'))
.finally(resolve)
))
}
render() {
return (
<Modal>
<FormBuilder
form={Form}
onSubmit={this.onSubmit}
submitButton={{
text: 'Sign Up'
}}
/>
</Modal>
)
}
}
export default CreateUserModal;
form-builder
makes it easier to create dynamic react forms by handling most of the logic involved.
The following props can be passed:
defaultInputClass
(string, default:input
)defaultLabelClass
(string, default:label
)defaultContainerClass
(string, default:container
)defaultValidationErrorClass
(string, default:error-label
)defaultSubmitClass
(string, defaultsubmit
)defaultValues
(object, default:{}
)values
(object, default:null
)classPrefix
(string, default:rdf
)invalidInputClass
(string, default:invalid
)validInputClass
(string, default:valid
)validationTimeout
(number, default:1000
)submitButton
(object,{ text, className? }
)onSubmit
(function)loading
(boolean, defaultfalse
)loadingElement
(element, defaultnull
)formErrors
(object, default:{}
)form
(array)
defaultInputClass
is appended to classPrefix
on inputs.
// With default value
'rdf-input'
defaultLabelClass
is appended to classPrefix
on labels.
// With default value
'rdf-label'
defaultContainerClass
is appended to classPrefix
on input containers.
// With default value
'rdf-container'
defaultValidationErrorClass
is appended to classPrefix
on error texts.
// With default value
'rdf-error-label'
defaultSubmitClass
is appended to classPrefix
on the managed submit button.
// With default value
'rdf-submit'
defaultValues
is used to pre-populate the inputs. It matches the property name against an input name.
// With default value
{}
// With custom value
{
first_name: 'John',
last_name: 'Doe',
}
values
is used to overwrite the state of the form and set values from outside the form.
This should be used in conjunction with onChange
.
// With default value
null
// With custom value
{
first_name: 'John',
last_name: 'Doe',
}
classPrefix
is used to prefix the majority of the class names.
// With default value
'rdf'
invalidInputClass
is appended to classPrefix
on invalid inputs.
// With default value
'rdf-invalid'
validInputClass
is appended to classPrefix
on valid inputs.
// With default value
'rdf-valid'
submitButton
can be used to pass a managed submit button into the form.
className
is optional and gets appended to classPrefix
.
// With custom value
{
text: 'Sign up'
// className: 'secondary-button'
// > 'rdf-secondary-button'
}
onSubmit
should be called when the form is ready to be submitted, it is also triggered when the submitButton
is pressed.
When onSubmit
is triggered the callback will recieve an object containing:
{
valid, // Boolean for valid state of inputs
data: {
form, // Data from inputs
validation_errors // An object of errors for each input
}
}
loading
is used to trigger a loading state on the form which is used to show the loading state
on the managed submit button when a loadingElement
has been passed.
// With default value
false
loadingElement
is shown when loading
is true
and a submitButton
has been passed.
// With default value
null
// With custom value
<p>Loading...</p>
formErrors
is used to display extra errors that cannot be managed internally by the form builder.
// With default value
{}
// With custom value
{
first_name: '\'John\' is an invalid first name',
}
form
is used to pass the schema used to build the form. This will be explained in more detail here.
// With custom value
[
{
name: 'first_name',
},
]
A form schema can be passed into the form
prop. This is used to build the form and display the inputs correctly. The schema consists of an array and child elements. A child can be an array
or object
. If an array
is found then any objects
inside will be put on the same row as each other (or wrapped in the same parent container).
Each object
child needs at least a name
, but can also become more complex and custom:
name
(string)placeholder
(string)label
(string, function, string{})type
(string, defaultstring
)render
(function/node)options
(object[])radioContainerClass
(string)containerClass
(string)inputClass
(string)defaultValue
(mixed)defaultOptionText
(string)validationRules
(string, string[], object, object[], RegExp, RegExp[], function, function[])transformer
(function{})filter
(string, function, RegExp)autocomplete
(boolean)renderIf
(function)
name
is used for when data is submitted and passed back to the onSubmit
callback.
// With custom value
{
name: 'first_name',
}
placeholder
is used to add placeholder text on empty text inputs.
// With custom value
{
name: 'first_name',
placeholder: 'Enter a first name',
}
label
is used to add a label above the input.
If an object is passed then at least text
is required. className
is optional.
// With custom value
{
name: 'fist_name',
label: 'First Name',
}
{
name: 'first_name',
label: {
text: 'First Name',
className: 'first-name-label'
}
}
{
name: 'first_name',
label: props => (
<label {...props}>
First Name
</label>
)
}
type
is used to change what type of input is rendered. Valid types input:
- text
- custom
- password
- date
- month
- number
- range
- color
- search
- time
- url
- week
- textarea
- checkbox
- select
- radio
// With default value
{
name: 'first_name',
type: 'string',
}
If custom
is passed then it is recommended that render
is also set.
render
is used when a normal input is not enough. This can be used to completely customize an input.
When using a function it recieves the following parameters:
input
(current input object)value
(current input value)onChange
(onChange handler)onBlur
(onBlur handler)errors
(current input errors)state
(form state)
When using a node it recieves the following props:
name
(current input name)placeholder
(current input placeholder)onChange
(onChange handler)onBlur
(onBlur handler)invalid
(invalid boolean)
// With custom value
{
name: 'first_name',
render: () => (
<h1>Custom Render</h1>
),
}
{
name: 'first_name',
render: <h1>Custom Render</h1>,
}
It works great with community addons, such as form-select
.
// With form select
{
name: 'custom',
label: 'custom',
render: (
<Select
options={[
{ label: 'a', value: 'a' },
{ label: 'b', value: 'b' },
]}
/>
),
}
{
name: 'custom',
label: 'custom',
render: ({ name, placeholder }, value, onChange) => (
<Select
name={name}
placeholder={placeholder}
value={value}
onChange={onChange}
options={[
{ label: 'a', value: 'a' },
{ label: 'b', value: 'b' },
]}
/>
),
}
options
are used for type
s select
or radio
.
It should be an array of objects containing: text
and value
.
{
name: 'gender',
type: 'select',
options: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
{ text: 'Other', value: 'other' },
],
}
radioContainerClass
is appended to classPrefix
on the container when type
is radio
.
// With default value
'rdf-'
containerClass
is appended to classPrefix
on the container when type
is not radio
.
// With default value
'rdf-container'
inputClass
is appended to classPrefix
on the input.
// With default value
'rdf-input'
defaultValue
is inserted as the default value when the input has text, if type
is checkbox
then a truthy/falsey should be passed.
// With custom value
{
name: 'first_name',
defaultValue: 'John',
}
defaultOptionText
is used to select a placeholder option for when type
is select
.
// With custom value
{
name: 'gender',
defaultOptionText: 'Select a gender...',
}
validationRules
is used when wanting to validate the content of the input. Valid validation rules:
required
(not empty)email
(valid email)decimal
(numeric with optional decimal points)
// With custom value
{
name: 'first_name',
validationRules: 'required',
}
{
name: 'age',
validationRule: /^\d+$/,
}
{
name: 'age',
validationRule: '/^\d+$/',
}
{
name: 'color',
validationRule: this.isValidColor
}
{
name: 'email_address',
validationRules: [
'required', 'email',
],
}
Note: Validation rule types can also be mixed.
tranformer
is used to tranform the data before it gets validated/stored.
It should be an object with the keys which define functions:
onChange
onBlur
// With custom value
{
name: 'first_name',
transformer: {
onChange: this.uppercaseWords
}
}
filter
is used can specific characters should never get stored as a value. It
can be a string, function or RegExp. The character will be ignored if false
is returned from the filter.
There are some pre-defined filters:
numeric
decimal
// With pre-defined filter
{
name: 'currency',
filter: 'decimal'
}
// With function
{
name: 'currency',
filter: event => event.target.value === 'a'
}
// With RegExp
{
name: 'currency',
filter: /[A-Ba-b]/
}
autocomplete
can be used in an attempt to counter user agent autofill. By setting
autocomplete
to false
a random input name is generated, rather than using the name
of the input object.
// With custom value
{
name: 'username',
autocomplete: false
}
renderIf
can be used to render inputs based on the state of the form. When an input
is hidden from the form the validationErrors
and form value is reset.
// With custom value
{
name: 'last_name',
// Render if `first_name` has a truthy value
renderIf: ({ form }) => (
!!form.first_name
)
}
A list of community addons, designed to work flawlessly with form-builder
.
Check out schema.render
on how to format your component to work well with form-builder
. When creating your component be mindful of what props are passed.
When using a function it recieves the following parameters:
input
(current input object)value
(current input value)onChange
(onChange handler)onBlur
(onBlur handler)errors
(current input errors)state
(form state)
When using a node it recieves the following props:
name
(current input name)placeholder
(current input placeholder)onChange
(onChange handler)onBlur
(onBlur handler)invalid
(invalid boolean)