https://github.com/gcanti/tcomb-form-native
  
  
    Forms library for react-native 
    https://github.com/gcanti/tcomb-form-native
  
        Last synced: 6 months ago 
        JSON representation
    
Forms library for react-native
- Host: GitHub
- URL: https://github.com/gcanti/tcomb-form-native
- Owner: gcanti
- License: mit
- Created: 2015-03-28T18:06:13.000Z (over 10 years ago)
- Default Branch: master
- Last Pushed: 2024-05-03T15:45:02.000Z (over 1 year ago)
- Last Synced: 2025-05-10T09:05:05.679Z (6 months ago)
- Language: JavaScript
- Homepage:
- Size: 825 KB
- Stars: 3,137
- Watchers: 63
- Forks: 458
- Open Issues: 125
- 
            Metadata Files:
            - Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
 
Awesome Lists containing this project
- awesome-react-native - tcomb-form-native ★2831 - Generate React Native forms (Components / Forms)
- awesome-react-native - tcomb-form-native ★2831 - Generate React Native forms (Components / Forms)
- react-native-awesome - tcomb-form-native
- awesome-react-native - tcomb-form-native - Generate React Native forms.  (Components / Forms)
- awesome-react-native - tcomb-form-native ★2831 - Generate React Native forms (Components / Forms)
- awesome-react-native-ui - tcomb-form-native ★1657 - Generate React Native forms (Components / Forms)
- awesome-react-native - tcomb-form-native ★2831 - Generate React Native forms (Components / Forms)
- awesome-react-native - tcomb-form-native
- ReactNativeMaterials - tcomb-form-native
README
          [](https://travis-ci.org/gcanti/tcomb-form-native)
[](https://david-dm.org/gcanti/tcomb-form-native)

# Notice
`tcomb-form-native` is looking for maintainers. If you're interested in helping, a great way to get started would just be to start weighing-in on [GitHub issues](https://github.com/gcanti/tcomb-form-native/issues), reviewing and testing some [PRs](https://github.com/gcanti/tcomb-form-native/pulls).
# Contents
- [Setup](#setup)
- [Supported react-native versions](#supported-react-native-versions)
- [Example](#example)
- [API](#api)
- [Types](#types)
- [Rendering options](#rendering-options)
- [Unions](#unions)
- [Lists](#lists)
- [Customizations](#customizations)
- [Tests](#tests)
- [License](#license)
# Setup
```
npm install tcomb-form-native
```
# Supported react-native versions
| Version | React Native Support | Android Support | iOS Support |
|---|---|---|---|
| 0.5 - 0.6.1 | 0.25.0 - 0.35.0 | 7.1 | 10.0.2 |
| 0.4 | 0.20.0 - 0.24.0 | 7.1 | 10.0.2 |
| 0.3 | 0.1.0 - 0.13.0  | 7.1 | 10.0.2 |
*Complies with [react-native-version-support-table](https://github.com/dangnelson/react-native-version-support-table)*
### Domain Driven Forms
The [tcomb library](https://github.com/gcanti/tcomb) provides a concise but expressive way to define domain models in JavaScript.
The [tcomb-validation library](https://github.com/gcanti/tcomb-validation) builds on tcomb, providing validation functions for tcomb domain models.
This library builds on those two and the awesome react-native.
### Benefits
With **tcomb-form-native** you simply call `` to generate a form based on that domain model. What does this get you?
1. Write a lot less code
2. Usability and accessibility for free (automatic labels, inline validation, etc)
3. No need to update forms when domain model changes
### JSON Schema support
JSON Schemas are also supported via the (tiny) [tcomb-json-schema library](https://github.com/gcanti/tcomb-json-schema).
**Note**. Please use tcomb-json-schema ^0.2.5.
### Pluggable look and feel
The look and feel is customizable via react-native stylesheets and *templates* (see documentation).
### Screencast
http://react.rocks/example/tcomb-form-native
### Example App
[https://github.com/bartonhammond/snowflake](https://github.com/bartonhammond/snowflake) React-Native, Tcomb, Redux, Parse.com, Jest - 88% coverage
# Example
```js
// index.ios.js
'use strict';
var React = require('react-native');
var t = require('tcomb-form-native');
var { AppRegistry, StyleSheet, Text, View, TouchableHighlight } = React;
var Form = t.form.Form;
// here we are: define your domain model
var Person = t.struct({
  name: t.String,              // a required string
  surname: t.maybe(t.String),  // an optional string
  age: t.Number,               // a required number
  rememberMe: t.Boolean        // a boolean
});
var options = {}; // optional rendering options (see documentation)
var AwesomeProject = React.createClass({
  onPress: function () {
    // call getValue() to get the values of the form
    var value = this.refs.form.getValue();
    if (value) { // if validation fails, value will be null
      console.log(value); // value here is an instance of Person
    }
  },
  render: function() {
    return (
      
        {/* display */}
        
        
          Save
        
      
    );
  }
});
var styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    marginTop: 50,
    padding: 20,
    backgroundColor: '#ffffff',
  },
  buttonText: {
    fontSize: 18,
    color: 'white',
    alignSelf: 'center'
  },
  button: {
    height: 36,
    backgroundColor: '#48BBEC',
    borderColor: '#48BBEC',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  }
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
```
**Output:**
(Labels are automatically generated)

**Ouput after a validation error:**

# API
## `getValue()`
Returns `null` if the validation failed, an instance of your model otherwise.
> **Note**. Calling `getValue` will cause the validation of all the fields of the form, including some side effects like highlighting the errors.
## `validate()`
Returns a `ValidationResult` (see [tcomb-validation](https://github.com/gcanti/tcomb-validation) for a reference documentation).
## Adding a default value and listen to changes
The `Form` component behaves like a [controlled component](https://facebook.github.io/react/docs/forms.html):
```js
var Person = t.struct({
  name: t.String,
  surname: t.maybe(t.String)
});
var AwesomeProject = React.createClass({
  getInitialState() {
    return {
      value: {
        name: 'Giulio',
        surname: 'Canti'
      }
    };
  },
  onChange(value) {
    this.setState({value});
  },
  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },
  render: function() {
    return (
      
        
        
          Save
        
      
    );
  }
});
```
The `onChange` handler has the following signature:
```
(raw: any, path: Array) => void
```
where
- `raw` contains the current raw value of the form (can be an invalid value for your model)
- `path` is the path to the field triggering the change
> **Warning**. tcomb-form-native uses `shouldComponentUpdate` aggressively. In order to ensure that tcomb-form-native detect any change to `type`, `options` or `value` props you have to change references:
## Disable a field based on another field's value
```js
var Type = t.struct({
  disable: t.Boolean, // if true, name field will be disabled
  name: t.String
});
// see the "Rendering options" section in this guide
var options = {
  fields: {
    name: {}
  }
};
var AwesomeProject = React.createClass({
  getInitialState() {
    return {
      options: options,
      value: null
    };
  },
  onChange(value) {
    // tcomb immutability helpers
    // https://github.com/gcanti/tcomb/blob/master/docs/API.md#updating-immutable-instances
    var options = t.update(this.state.options, {
      fields: {
        name: {
          editable: {'$set': !value.disable}
        }
      }
    });
    this.setState({options: options, value: value});
  },
  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },
  render: function() {
    return (
      
        
        
          Save
        
      
    );
  }
});
```
## How to get access to a field
You can get access to a field with the `getComponent(path)` API:
```js
var Person = t.struct({
  name: t.String,
  surname: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean
});
var AwesomeProject = React.createClass({
  componentDidMount() {
    // give focus to the name textbox
    this.refs.form.getComponent('name').refs.input.focus();
  },
  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },
  render: function() {
    return (
      
        
        
          Save
        
      
    );
  }
});
```
## How to clear form after submit
```js
var Person = t.struct({
  name: t.String,
  surname: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean
});
var AwesomeProject = React.createClass({
  getInitialState() {
    return { value: null };
  },
  onChange(value) {
    this.setState({ value });
  },
  clearForm() {
    // clear content from all textbox
    this.setState({ value: null });
  },
  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
      // clear all fields after submit
      this.clearForm();
    }
  },
  render: function() {
    return (
      
        
        
          Save
        
      
    );
  }
});
```
## Dynamic forms example: how to change a form based on selection
Say I have an iOS Picker, depending on which option is selected in this picker I want the next component to either be a checkbox or a textbox:
```js
const Country = t.enums({
  'IT': 'Italy',
  'US': 'United States'
}, 'Country');
var AwesomeProject = React.createClass({
  // returns the suitable type based on the form value
  getType(value) {
    if (value.country === 'IT') {
      return t.struct({
        country: Country,
        rememberMe: t.Boolean
      });
    } else if (value.country === 'US') {
      return t.struct({
        country: Country,
        name: t.String
      });
    } else {
      return t.struct({
        country: Country
      });
    }
  },
  getInitialState() {
    const value = {};
    return { value, type: this.getType(value) };
  },
  onChange(value) {
    // recalculate the type only if strictly necessary
    const type = value.country !== this.state.value.country ?
      this.getType(value) :
      this.state.type;
    this.setState({ value, type });
  },
  onPress() {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },
  render() {
    return (
      
        
        
          Save
        
      
    );
  }
});
```
# Types
### Required field
By default fields are required:
```js
var Person = t.struct({
  name: t.String,    // a required string
  surname: t.String  // a required string
});
```
### Optional field
In order to create an optional field, wrap the field type with the `t.maybe` combinator:
```js
var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String) // an optional string
});
```
The postfix `" (optional)"` is automatically added to optional fields.
You can customise the postfix value or setting a postfix for required fields:
```js
t.form.Form.i18n = {
  optional: '',
  required: ' (required)' // inverting the behaviour: adding a postfix to the required fields
};
```
### Numbers
In order to create a numeric field, use the `t.Number` type:
```js
var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number // a numeric field
});
```
tcomb-form-native will convert automatically numbers to / from strings.
### Booleans
In order to create a boolean field, use the `t.Boolean` type:
```js
var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean // a boolean field
});
```
Booleans are displayed as `SwitchIOS`s.
### Dates
In order to create a date field, use the `t.Date` type:
```js
var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number,
  birthDate: t.Date // a date field
});
```
Dates are displayed as `DatePickerIOS`s under iOS and `DatePickerAndroid` or `TimePickerAndroid` under Android, depending on the `mode` selected (`date` or `time`).
Under Android, use the `fields` option to configure which `mode` to display the Picker:
```js
// see the "Rendering options" section in this guide
var options = {
  fields: {
    birthDate: {
      mode: 'date' // display the Date field as a DatePickerAndroid
    }
  }
};
```
#### iOS date `config` option
The bundled template will render an iOS `UIDatePicker` component, but collapsed into a touchable component in order to improve usability. A `config` object can be passed to customize it with the following parameters:
| Key | Value |
|-----|-------|
| `animation` | The animation to collapse the date picker. Defaults to `Animated.timing`. |
| `animationConfig` | The animation configuration object. Defaults to `{duration: 200}` for the default animation. |
| `format` | A `(date) => String(date)` kind of function to provide a custom date format parsing to display the value. Optional, defaults to `(date) => String(date)`.
| `defaultValueText` | An `string` to customize the default value of the `null` date value text. |
For the collapsible customization, look at the `dateTouchable` and `dateValue` keys in the stylesheet file.
#### Android date `config` option
When using a `t.Date` type in Android, it can be configured through a `config` option that take the following parameters:
| Key | Value |
|-----|-------|
| ``background`` | Determines the type of background drawable that's going to be used to display feedback. Optional, defaults to ``TouchableNativeFeedback.SelectableBackground``. |
| ``format`` | A ``(date) => String(date)`` kind of function to provide a custom date format parsing to display the value. Optional, defaults to ``(date) => String(date)``.
| ``dialogMode`` | Determines the type of datepicker mode for Android (`default`, `spinner` or `calendar`). |
| `defaultValueText` | An `string` to customize the default value of the `null` date value text. |
### Enums
In order to create an enum field, use the `t.enums` combinator:
```js
var Gender = t.enums({
  M: 'Male',
  F: 'Female'
});
var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: t.Number,
  rememberMe: t.Boolean,
  gender: Gender // enum
});
```
Enums are displayed as `Picker`s.
#### iOS select `config` option
The bundled template will render an iOS `UIPickerView` component, but collapsed into a touchable component in order to improve usability. A `config` object can be passed to customize it with the following parameters:
| Key | Value |
|-----|-------|
| `animation` | The animation to collapse the date picker. Defaults to `Animated.timing`. |
| `animationConfig` | The animation configuration object. Defaults to `{duration: 200}` for the default animation. |
For the collapsible customization, look at the `pickerContainer`, `pickerTouchable` and `pickerValue` keys in the stylesheet file.
### Refinements
A *predicate* is a function with the following signature:
```
(x: any) => boolean
```
You can refine a type with the `t.refinement(type, predicate)` combinator:
```js
// a type representing positive numbers
var Positive = t.refinement(t.Number, function (n) {
  return n >= 0;
});
var Person = t.struct({
  name: t.String,
  surname: t.String,
  email: t.maybe(t.String),
  age: Positive, // refinement
  rememberMe: t.Boolean,
  gender: Gender
});
```
Subtypes allow you to express any custom validation with a simple predicate.
# Rendering options
In order to customize the look and feel, use an `options` prop:
```js
```
## Form component
### Labels and placeholders
By default labels are automatically generated. You can turn off this behaviour or override the default labels
on field basis.
```js
var options = {
  label: 'My struct label' // <= form legend, displayed before the fields
};
var options = {
  fields: {
    name: {
      label: 'My name label' // <= label for the name field
    }
  }
};
```
In order to automatically generate default placeholders, use the option `auto: 'placeholders'`:
```js
var options = {
  auto: 'placeholders'
};
```

Set `auto: 'none'` if you don't want neither labels nor placeholders.
```js
var options = {
  auto: 'none'
};
```
### Fields order
You can sort the fields with the `order` option:
```js
var options = {
  order: ['name', 'surname', 'rememberMe', 'gender', 'age', 'email']
};
```
### Default values
You can set the default values passing a `value` prop to the `Form` component:
```js
var value = {
  name: 'Giulio',
  surname: 'Canti',
  age: 41,
  gender: 'M'
};
```
### Fields configuration
You can configure each field with the `fields` option:
```js
var options = {
  fields: {
    name: {
      // name field configuration here..
    },
    surname: {
      // surname field configuration here..
    }
  }
};
```
## Textbox component
Implementation: `TextInput`
**Tech note.** Values containing only white spaces are converted to `null`.
### Placeholder
You can set the placeholder with the `placeholder` option:
```js
var options = {
  fields: {
    name: {
      placeholder: 'Your placeholder here'
    }
  }
};
```
### Label
You can set the label with the `label` option:
```js
var options = {
  fields: {
    name: {
      label: 'Insert your name'
    }
  }
};
```
### Help message
You can set a help message with the `help` option:
```js
var options = {
  fields: {
    name: {
      help: 'Your help message here'
    }
  }
};
```

### Error messages
You can add a custom error message with the `error` option:
```js
var options = {
  fields: {
    email: {
      // you can use strings or JSX
      error: 'Insert a valid email'
    }
  }
};
```

tcomb-form-native will display the error message when the field validation fails.
`error` can also be a function with the following signature:
```
(value, path, context) => ?(string | ReactElement)
```
where
- `value` is an object containing the current form value.
- `path` is the path of the value being validated
- `context` is the value of the `context` prop. Also it contains a reference to the component options.
The value returned by the function will be used as error message.
If you want to show the error message onload, add the `hasError` option:
```js
var options = {
  hasError: true,
  error: A custom error message
};
```
Another way is to add a:
```
getValidationErrorMessage(value, path, context)
```
static function to a type, where:
- `value` is the (parsed) current value of the component.
- `path` is the path of the value being validated
- `context` is the value of the `context` prop. Also it contains a reference to the component options.
```js
var Age = t.refinement(t.Number, function (n) { return n >= 18; });
// if you define a getValidationErrorMessage function, it will be called on validation errors
Age.getValidationErrorMessage = function (value, path, context) {
  return 'bad age, locale: ' + context.locale;
};
var Schema = t.struct({
  age: Age
});
...
```
You can even define `getValidationErrorMessage` on the supertype in order to be DRY:
```js
t.Number.getValidationErrorMessage = function (value, path, context) {
  return 'bad number';
};
Age.getValidationErrorMessage = function (value, path, context) {
  return 'bad age, locale: ' + context.locale;
};
```
### Other standard options
The following standard options are available (see http://facebook.github.io/react-native/docs/textinput.html):
- `allowFontScaling`
- `autoCapitalize`
- `autoCorrect`
- `autoFocus`
- `bufferDelay`
- `clearButtonMode`
- `editable`
- `enablesReturnKeyAutomatically`
- `keyboardType`
- `maxLength`
- `multiline`
- `numberOfLines`
- `onBlur`
- `onEndEditing`
- `onFocus`
- `onSubmitEditing`
- `onContentSizeChange`
- `password`
- `placeholderTextColor`
- `returnKeyType`
- `selectTextOnFocus`
- `secureTextEntry`
- `selectionState`
- `textAlign`
- `textAlignVertical`
- `textContentType`
- ~~`underlineColorAndroid`~~
`underlineColorAndroid` is not supported now on `tcomb-form-native` due to random crashes on Android, especially on ScrollView. See more on:
https://github.com/facebook/react-native/issues/17530#issuecomment-416367184
## Checkbox component
Implementation: `SwitchIOS`
The following options are similar to the `Textbox` component's ones:
- `label`
- `help`
- `error`
### Other standard options
The following standard options are available (see http://facebook.github.io/react-native/docs/switchios.html):
- `disabled`
- `onTintColor`
- `thumbTintColor`
- `tintColor`
## Select component
Implementation: `PickerIOS`
The following options are similar to the `Textbox` component's ones:
- `label`
- `help`
- `error`
### `nullOption` option
You can customize the null option with the `nullOption` option:
```js
var options = {
  fields: {
    gender: {
      nullOption: {value: '', text: 'Choose your gender'}
    }
  }
};
```
You can remove the null option setting the `nullOption` option to `false`.
**Warning**: when you set `nullOption = false` you must also set the Form's `value` prop for the select field.
**Tech note.** A value equal to `nullOption.value` (default `''`) is converted to `null`.
### Options order
You can sort the options with the `order` option:
```js
var options = {
  fields: {
    gender: {
      order: 'asc' // or 'desc'
    }
  }
};
```
### Options isCollapsed
You can determinate if Select is collapsed:
```js
var options = {
  fields: {
    gender: {
      isCollapsed: false // default: true
    }
  }
};
```
If option not set, default is `true`
### Options onCollapseChange
You can set a callback, triggered, when collapse change:
```js
var options = {
  fields: {
    gender: {
      onCollapseChange: () => { console.log('collapse changed'); }
    }
  }
};
```
## DatePicker component
Implementation: `DatePickerIOS`
### Example
```js
var Person = t.struct({
  name: t.String,
  birthDate: t.Date
});
```
The following options are similar to the `Textbox` component's ones:
- `label`
- `help`
- `error`
### Other standard options
The following standard options are available (see http://facebook.github.io/react-native/docs/datepickerios.html):
- `maximumDate`,
- `minimumDate`,
- `minuteInterval`,
- `mode`,
- `timeZoneOffsetInMinutes`
## Hidden Component
For any component, you can set the field with the `hidden` option:
```js
var options = {
  fields: {
    name: {
      hidden: true
    }
  }
};
```
This will completely skip the rendering of the component, while the default value will be available for validation purposes.
# Unions
**Code Example**
```js
const AccountType = t.enums.of([
  'type 1',
  'type 2',
  'other'
], 'AccountType')
const KnownAccount = t.struct({
  type: AccountType
}, 'KnownAccount')
// UnknownAccount extends KnownAccount so it owns also the type field
const UnknownAccount = KnownAccount.extend({
  label: t.String,
}, 'UnknownAccount')
// the union
const Account = t.union([KnownAccount, UnknownAccount], 'Account')
// the final form type
const Type = t.list(Account)
const options = {
  item: [ // one options object for each concrete type of the union
    {
      label: 'KnownAccount'
    },
    {
      label: 'UnknownAccount'
    }
  ]
}
```
Generally `tcomb`'s unions require a `dispatch` implementation in order to select the suitable type constructor for a given value and this would be the key in this use case:
```js
// if account type is 'other' return the UnknownAccount type
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount
```
# Lists
You can handle a list with the `t.list` combinator:
```js
const Person = t.struct({
  name: t.String,
  tags: t.list(t.String) // a list of strings
});
```
## Items configuration
To configure all the items in a list, set the `item` option:
```js
const Person = t.struct({
  name: t.String,
  tags: t.list(t.String) // a list of strings
});
const options = {
  fields: { // <= Person options
    tags: {
      item: { // <= options applied to each item in the list
        label: 'My tag'
      }
    }
  }
});
```
## Nested structures
You can nest lists and structs at an arbitrary level:
```js
const Person = t.struct({
  name: t.String,
  surname: t.String
});
const Persons = t.list(Person);
```
If you want to provide options for your nested structures they must be nested
following the type structure. Here is an example:
```js
const Person = t.struct({
  name: t.Struct,
  position: t.Struct({
    latitude: t.Number,
    longitude: t.Number
  });
});
const options = {
  fields: { // <= Person options
    name: {
        label: 'name label'
    }
    position: {
        fields: {
            // Note that latitude is not directly nested in position,
            // but in the fields property
            latitude: {
                label: 'My position label'
            }
        }
    }
  }
});
```
When dealing with `t.list`, make sure to declare the `fields` property inside the `item` property, as such:
```js
const Documents = t.struct({
  type: t.Number,
  value: t.String
})
const Person = t.struct({
  name: t.Struct,
  documents: t.list(Documents)
});
const options = {
  fields: {
    name: { /*...*/ },
    documents: {
      item: {
        fields: {
          type: {
            // Documents t.struct 'type' options
          },
          value: {
            // Documents t.struct 'value' options
          }
        }
      }
    }
  }
}
```
## Internationalization
You can override the default language (english) with the `i18n` option:
```js
const options = {
  i18n: {
    optional: ' (optional)',
    required: '',
    add: 'Add',   // add button
    remove: '✘',  // remove button
    up: '↑',      // move up button
    down: '↓'     // move down button
  }
};
```
## Buttons configuration
You can prevent operations on lists with the following options:
- `disableAdd`: (default `false`) prevents adding new items
- `disableRemove`: (default `false`) prevents removing existing items
- `disableOrder`: (default `false`) prevents sorting existing items
```js
const options = {
  disableOrder: true
};
```
## List with Dynamic Items (Different structs based on selected value)
Lists of different types are not supported. This is because a `tcomb`'s list, by definition, contains only values of the same type. You can define a union though:
```js
const AccountType = t.enums.of([
  'type 1',
  'type 2',
  'other'
], 'AccountType')
const KnownAccount = t.struct({
  type: AccountType
}, 'KnownAccount')
// UnknownAccount extends KnownAccount so it owns also the type field
const UnknownAccount = KnownAccount.extend({
  label: t.String,
}, 'UnknownAccount')
// the union
const Account = t.union([KnownAccount, UnknownAccount], 'Account')
// the final form type
const Type = t.list(Account)
```
Generally `tcomb`'s unions require a `dispatch` implementation in order to select the suitable type constructor for a given value and this would be the key in this use case:
```js
// if account type is 'other' return the UnknownAccount type
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount
```
# Customizations
## Stylesheets
See also [Stylesheet guide](docs/STYLESHEETS.md).
tcomb-form-native comes with a default style. You can customize the look and feel by setting another stylesheet:
```js
var t = require('tcomb-form-native/lib');
var i18n = require('tcomb-form-native/lib/i18n/en');
var templates = require('tcomb-form-native/lib/templates/bootstrap');
// define a stylesheet (see lib/stylesheets/bootstrap for an example)
var stylesheet = {...};
// override globally the default stylesheet
t.form.Form.stylesheet = stylesheet;
// set defaults
t.form.Form.templates = templates;
t.form.Form.i18n = i18n;
```
You can also override the stylesheet locally for selected fields:
```js
var Person = t.struct({
  name: t.String
});
var options = {
  fields: {
    name: {
      stylesheet: myCustomStylesheet
    }
  }
};
```
Or per form:
```js
var Person = t.struct({
  name: t.String
});
var options = {
  stylesheet: myCustomStylesheet
};
```
For a complete example see the default stylesheet https://github.com/gcanti/tcomb-form-native/blob/master/lib/stylesheets/bootstrap.js.
## Templates
tcomb-form-native comes with a default layout, i.e. a bunch of templates, one for each component.
When changing the stylesheet is not enough, you can customize the layout by setting custom templates:
```js
var t = require('tcomb-form-native/lib');
var i18n = require('tcomb-form-native/lib/i18n/en');
var stylesheet = require('tcomb-form-native/lib/stylesheets/bootstrap');
// define the templates (see lib/templates/bootstrap for an example)
var templates = {...};
// override globally the default layout
t.form.Form.templates = templates;
// set defaults
t.form.Form.stylesheet = stylesheet;
t.form.Form.i18n = i18n;
```
You can also override the template locally:
```js
var Person = t.struct({
  name: t.String
});
function myCustomTemplate(locals) {
  var containerStyle = {...};
  var labelStyle = {...};
  var textboxStyle = {...};
  return (
    
      {locals.label}
      
    
  );
}
var options = {
  fields: {
    name: {
      template: myCustomTemplate
    }
  }
};
```
A template is a function with the following signature:
```
(locals: Object) => ReactElement
```
where `locals` is an object contaning the "recipe" for rendering the input and it's built for you by tcomb-form-native.
Let's see an example: the `locals` object passed in the `checkbox` template:
```js
type Message = string | ReactElement
{
  stylesheet: Object, // the styles to be applied
  hasError: boolean,  // true if there is a validation error
  error: ?Message,    // the optional error message to be displayed
  label: Message,     // the label to be displayed
  help: ?Message,     // the optional help message to be displayed
  value: boolean,     // the current value of the checkbox
  onChange: Function, // the event handler to be called when the value changes
  config: Object,     // an optional object to pass configuration options to the new template
  ...other input options here...
}
```
For a complete example see the default template https://github.com/gcanti/tcomb-form-native/blob/master/lib/templates/bootstrap.
## i18n
tcomb-form-native comes with a default internationalization (English). You can change it by setting another i18n object:
```js
var t = require('tcomb-form-native/lib');
var templates = require('tcomb-form-native/lib/templates/bootstrap');
// define an object containing your translations (see tcomb-form-native/lib/i18n/en for an example)
var i18n = {...};
// override globally the default i18n
t.form.Form.i18n = i18n;
// set defaults
t.form.Form.templates = templates;
t.form.Form.stylesheet = stylesheet;
```
## Transformers
Say you want a search textbox which accepts a list of keywords separated by spaces:
```js
var Search = t.struct({
  search: t.list(t.String)
});
```
tcomb-form by default will render the `search` field as a list. In order to render a textbox you have to override the default behaviour with the factory option:
```js
var options = {
  fields: {
    search: {
      factory: t.form.Textbox
    }
  }
};
```
There is a problem though: a textbox handle only strings so we need a way to transform a list in a string and a string in a list: a `Transformer` deals with serialization / deserialization of data and has the following interface:
```js
var Transformer = t.struct({
  format: t.Function, // from value to string, it must be idempotent
  parse: t.Function   // from string to value
});
```
A basic transformer implementation for the search textbox:
```js
var listTransformer = {
  format: function (value) {
    return Array.isArray(value) ? value.join(' ') : value;
  },
  parse: function (str) {
    return str ? str.split(' ') : [];
  }
};
```
Now you can handle lists using the transformer option:
```js
// example of initial value
var value = {
  search: ['climbing', 'yosemite']
};
var options = {
  fields: {
    search: {
      factory: t.form.Textbox, // tell tcomb-react-native to use the same component for textboxes
      transformer: listTransformer,
      help: 'Keywords are separated by spaces'
    }
  }
};
```
## Custom factories
You can pack together style, template (and transformers) in a custom component and then you can use it with the `factory` option:
```js
var Component = t.form.Component;
// extend the base Component
class MyComponent extends Component {
  // this is the only required method to implement
  getTemplate() {
    // define here your custom template
    return function (locals) {
      //return ... jsx ...
    };
  }
  // you can optionally override the default getLocals method
  // it will provide the locals param to your template
  getLocals() {
    // in locals you'll find the default locals:
    // - path
    // - error
    // - hasError
    // - label
    // - onChange
    // - stylesheet
    var locals = super.getLocals();
    // add here your custom locals
    return locals;
  }
}
// as example of transformer: this is the default transformer for textboxes
MyComponent.transformer = {
  format: value => Nil.is(value) ? null : value,
  parse: value => (t.String.is(value) && value.trim() === '') || Nil.is(value) ? null : value
};
var Person = t.struct({
  name: t.String
});
var options = {
  fields: {
    name: {
      factory: MyComponent
    }
  }
};
```
# Tests
```
npm test
```
**Note:** If you are using Jest, you will encounter an error which can
be fixed w/ a small change to the ```package.json```.
The error will look similiar to the following:
```
Error: Cannot find module './datepicker' from 'index.js' at
Resolver.resolveModule
```
A completely working example ```jest``` setup is shown below w/ the
[http://facebook.github.io/jest/docs/api.html#modulefileextensions-array-string](http://facebook.github.io/jest/docs/api.html#modulefileextensions-array-string)
fix added:
```
  "jest": {
    "setupEnvScriptFile": "./node_modules/react-native/jestSupport/env.js",
    "haste": {
      "defaultPlatform": "ios",
      "platforms": [
        "ios",
        "android"
      ],
      "providesModuleNodeModules": [
        "react-native"
      ]
    },
    "testPathIgnorePatterns": [
      "/node_modules/"
    ],
    "testFileExtensions": [
      "es6",
      "js"
    ],
    "moduleFileExtensions": [
      "js",
      "json",
      "es6",
      "ios.js"    <<<<<<<<<<<< this needs to be defined!
    ],
    "unmockedModulePathPatterns": [
      "react",
      "react-addons-test-utils",
      "react-native-router-flux",
      "promise",
      "source-map",
      "key-mirror",
      "immutable",
      "fetch",
      "redux",
      "redux-thunk",
      "fbjs"
    ],
    "collectCoverage": false,
    "verbose": true
    },
```
# License
[MIT](LICENSE)