gcanti / tcomb-form

Forms library for react
https://gcanti.github.io/tcomb-form
MIT License
1.16k stars 136 forks source link

How to do child forms? #289

Open dukedougal opened 8 years ago

dukedougal commented 8 years ago

I am creating a form to build up a data structure.

I need to be able to add an array of fields.

Like the "children" in this example:

http://prometheusresearch.github.io/react-forms/examples/family-editor.html

Can I do this in tcomb-form?

gcanti commented 8 years ago

Hi @dukedougal,

Porting the family example is a valuable exercise, so I'll try to do it in the form of a little tutorial on tcomb-form.

While in react-forms you configure the schema and the layout toghether, in tcomb-form you configure them in separate props:

import t from 'tcomb-form'

<t.form.Form type="..." options="..." />

Props

Implementation

Schema

// SexField is an enum
const Gender = t.enums({
  male: 'Male',
  female: 'Female'
}, 'Gender')

// NameField is a refinement of a string
const Name = t.refinement(t.String, (s) => /^[a-z\s]+$/i.test(s))

const Adult = t.struct({
  name: Name, // a required name
  dateOfBirth: t.maybe(t.Date) // an optional date of birth. Note the t.maybe() combinator
}, 'Adult')

// Child has the same fields as Adult plus a gender
const Child = Adult.extend({
  gender: Gender // a required gender
}, 'Child')

const Family = t.struct({
  mother: Adult, // a required Adult
  father: Adult, // a required Adult
  children: t.list(Child) // a list of Child. Note the t.list() combinator
}, 'Family')

Layout, labels and hints

// DRY
const nameOptions = {
  help: 'Should contain only alphanumeric characters'
}

const options = {  // options of Family
  fields: {
    father: {  // options of the father field
      fields: {
        name: nameOptions
      }
    },
    mother: {  // options of the mother field
      fields: {
        name: nameOptions
      }
    },
    children: {  // options of the children field
      item: { // options of each item in the chidlren list
        fields: {
          name: nameOptions
        }
      }
    }
  }
}

Result

const App = React.createClass({

  onSubmit(evt) {
    evt.preventDefault()
    const value = this.refs.form.getValue()
    if (value) {
      console.log(value)
    }
  },

  render() {
    return (
      <form onSubmit={this.onSubmit}>
        <t.form.Form
          ref="form"
          type={Family}
          options={options}
        />
        <div className="form-group">
          <button type="submit" className="btn btn-primary">Save</button>
        </div>
      </form>
    )
  }

})
form