Skip to content

A high performance form library built specifically for React using persistent immutable data.

Notifications You must be signed in to change notification settings

luthv/react-serial-forms

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Serial Forms

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:

Example

Example 1

This example demonstrates a very basic form with validation, a repeater field, and an undo button.

Installation

npm install react-serial-forms

CommonJS

var SerialForms = require('react-serial-forms');

ES6

import SerialForms from 'react-serial-forms';

or better, just get what you need:

import {
  BasicForm,
  InputField,
  SelectField,
  TextareaField
} from 'react-serial-forms'

Browser

Include one of the files (minified or non-minfied) in the dist/ directory of the npm installed module.

Usage

There are 3 fundamental aspects to Serial Forms:

  1. Validation - On field change check value for validity.
  2. 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.
  3. 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.

Validation

Vaidation is specified on the validation attribute. Currently, the following validators are included:

  • required
  • numeral
  • email

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' />
Custom Validation Messages

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} />
Custom Validators

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

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

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.

Files

<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

Empty values will always be null.

Select Field

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

Extending this library is one of its main features. Please do.

Extend complex third-party components.

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.

An example of creating a custom Form with react-bootstrap.
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>
    );
  }
}

Let's make a 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
        }
      ]
    }
  ]
}

Development

  • npm install
  • npm test
  • Make sure your IDE is eslint capable!

About

A high performance form library built specifically for React using persistent immutable data.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%