This library is a light abstraction over
React forms that allows for
customizable layouts for various types of data schemas. Serial Forms decouples
the input itself from the data it generates by creating a serialized object
that represents the input's value which may or may not be on the input itself.
This separation of concerns makes it easy to work with third party components
like Bootstrap and React
Widgets or your own custom
components. For example - you can create a complex component containing multiple
<input>
elements which produces one result in your serialized object, essentially
acting as one field.
Serial Forms aims to accomplish the following goals:
- Serialization is based on the name attribute for inputs so creating repeater fieldsets and nested repeater fieldsets is a snap.
- Must be easily extendable and work with any type of custom component (date pickers, bootstrap components, ect.). It should be a good foundation for other form frameworks and abstractions.
- Immutable internal data structures.
- Built with performance and compatibility in mind and well tested.
- Source code must be as good as any documentation.
This example demonstrates a very basic form with validation, a repeater field, and an undo button.
npm install react-serial-forms
var SerialForms = require('react-serial-forms');
import SerialForms from 'react-serial-forms';
or better, just get what you need:
import {
BasicForm,
InputField,
SelectField,
TextareaField
} from 'react-serial-forms'
Include one of the files (minified or non-minfied) in the dist/ directory of the npm installed module.
There are 3 fundamental aspects to Serial Forms:
- Validation - On field change check value for validity.
- Serialization - Serial Forms allows you to be expressive with creating forms
by giving you the ability to specify how the data is formatted by the naming
conventions of the
name
attribute. - Freedom. You should not be restricted by using premade columns or rows. Just build the form the way you want with whatever frontend technology you want.
Without extending Serial Forms, the fields are very basic.
- Input
<InputField />
- Textarea
<TextareaField />
- SelectField
<SelectField />
These components except all of the attributes the native (react) components
would, with the addition of a validation
and messages
attribute which
allows you to specify how the field should validate.
Vaidation is specified on the validation
attribute. Currently, the following
validators are included:
- required
- numeral
Multiple can be applied at one time by delimiting by a comma. Example:
<InputField validation='required' name='full_name' />
<InputField validation='required,email' name='email' type='email' />
<InputField validation='required,numeral' name='zip' type='number' />
<InputField validation='required,numeral' name='other-number' type='text' />
Supply an object to the special messages
attribute to customize the validation
message for any of the validators.
/**
* Format:
* <validator name>: <message>
*
* @type {object}
*/
let messages = {
required: 'I am a custom message for the required validator.'
}
<InputField validation='required' name='full_name' messages={messages} />
Creating a custom validator is simple. Validators are synchronous at the moment. They must be defined sometime before the component is mounted.
validation.registerValidator({
name: 'largerThanFive',
invalid: function(value) {
return value < 5;
},
message: 'The field should be larger than 5.'
});
<InputField type='number' validation='largerThanFive' name='full_name' />
Serialization is based on the naming convention. This allows for the ability to easily create complex data structures in components without much post-submit processing. Thus, saving you tons of time.
name="my-title"
={ "my-title": "<value>" }
name="fruits[]"
={ "fruits": ["<value>", "<value>", ...] }
name="fruits[0][name]"
={ "fruits": [{"name": "<value>"}] }
- And so on. You can nest arrays and objects infinitely.
You will more than likely want to obtain the serialized object onSubmit
.
onSubmit(e) {
let theform = this.refs.myform; // Grab from the refs.
theform.validate((valid) => { // You may want to force validation.
if (valid) {
console.log(theform.serialize()); // Save to a store or something.
}
});
}
Numbers will be real integer and float values when the type='number'
is
used on the Input field. Otherwise, they will be strings as the type suggests -
text.
<InputField type='file' />
Conveniently, the value for a file will be the actual files
object. This will
only be the case for newly added files of course. Otherwise, it will be whatever
the value attribute is set to. Likely, the name of the file on the record.
Empty values will always be null
.
Options should be specified with a collection of objects:
const choices = [
{ text: '- Select Something -', value: null },
{ text: 'Option 1', value: 'option_1' },
{ text: 'Option 2', value: 'option_2' }
];
<SelectField options={choices} />
If the select field has multiple={true}
, then the value will be an array.
Providing defaults
Simply pass the pre-populated value a value
attribute.
Extending this library is one of its main features. Please do.
Here is an example of the Date Picker being extended. Using the technique of having a "host" hidden input allows us to handle any type of component - whether it returns a SyntheticEvent or not.
import React from 'react';
import { FormBase } from 'react-serial-forms';
import { Grid, Row, ButtonInput, Col } from 'react-bootstrap';
export default class BootstrapForm extends FormBase {
constructor(props) {
super(props);
}
render() {
return (
<form onSubmit={this.props.onSubmit} method={this.props.method}>
<Grid>
{this.props.children}
<Row>
<Col xs={12}>
<ButtonInput type='submit' bsStyle='primary' value={this.props.submitText} />
</Col>
</Row>
</Grid>
</form>
);
}
}
import {
BasicForm,
InputField,
SelectField,
TextareaField
} from 'react-serial-forms'
const options = [
{ text: '- Select Something -', value: null },
{ text: 'Rollerblading', value: 'rollerblading' },
{ text: 'Fishing', value: 'fishing' },
{ text: 'Camping', value: 'camping' },
{ text: 'Reading', value: 'reading' }
];
const form = (
<BasicForm>
<InputField name='title' validation='required' />
<div className='simpleList'>
<InputField value='apple' name='fruits[]' />
<InputField value='orange' name='fruits[]' />
<InputField value='strawberry' name='fruits[]' />
<InputField value='grapefruit' name='fruits[]' />
</div>
<div className='simpleList-numbered'>
<InputField value='garlic' name='veges[0]' />
<InputField value='pepper' name='veges[3]' />
<InputField value='avocado' name='veges[2]' />
<InputField value='carrot' name='veges[1]' />
</div>
<div className='person'>
<InputField value='john' name='people[0][first_name]' validation='required' />
<InputField value='doe' name='people[0][last_name]' validation='required' />
<SelectField multiple={true} value={['camping', 'fishing']} name='people[0][hobbies]' options={options} validation='required' />
<div className='books'>
<div className='book'>
<InputField value='devil in the white city' name='people[0][books][0][title]' />
<InputField type='number' value='2003' name='people[0][books][0][year]' />
</div>
<div className='book'>
<InputField value='react' name='people[0][books][1][title]' />
<InputField type='number' value='2011' name='people[0][books][1][year]' />
</div>
</div>
</div>
<div className='person'>
<InputField value='crazy' name='people[1][first_name]' validation='required' />
<InputField value='tom' name='people[1][last_name]' validation='required' />
<SelectField multiple={true} value={['reading', 'rollerblading']} name='people[1][hobbies]' options={options} validation='required' />
<div className='books'>
<div className='book'>
<InputField value='1984' name='people[1][books][0][title]' />
<InputField type='number' value='1950' name='people[1][books][0][year]' />
</div>
<div className='book'>
<InputField value='dune' name='people[1][books][1][title]' />
<InputField type='number' value='1963' name='people[1][books][1][year]' />
</div>
</div>
</div>
</BasicForm>
);
Would automatically create a serialized object like this:
{
"title": "My Title",
"fruits": [
"apple",
"orange",
"strawberry",
"grapefruit"
],
"veges": [
"garlic",
"carrot",
"avocado",
"pepper"
],
"people": [
{
"first_name": "john",
"last_name": "doe",
"hobbies": [
"camping",
"fishing"
],
"books": [
{
"title": "devil in the white city",
"year": 2003
},
{
"title": "react",
"year": 2011
}
]
},
{
"first_name": "crazy",
"last_name": "tom",
"hobbies": [
"reading",
"rollerblading"
],
"books": [
{
"title": 1984,
"year": 1950
},
{
"title": "dune",
"year": 1963
}
]
}
]
}
npm install
npm test
- Make sure your IDE is eslint capable!