Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gcanti/tcomb-form-native
Forms library for react-native
https://github.com/gcanti/tcomb-form-native
Last synced: 4 days 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 (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2024-05-03T15:45:02.000Z (9 months ago)
- Last Synced: 2024-10-29T15:05:13.322Z (3 months ago)
- Language: JavaScript
- Homepage:
- Size: 825 KB
- Stars: 3,147
- Watchers: 64
- 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. ![](https://img.shields.io/github/stars/gcanti/tcomb-form-native.svg?style=social&label=Star) (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
[![build status](https://img.shields.io/travis/gcanti/tcomb-form-native/master.svg?style=flat-square)](https://travis-ci.org/gcanti/tcomb-form-native)
[![dependency status](https://img.shields.io/david/gcanti/tcomb-form-native.svg?style=flat-square)](https://david-dm.org/gcanti/tcomb-form-native)
![npm downloads](https://img.shields.io/npm/dm/tcomb-form-native.svg)# 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)
![Result](docs/images/result.png)
**Ouput after a validation error:**
![Result after a validation error](docs/images/validation.png)
# 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'
};```
![Placeholders](docs/images/placeholders.png)
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'
}
}
};
```![Help](docs/images/help.png)
### 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'
}
}
};
```![Help](docs/images/error.png)
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)