Building dynamic forms with Facebook React

In this post, I will demonstrate how to use Facebook React to build a simple form. It leverages the
wingspan-forms library.

wingspan-forms is a a dynamic form library for Facebook React, providing abstractions for building
dynamic forms and controlled grids. Widgets are provided by Telerik's KendoUI. wingspan-forms is
about half "Kendo-React adapter", and half general functional-programming friendly form abstractions that aren't
coupled to the underlying widget implementation. The higher level of abstraction is the important bit - this
level of abstraction isn't possible in "just Kendo" or any other OOP style widget library.

Almost all form fields need at least a label, so lets wrap our KendoText in a FormField. FormField takes an
isValid prop which sets some css for the invalid-style and the invalid-tooltip, as well as a
fieldInfo prop for various optional metadata, like a label.

<FormFieldfieldInfo={{"label":"First name"}}isValid={[false,'This field is invalid']}><KendoTextvalue={this.state.firstName}onChange={this.onFirstNameChange}/></FormField>

We know enough now to also use KendoDate, KendoNumber and KendoComboBox:

varMyForm=React.createClass({getInitialState:function(){return{firstName:'',lastName:'',gender:'',// due to a `wingspan-forms` bug, this has to be a string. Fix coming soon!age:null,birthday:null};},render:function(){return(<divclassName="MyForm"><div><FormFieldfieldInfo={{"label":"First name"}}isValid={[true,'']}><KendoTextvalue={this.state.firstName}onChange={this.onFirstNameChange}/></FormField><FormFieldfieldInfo={{"label":"Last name"}}isValid={[true,'']}><KendoTextvalue={this.state.lastName}onChange={this.onLastNameChange}/></FormField><FormFieldfieldInfo={{"label":"Gender"}}isValid={[true,'']}><KendoComboBoxdataSource={[{value:'male',label:'Male'},{value:'female',label:'Female'}]}displayField="label"valueField="value"value={this.state.gender}onChange={this.onGenderChange}/></FormField><FormFieldfieldInfo={{"label":"Age"}}isValid={[true,'']}><KendoNumbervalue={this.state.age}onChange={this.onAgeChange}/></FormField><FormFieldfieldInfo={{"label":"Birthday"}}isValid={[true,'']}><KendoDatevalue={this.state.birthday}onChange={this.onBirthdayChange}/></FormField></div><pre>{JSON.stringify(this.state,undefined,2)}</pre></div>);},onFirstNameChange:function(value){this.setState({firstName:value});},onLastNameChange:function(value){this.setState({lastName:value});},onAgeChange:function(value){this.setState({age:value});},onBirthdayChange:function(value){this.setState({birthday:value});},onGenderChange:function(value){this.setState({gender:value});}});

React doesn't yet have a way to use React components that are namespaced, so the following JSX does not work:

<WingspanForms.KendoComboBox ... />

Until the React folks decide on a mechanism to allow this, we have to alias the components into the local scope. We can
do this out of the way, at the bottom of the file, due to JavaScript's var hoisting.

Let's use our functional programming chops to abstract even further, and make all the fields use the same
onChange callback. Note the 'gender' fieldInfo got a little more complicated, since
KendoComboBox requires configuration.

render:function(){varcontrols=_.map(fieldInfos,function(fieldInfo){return(<AutoFieldfieldInfo={fieldInfo}value={this.state[fieldInfo.name]}onChange={_.partial(this.onFieldChange,fieldInfo.name)}isValid={this.isFieldValid(fieldInfo.name)}/>);}.bind(this));return(<divclassName="MyForm"><div>{controls}</div><pre>{JSON.stringify(this.state,undefined,2)}</pre></div>);},isFieldValid:function(fieldName){varval=this.state[fieldName];returnval!==null&&val!==''?[true,'']:[false,'This field is required.'];}