{"id":4489,"url":"https://github.com/gcanti/tcomb-form-native","last_synced_at":"2025-05-14T00:09:22.535Z","repository":{"id":29506187,"uuid":"33044331","full_name":"gcanti/tcomb-form-native","owner":"gcanti","description":"Forms library for react-native","archived":false,"fork":false,"pushed_at":"2024-05-03T15:45:02.000Z","size":845,"stargazers_count":3137,"open_issues_count":125,"forks_count":458,"subscribers_count":63,"default_branch":"master","last_synced_at":"2025-05-10T09:05:05.679Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gcanti.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-28T18:06:13.000Z","updated_at":"2025-03-20T12:51:05.000Z","dependencies_parsed_at":"2024-11-15T13:57:10.277Z","dependency_job_id":"d12beee1-971b-4417-abff-cc456304cfed","html_url":"https://github.com/gcanti/tcomb-form-native","commit_stats":{"total_commits":197,"total_committers":47,"mean_commits":4.191489361702128,"dds":0.4314720812182741,"last_synced_commit":"cb70ddf0a2a900094fdf3809ae6dd0afa84b719f"},"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Ftcomb-form-native","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Ftcomb-form-native/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Ftcomb-form-native/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Ftcomb-form-native/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gcanti","download_url":"https://codeload.github.com/gcanti/tcomb-form-native/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254044137,"owners_count":22005085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:17:14.150Z","updated_at":"2025-05-14T00:09:17.528Z","avatar_url":"https://github.com/gcanti.png","language":"JavaScript","funding_links":[],"categories":["Components","JavaScript","Libraries","框架"],"sub_categories":["Forms","Other Platforms","TabLayout","项目实践\u0026教程","综合"],"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)\n[![dependency status](https://img.shields.io/david/gcanti/tcomb-form-native.svg?style=flat-square)](https://david-dm.org/gcanti/tcomb-form-native)\n![npm downloads](https://img.shields.io/npm/dm/tcomb-form-native.svg)\n\n# Notice\n\n`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).\n\n# Contents\n\n- [Setup](#setup)\n- [Supported react-native versions](#supported-react-native-versions)\n- [Example](#example)\n- [API](#api)\n- [Types](#types)\n- [Rendering options](#rendering-options)\n- [Unions](#unions)\n- [Lists](#lists)\n- [Customizations](#customizations)\n- [Tests](#tests)\n- [License](#license)\n\n# Setup\n\n```\nnpm install tcomb-form-native\n```\n\n# Supported react-native versions\n\n| Version | React Native Support | Android Support | iOS Support |\n|---|---|---|---|\n| 0.5 - 0.6.1 | 0.25.0 - 0.35.0 | 7.1 | 10.0.2 |\n| 0.4 | 0.20.0 - 0.24.0 | 7.1 | 10.0.2 |\n| 0.3 | 0.1.0 - 0.13.0  | 7.1 | 10.0.2 |\n*Complies with [react-native-version-support-table](https://github.com/dangnelson/react-native-version-support-table)*\n\n### Domain Driven Forms\n\nThe [tcomb library](https://github.com/gcanti/tcomb) provides a concise but expressive way to define domain models in JavaScript.\n\nThe [tcomb-validation library](https://github.com/gcanti/tcomb-validation) builds on tcomb, providing validation functions for tcomb domain models.\n\nThis library builds on those two and the awesome react-native.\n\n### Benefits\n\nWith **tcomb-form-native** you simply call `\u003cForm type={Model} /\u003e` to generate a form based on that domain model. What does this get you?\n\n1. Write a lot less code\n2. Usability and accessibility for free (automatic labels, inline validation, etc)\n3. No need to update forms when domain model changes\n\n### JSON Schema support\n\nJSON Schemas are also supported via the (tiny) [tcomb-json-schema library](https://github.com/gcanti/tcomb-json-schema).\n\n**Note**. Please use tcomb-json-schema ^0.2.5.\n\n### Pluggable look and feel\n\nThe look and feel is customizable via react-native stylesheets and *templates* (see documentation).\n\n### Screencast\n\nhttp://react.rocks/example/tcomb-form-native\n\n### Example App\n\n[https://github.com/bartonhammond/snowflake](https://github.com/bartonhammond/snowflake) React-Native, Tcomb, Redux, Parse.com, Jest - 88% coverage\n\n# Example\n\n```js\n// index.ios.js\n\n'use strict';\n\nvar React = require('react-native');\nvar t = require('tcomb-form-native');\nvar { AppRegistry, StyleSheet, Text, View, TouchableHighlight } = React;\n\nvar Form = t.form.Form;\n\n// here we are: define your domain model\nvar Person = t.struct({\n  name: t.String,              // a required string\n  surname: t.maybe(t.String),  // an optional string\n  age: t.Number,               // a required number\n  rememberMe: t.Boolean        // a boolean\n});\n\nvar options = {}; // optional rendering options (see documentation)\n\nvar AwesomeProject = React.createClass({\n\n  onPress: function () {\n    // call getValue() to get the values of the form\n    var value = this.refs.form.getValue();\n    if (value) { // if validation fails, value will be null\n      console.log(value); // value here is an instance of Person\n    }\n  },\n\n  render: function() {\n    return (\n      \u003cView style={styles.container}\u003e\n        {/* display */}\n        \u003cForm\n          ref=\"form\"\n          type={Person}\n          options={options}\n        /\u003e\n        \u003cTouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'\u003e\n          \u003cText style={styles.buttonText}\u003eSave\u003c/Text\u003e\n        \u003c/TouchableHighlight\u003e\n      \u003c/View\u003e\n    );\n  }\n});\n\nvar styles = StyleSheet.create({\n  container: {\n    justifyContent: 'center',\n    marginTop: 50,\n    padding: 20,\n    backgroundColor: '#ffffff',\n  },\n  buttonText: {\n    fontSize: 18,\n    color: 'white',\n    alignSelf: 'center'\n  },\n  button: {\n    height: 36,\n    backgroundColor: '#48BBEC',\n    borderColor: '#48BBEC',\n    borderWidth: 1,\n    borderRadius: 8,\n    marginBottom: 10,\n    alignSelf: 'stretch',\n    justifyContent: 'center'\n  }\n});\n\nAppRegistry.registerComponent('AwesomeProject', () =\u003e AwesomeProject);\n```\n\n**Output:**\n\n(Labels are automatically generated)\n\n![Result](docs/images/result.png)\n\n**Ouput after a validation error:**\n\n![Result after a validation error](docs/images/validation.png)\n\n# API\n\n## `getValue()`\n\nReturns `null` if the validation failed, an instance of your model otherwise.\n\n\u003e **Note**. Calling `getValue` will cause the validation of all the fields of the form, including some side effects like highlighting the errors.\n\n## `validate()`\n\nReturns a `ValidationResult` (see [tcomb-validation](https://github.com/gcanti/tcomb-validation) for a reference documentation).\n\n## Adding a default value and listen to changes\n\nThe `Form` component behaves like a [controlled component](https://facebook.github.io/react/docs/forms.html):\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.maybe(t.String)\n});\n\nvar AwesomeProject = React.createClass({\n\n  getInitialState() {\n    return {\n      value: {\n        name: 'Giulio',\n        surname: 'Canti'\n      }\n    };\n  },\n\n  onChange(value) {\n    this.setState({value});\n  },\n\n  onPress: function () {\n    var value = this.refs.form.getValue();\n    if (value) {\n      console.log(value);\n    }\n  },\n\n  render: function() {\n    return (\n      \u003cView style={styles.container}\u003e\n        \u003cForm\n          ref=\"form\"\n          type={Person}\n          value={this.state.value}\n          onChange={this.onChange}\n        /\u003e\n        \u003cTouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'\u003e\n          \u003cText style={styles.buttonText}\u003eSave\u003c/Text\u003e\n        \u003c/TouchableHighlight\u003e\n      \u003c/View\u003e\n    );\n  }\n});\n```\n\nThe `onChange` handler has the following signature:\n\n```\n(raw: any, path: Array\u003cstring | number\u003e) =\u003e void\n```\n\nwhere\n\n- `raw` contains the current raw value of the form (can be an invalid value for your model)\n- `path` is the path to the field triggering the change\n\n\u003e **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:\n\n## Disable a field based on another field's value\n\n```js\nvar Type = t.struct({\n  disable: t.Boolean, // if true, name field will be disabled\n  name: t.String\n});\n\n// see the \"Rendering options\" section in this guide\nvar options = {\n  fields: {\n    name: {}\n  }\n};\n\nvar AwesomeProject = React.createClass({\n\n  getInitialState() {\n    return {\n      options: options,\n      value: null\n    };\n  },\n\n  onChange(value) {\n    // tcomb immutability helpers\n    // https://github.com/gcanti/tcomb/blob/master/docs/API.md#updating-immutable-instances\n    var options = t.update(this.state.options, {\n      fields: {\n        name: {\n          editable: {'$set': !value.disable}\n        }\n      }\n    });\n    this.setState({options: options, value: value});\n  },\n\n  onPress: function () {\n    var value = this.refs.form.getValue();\n    if (value) {\n      console.log(value);\n    }\n  },\n\n  render: function() {\n    return (\n      \u003cView style={styles.container}\u003e\n        \u003cForm\n          ref=\"form\"\n          type={Type}\n          options={this.state.options}\n          value={this.state.value}\n          onChange={this.onChange}\n        /\u003e\n        \u003cTouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'\u003e\n          \u003cText style={styles.buttonText}\u003eSave\u003c/Text\u003e\n        \u003c/TouchableHighlight\u003e\n      \u003c/View\u003e\n    );\n  }\n\n});\n```\n\n## How to get access to a field\n\nYou can get access to a field with the `getComponent(path)` API:\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.maybe(t.String),\n  age: t.Number,\n  rememberMe: t.Boolean\n});\n\nvar AwesomeProject = React.createClass({\n\n  componentDidMount() {\n    // give focus to the name textbox\n    this.refs.form.getComponent('name').refs.input.focus();\n  },\n\n  onPress: function () {\n    var value = this.refs.form.getValue();\n    if (value) {\n      console.log(value);\n    }\n  },\n\n  render: function() {\n    return (\n      \u003cView style={styles.container}\u003e\n        \u003cForm\n          ref=\"form\"\n          type={Person}\n        /\u003e\n        \u003cTouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'\u003e\n          \u003cText style={styles.buttonText}\u003eSave\u003c/Text\u003e\n        \u003c/TouchableHighlight\u003e\n      \u003c/View\u003e\n    );\n  }\n});\n```\n\n## How to clear form after submit\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.maybe(t.String),\n  age: t.Number,\n  rememberMe: t.Boolean\n});\n\nvar AwesomeProject = React.createClass({\n\n  getInitialState() {\n    return { value: null };\n  },\n\n  onChange(value) {\n    this.setState({ value });\n  },\n\n  clearForm() {\n    // clear content from all textbox\n    this.setState({ value: null });\n  },\n\n  onPress: function () {\n    var value = this.refs.form.getValue();\n    if (value) {\n      console.log(value);\n      // clear all fields after submit\n      this.clearForm();\n    }\n  },\n\n  render: function() {\n    return (\n      \u003cView style={styles.container}\u003e\n        \u003cForm\n          ref=\"form\"\n          type={Person}\n          value={this.state.value}\n          onChange={this.onChange.bind(this)}\n        /\u003e\n        \u003cTouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'\u003e\n          \u003cText style={styles.buttonText}\u003eSave\u003c/Text\u003e\n        \u003c/TouchableHighlight\u003e\n      \u003c/View\u003e\n    );\n  }\n});\n```\n\n## Dynamic forms example: how to change a form based on selection\n\nSay 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:\n\n```js\nconst Country = t.enums({\n  'IT': 'Italy',\n  'US': 'United States'\n}, 'Country');\n\nvar AwesomeProject = React.createClass({\n\n  // returns the suitable type based on the form value\n  getType(value) {\n    if (value.country === 'IT') {\n      return t.struct({\n        country: Country,\n        rememberMe: t.Boolean\n      });\n    } else if (value.country === 'US') {\n      return t.struct({\n        country: Country,\n        name: t.String\n      });\n    } else {\n      return t.struct({\n        country: Country\n      });\n    }\n  },\n\n  getInitialState() {\n    const value = {};\n    return { value, type: this.getType(value) };\n  },\n\n  onChange(value) {\n    // recalculate the type only if strictly necessary\n    const type = value.country !== this.state.value.country ?\n      this.getType(value) :\n      this.state.type;\n    this.setState({ value, type });\n  },\n\n  onPress() {\n    var value = this.refs.form.getValue();\n    if (value) {\n      console.log(value);\n    }\n  },\n\n  render() {\n\n    return (\n      \u003cView style={styles.container}\u003e\n        \u003ct.form.Form\n          ref=\"form\"\n          type={this.state.type}\n          value={this.state.value}\n          onChange={this.onChange}\n        /\u003e\n        \u003cTouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'\u003e\n          \u003cText style={styles.buttonText}\u003eSave\u003c/Text\u003e\n        \u003c/TouchableHighlight\u003e\n      \u003c/View\u003e\n    );\n  }\n});\n```\n\n# Types\n\n### Required field\n\nBy default fields are required:\n\n```js\nvar Person = t.struct({\n  name: t.String,    // a required string\n  surname: t.String  // a required string\n});\n```\n\n### Optional field\n\nIn order to create an optional field, wrap the field type with the `t.maybe` combinator:\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.String,\n  email: t.maybe(t.String) // an optional string\n});\n```\n\nThe postfix `\" (optional)\"` is automatically added to optional fields.\n\nYou can customise the postfix value or setting a postfix for required fields:\n\n```js\nt.form.Form.i18n = {\n  optional: '',\n  required: ' (required)' // inverting the behaviour: adding a postfix to the required fields\n};\n```\n\n### Numbers\n\nIn order to create a numeric field, use the `t.Number` type:\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.String,\n  email: t.maybe(t.String),\n  age: t.Number // a numeric field\n});\n```\n\ntcomb-form-native will convert automatically numbers to / from strings.\n\n### Booleans\n\nIn order to create a boolean field, use the `t.Boolean` type:\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.String,\n  email: t.maybe(t.String),\n  age: t.Number,\n  rememberMe: t.Boolean // a boolean field\n});\n```\n\nBooleans are displayed as `SwitchIOS`s.\n\n### Dates\n\nIn order to create a date field, use the `t.Date` type:\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  surname: t.String,\n  email: t.maybe(t.String),\n  age: t.Number,\n  birthDate: t.Date // a date field\n});\n```\n\nDates are displayed as `DatePickerIOS`s under iOS and `DatePickerAndroid` or `TimePickerAndroid` under Android, depending on the `mode` selected (`date` or `time`).\n\nUnder Android, use the `fields` option to configure which `mode` to display the Picker:\n\n```js\n// see the \"Rendering options\" section in this guide\nvar options = {\n  fields: {\n    birthDate: {\n      mode: 'date' // display the Date field as a DatePickerAndroid\n    }\n  }\n};\n```\n\n#### iOS date `config` option\n\nThe 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:\n\n| Key | Value |\n|-----|-------|\n| `animation` | The animation to collapse the date picker. Defaults to `Animated.timing`. |\n| `animationConfig` | The animation configuration object. Defaults to `{duration: 200}` for the default animation. |\n| `format` | A `(date) =\u003e String(date)` kind of function to provide a custom date format parsing to display the value. Optional, defaults to `(date) =\u003e String(date)`.\n| `defaultValueText` | An `string` to customize the default value of the `null` date value text. |\n\nFor the collapsible customization, look at the `dateTouchable` and `dateValue` keys in the stylesheet file.\n\n#### Android date `config` option\n\nWhen using a `t.Date` type in Android, it can be configured through a `config` option that take the following parameters:\n\n| Key | Value |\n|-----|-------|\n| ``background`` | Determines the type of background drawable that's going to be used to display feedback. Optional, defaults to ``TouchableNativeFeedback.SelectableBackground``. |\n| ``format`` | A ``(date) =\u003e String(date)`` kind of function to provide a custom date format parsing to display the value. Optional, defaults to ``(date) =\u003e String(date)``.\n| ``dialogMode`` | Determines the type of datepicker mode for Android (`default`, `spinner` or `calendar`). |\n| `defaultValueText` | An `string` to customize the default value of the `null` date value text. |\n\n### Enums\n\nIn order to create an enum field, use the `t.enums` combinator:\n\n```js\nvar Gender = t.enums({\n  M: 'Male',\n  F: 'Female'\n});\n\nvar Person = t.struct({\n  name: t.String,\n  surname: t.String,\n  email: t.maybe(t.String),\n  age: t.Number,\n  rememberMe: t.Boolean,\n  gender: Gender // enum\n});\n```\n\nEnums are displayed as `Picker`s.\n\n#### iOS select `config` option\n\nThe 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:\n\n| Key | Value |\n|-----|-------|\n| `animation` | The animation to collapse the date picker. Defaults to `Animated.timing`. |\n| `animationConfig` | The animation configuration object. Defaults to `{duration: 200}` for the default animation. |\n\nFor the collapsible customization, look at the `pickerContainer`, `pickerTouchable` and `pickerValue` keys in the stylesheet file.\n\n### Refinements\n\nA *predicate* is a function with the following signature:\n\n```\n(x: any) =\u003e boolean\n```\n\nYou can refine a type with the `t.refinement(type, predicate)` combinator:\n\n```js\n// a type representing positive numbers\nvar Positive = t.refinement(t.Number, function (n) {\n  return n \u003e= 0;\n});\n\nvar Person = t.struct({\n  name: t.String,\n  surname: t.String,\n  email: t.maybe(t.String),\n  age: Positive, // refinement\n  rememberMe: t.Boolean,\n  gender: Gender\n});\n```\n\nSubtypes allow you to express any custom validation with a simple predicate.\n\n# Rendering options\n\nIn order to customize the look and feel, use an `options` prop:\n\n```js\n\u003cForm type={Model} options={options} /\u003e\n```\n\n## Form component\n\n### Labels and placeholders\n\nBy default labels are automatically generated. You can turn off this behaviour or override the default labels\non field basis.\n\n```js\nvar options = {\n  label: 'My struct label' // \u003c= form legend, displayed before the fields\n};\n\nvar options = {\n  fields: {\n    name: {\n      label: 'My name label' // \u003c= label for the name field\n    }\n  }\n};\n```\n\nIn order to automatically generate default placeholders, use the option `auto: 'placeholders'`:\n\n```js\nvar options = {\n  auto: 'placeholders'\n};\n\n\u003cForm type={Person} options={options} /\u003e\n```\n\n![Placeholders](docs/images/placeholders.png)\n\nSet `auto: 'none'` if you don't want neither labels nor placeholders.\n\n```js\nvar options = {\n  auto: 'none'\n};\n```\n\n### Fields order\n\nYou can sort the fields with the `order` option:\n\n```js\nvar options = {\n  order: ['name', 'surname', 'rememberMe', 'gender', 'age', 'email']\n};\n```\n\n### Default values\n\nYou can set the default values passing a `value` prop to the `Form` component:\n\n```js\nvar value = {\n  name: 'Giulio',\n  surname: 'Canti',\n  age: 41,\n  gender: 'M'\n};\n\n\u003cForm type={Model} value={value} /\u003e\n```\n\n### Fields configuration\n\nYou can configure each field with the `fields` option:\n\n```js\nvar options = {\n  fields: {\n    name: {\n      // name field configuration here..\n    },\n    surname: {\n      // surname field configuration here..\n    }\n  }\n};\n```\n\n## Textbox component\n\nImplementation: `TextInput`\n\n**Tech note.** Values containing only white spaces are converted to `null`.\n\n### Placeholder\n\nYou can set the placeholder with the `placeholder` option:\n\n```js\nvar options = {\n  fields: {\n    name: {\n      placeholder: 'Your placeholder here'\n    }\n  }\n};\n```\n\n### Label\n\nYou can set the label with the `label` option:\n\n```js\nvar options = {\n  fields: {\n    name: {\n      label: 'Insert your name'\n    }\n  }\n};\n```\n\n### Help message\n\nYou can set a help message with the `help` option:\n\n```js\nvar options = {\n  fields: {\n    name: {\n      help: 'Your help message here'\n    }\n  }\n};\n```\n\n![Help](docs/images/help.png)\n\n### Error messages\n\nYou can add a custom error message with the `error` option:\n\n```js\nvar options = {\n  fields: {\n    email: {\n      // you can use strings or JSX\n      error: 'Insert a valid email'\n    }\n  }\n};\n```\n\n![Help](docs/images/error.png)\n\ntcomb-form-native will display the error message when the field validation fails.\n\n`error` can also be a function with the following signature:\n\n```\n(value, path, context) =\u003e ?(string | ReactElement)\n```\n\nwhere\n\n- `value` is an object containing the current form value.\n- `path` is the path of the value being validated\n- `context` is the value of the `context` prop. Also it contains a reference to the component options.\n\nThe value returned by the function will be used as error message.\n\nIf you want to show the error message onload, add the `hasError` option:\n\n```js\nvar options = {\n  hasError: true,\n  error: \u003ci\u003eA custom error message\u003c/i\u003e\n};\n```\n\nAnother way is to add a:\n\n```\ngetValidationErrorMessage(value, path, context)\n```\n\nstatic function to a type, where:\n\n- `value` is the (parsed) current value of the component.\n- `path` is the path of the value being validated\n- `context` is the value of the `context` prop. Also it contains a reference to the component options.\n\n\n```js\nvar Age = t.refinement(t.Number, function (n) { return n \u003e= 18; });\n\n// if you define a getValidationErrorMessage function, it will be called on validation errors\nAge.getValidationErrorMessage = function (value, path, context) {\n  return 'bad age, locale: ' + context.locale;\n};\n\nvar Schema = t.struct({\n  age: Age\n});\n\n...\n\n\u003ct.form.Form\n  ref=\"form\"\n  type={Schema}\n  context={{locale: 'it-IT'}}\n/\u003e\n```\n\nYou can even define `getValidationErrorMessage` on the supertype in order to be DRY:\n\n```js\nt.Number.getValidationErrorMessage = function (value, path, context) {\n  return 'bad number';\n};\n\nAge.getValidationErrorMessage = function (value, path, context) {\n  return 'bad age, locale: ' + context.locale;\n};\n```\n\n### Other standard options\n\nThe following standard options are available (see http://facebook.github.io/react-native/docs/textinput.html):\n\n- `allowFontScaling`\n- `autoCapitalize`\n- `autoCorrect`\n- `autoFocus`\n- `bufferDelay`\n- `clearButtonMode`\n- `editable`\n- `enablesReturnKeyAutomatically`\n- `keyboardType`\n- `maxLength`\n- `multiline`\n- `numberOfLines`\n- `onBlur`\n- `onEndEditing`\n- `onFocus`\n- `onSubmitEditing`\n- `onContentSizeChange`\n- `password`\n- `placeholderTextColor`\n- `returnKeyType`\n- `selectTextOnFocus`\n- `secureTextEntry`\n- `selectionState`\n- `textAlign`\n- `textAlignVertical`\n- `textContentType`\n- ~~`underlineColorAndroid`~~\n\n`underlineColorAndroid` is not supported now on `tcomb-form-native` due to random crashes on Android, especially on ScrollView. See more on:\nhttps://github.com/facebook/react-native/issues/17530#issuecomment-416367184\n\n## Checkbox component\n\nImplementation: `SwitchIOS`\n\nThe following options are similar to the `Textbox` component's ones:\n\n- `label`\n- `help`\n- `error`\n\n### Other standard options\n\nThe following standard options are available (see http://facebook.github.io/react-native/docs/switchios.html):\n\n- `disabled`\n- `onTintColor`\n- `thumbTintColor`\n- `tintColor`\n\n## Select component\n\nImplementation: `PickerIOS`\n\nThe following options are similar to the `Textbox` component's ones:\n\n- `label`\n- `help`\n- `error`\n\n### `nullOption` option\n\nYou can customize the null option with the `nullOption` option:\n\n```js\nvar options = {\n  fields: {\n    gender: {\n      nullOption: {value: '', text: 'Choose your gender'}\n    }\n  }\n};\n```\n\nYou can remove the null option setting the `nullOption` option to `false`.\n\n**Warning**: when you set `nullOption = false` you must also set the Form's `value` prop for the select field.\n\n**Tech note.** A value equal to `nullOption.value` (default `''`) is converted to `null`.\n\n### Options order\n\nYou can sort the options with the `order` option:\n\n```js\nvar options = {\n  fields: {\n    gender: {\n      order: 'asc' // or 'desc'\n    }\n  }\n};\n```\n\n### Options isCollapsed\n\nYou can determinate if Select is collapsed:\n\n```js\nvar options = {\n  fields: {\n    gender: {\n      isCollapsed: false // default: true\n    }\n  }\n};\n```\n\nIf option not set, default is `true`\n\n### Options onCollapseChange\n\nYou can set a callback, triggered, when collapse change:\n\n```js\nvar options = {\n  fields: {\n    gender: {\n      onCollapseChange: () =\u003e { console.log('collapse changed'); }\n    }\n  }\n};\n```\n\n## DatePicker component\n\nImplementation: `DatePickerIOS`\n\n### Example\n\n```js\nvar Person = t.struct({\n  name: t.String,\n  birthDate: t.Date\n});\n```\n\nThe following options are similar to the `Textbox` component's ones:\n\n- `label`\n- `help`\n- `error`\n\n### Other standard options\n\nThe following standard options are available (see http://facebook.github.io/react-native/docs/datepickerios.html):\n\n- `maximumDate`,\n- `minimumDate`,\n- `minuteInterval`,\n- `mode`,\n- `timeZoneOffsetInMinutes`\n\n## Hidden Component\n\nFor any component, you can set the field with the `hidden` option:\n\n```js\nvar options = {\n  fields: {\n    name: {\n      hidden: true\n    }\n  }\n};\n```\n\nThis will completely skip the rendering of the component, while the default value will be available for validation purposes.\n\n# Unions\n\n**Code Example**\n\n```js\nconst AccountType = t.enums.of([\n  'type 1',\n  'type 2',\n  'other'\n], 'AccountType')\n\nconst KnownAccount = t.struct({\n  type: AccountType\n}, 'KnownAccount')\n\n// UnknownAccount extends KnownAccount so it owns also the type field\nconst UnknownAccount = KnownAccount.extend({\n  label: t.String,\n}, 'UnknownAccount')\n\n// the union\nconst Account = t.union([KnownAccount, UnknownAccount], 'Account')\n\n// the final form type\nconst Type = t.list(Account)\n\nconst options = {\n  item: [ // one options object for each concrete type of the union\n    {\n      label: 'KnownAccount'\n    },\n    {\n      label: 'UnknownAccount'\n    }\n  ]\n}\n```\n\nGenerally `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:\n\n```js\n// if account type is 'other' return the UnknownAccount type\nAccount.dispatch = value =\u003e value \u0026\u0026 value.type === 'other' ? UnknownAccount : KnownAccount\n```\n\n# Lists\n\nYou can handle a list with the `t.list` combinator:\n\n```js\nconst Person = t.struct({\n  name: t.String,\n  tags: t.list(t.String) // a list of strings\n});\n```\n\n## Items configuration\n\nTo configure all the items in a list, set the `item` option:\n\n```js\nconst Person = t.struct({\n  name: t.String,\n  tags: t.list(t.String) // a list of strings\n});\n\nconst options = {\n  fields: { // \u003c= Person options\n    tags: {\n      item: { // \u003c= options applied to each item in the list\n        label: 'My tag'\n      }\n    }\n  }\n});\n```\n\n## Nested structures\n\nYou can nest lists and structs at an arbitrary level:\n\n```js\nconst Person = t.struct({\n  name: t.String,\n  surname: t.String\n});\n\nconst Persons = t.list(Person);\n```\n\nIf you want to provide options for your nested structures they must be nested\nfollowing the type structure. Here is an example:\n\n```js\nconst Person = t.struct({\n  name: t.Struct,\n  position: t.Struct({\n    latitude: t.Number,\n    longitude: t.Number\n  });\n});\n\nconst options = {\n  fields: { // \u003c= Person options\n    name: {\n        label: 'name label'\n    }\n    position: {\n        fields: {\n            // Note that latitude is not directly nested in position,\n            // but in the fields property\n            latitude: {\n                label: 'My position label'\n            }\n        }\n    }\n  }\n});\n```\n\nWhen dealing with `t.list`, make sure to declare the `fields` property inside the `item` property, as such:\n\n```js\nconst Documents = t.struct({\n  type: t.Number,\n  value: t.String\n})\n\nconst Person = t.struct({\n  name: t.Struct,\n  documents: t.list(Documents)\n});\n\nconst options = {\n  fields: {\n    name: { /*...*/ },\n    documents: {\n      item: {\n        fields: {\n          type: {\n            // Documents t.struct 'type' options\n          },\n          value: {\n            // Documents t.struct 'value' options\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n## Internationalization\n\nYou can override the default language (english) with the `i18n` option:\n\n```js\nconst options = {\n  i18n: {\n    optional: ' (optional)',\n    required: '',\n    add: 'Add',   // add button\n    remove: '✘',  // remove button\n    up: '↑',      // move up button\n    down: '↓'     // move down button\n  }\n};\n```\n\n## Buttons configuration\n\nYou can prevent operations on lists with the following options:\n\n- `disableAdd`: (default `false`) prevents adding new items\n- `disableRemove`: (default `false`) prevents removing existing items\n- `disableOrder`: (default `false`) prevents sorting existing items\n\n```js\nconst options = {\n  disableOrder: true\n};\n```\n\n## List with Dynamic Items (Different structs based on selected value)\n\nLists 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:\n\n```js\nconst AccountType = t.enums.of([\n  'type 1',\n  'type 2',\n  'other'\n], 'AccountType')\n\nconst KnownAccount = t.struct({\n  type: AccountType\n}, 'KnownAccount')\n\n// UnknownAccount extends KnownAccount so it owns also the type field\nconst UnknownAccount = KnownAccount.extend({\n  label: t.String,\n}, 'UnknownAccount')\n\n// the union\nconst Account = t.union([KnownAccount, UnknownAccount], 'Account')\n\n// the final form type\nconst Type = t.list(Account)\n```\n\nGenerally `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:\n\n```js\n// if account type is 'other' return the UnknownAccount type\nAccount.dispatch = value =\u003e value \u0026\u0026 value.type === 'other' ? UnknownAccount : KnownAccount\n```\n\n# Customizations\n\n## Stylesheets\n\nSee also [Stylesheet guide](docs/STYLESHEETS.md).\n\ntcomb-form-native comes with a default style. You can customize the look and feel by setting another stylesheet:\n\n```js\nvar t = require('tcomb-form-native/lib');\nvar i18n = require('tcomb-form-native/lib/i18n/en');\nvar templates = require('tcomb-form-native/lib/templates/bootstrap');\n\n// define a stylesheet (see lib/stylesheets/bootstrap for an example)\nvar stylesheet = {...};\n\n// override globally the default stylesheet\nt.form.Form.stylesheet = stylesheet;\n// set defaults\nt.form.Form.templates = templates;\nt.form.Form.i18n = i18n;\n```\n\nYou can also override the stylesheet locally for selected fields:\n\n```js\nvar Person = t.struct({\n  name: t.String\n});\n\nvar options = {\n  fields: {\n    name: {\n      stylesheet: myCustomStylesheet\n    }\n  }\n};\n```\n\nOr per form:\n\n```js\nvar Person = t.struct({\n  name: t.String\n});\n\nvar options = {\n  stylesheet: myCustomStylesheet\n};\n```\n\nFor a complete example see the default stylesheet https://github.com/gcanti/tcomb-form-native/blob/master/lib/stylesheets/bootstrap.js.\n\n## Templates\n\ntcomb-form-native comes with a default layout, i.e. a bunch of templates, one for each component.\nWhen changing the stylesheet is not enough, you can customize the layout by setting custom templates:\n\n```js\nvar t = require('tcomb-form-native/lib');\nvar i18n = require('tcomb-form-native/lib/i18n/en');\nvar stylesheet = require('tcomb-form-native/lib/stylesheets/bootstrap');\n\n// define the templates (see lib/templates/bootstrap for an example)\nvar templates = {...};\n\n// override globally the default layout\nt.form.Form.templates = templates;\n// set defaults\nt.form.Form.stylesheet = stylesheet;\nt.form.Form.i18n = i18n;\n```\n\nYou can also override the template locally:\n\n```js\nvar Person = t.struct({\n  name: t.String\n});\n\nfunction myCustomTemplate(locals) {\n\n  var containerStyle = {...};\n  var labelStyle = {...};\n  var textboxStyle = {...};\n\n  return (\n    \u003cView style={containerStyle}\u003e\n      \u003cText style={labelStyle}\u003e{locals.label}\u003c/Text\u003e\n      \u003cTextInput style={textboxStyle} /\u003e\n    \u003c/View\u003e\n  );\n}\n\nvar options = {\n  fields: {\n    name: {\n      template: myCustomTemplate\n    }\n  }\n};\n```\n\nA template is a function with the following signature:\n\n```\n(locals: Object) =\u003e ReactElement\n```\n\nwhere `locals` is an object contaning the \"recipe\" for rendering the input and it's built for you by tcomb-form-native.\nLet's see an example: the `locals` object passed in the `checkbox` template:\n\n```js\ntype Message = string | ReactElement\n\n{\n  stylesheet: Object, // the styles to be applied\n  hasError: boolean,  // true if there is a validation error\n  error: ?Message,    // the optional error message to be displayed\n  label: Message,     // the label to be displayed\n  help: ?Message,     // the optional help message to be displayed\n  value: boolean,     // the current value of the checkbox\n  onChange: Function, // the event handler to be called when the value changes\n  config: Object,     // an optional object to pass configuration options to the new template\n\n  ...other input options here...\n\n}\n```\n\nFor a complete example see the default template https://github.com/gcanti/tcomb-form-native/blob/master/lib/templates/bootstrap.\n\n## i18n\n\ntcomb-form-native comes with a default internationalization (English). You can change it by setting another i18n object:\n\n```js\nvar t = require('tcomb-form-native/lib');\nvar templates = require('tcomb-form-native/lib/templates/bootstrap');\n\n// define an object containing your translations (see tcomb-form-native/lib/i18n/en for an example)\nvar i18n = {...};\n\n// override globally the default i18n\nt.form.Form.i18n = i18n;\n// set defaults\nt.form.Form.templates = templates;\nt.form.Form.stylesheet = stylesheet;\n```\n\n## Transformers\n\nSay you want a search textbox which accepts a list of keywords separated by spaces:\n\n```js\nvar Search = t.struct({\n  search: t.list(t.String)\n});\n```\n\ntcomb-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:\n\n```js\nvar options = {\n  fields: {\n    search: {\n      factory: t.form.Textbox\n    }\n  }\n};\n```\n\nThere 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:\n\n```js\nvar Transformer = t.struct({\n  format: t.Function, // from value to string, it must be idempotent\n  parse: t.Function   // from string to value\n});\n```\n\nA basic transformer implementation for the search textbox:\n\n```js\nvar listTransformer = {\n  format: function (value) {\n    return Array.isArray(value) ? value.join(' ') : value;\n  },\n  parse: function (str) {\n    return str ? str.split(' ') : [];\n  }\n};\n```\n\nNow you can handle lists using the transformer option:\n\n```js\n// example of initial value\nvar value = {\n  search: ['climbing', 'yosemite']\n};\n\nvar options = {\n  fields: {\n    search: {\n      factory: t.form.Textbox, // tell tcomb-react-native to use the same component for textboxes\n      transformer: listTransformer,\n      help: 'Keywords are separated by spaces'\n    }\n  }\n};\n```\n\n## Custom factories\n\nYou can pack together style, template (and transformers) in a custom component and then you can use it with the `factory` option:\n\n```js\nvar Component = t.form.Component;\n\n// extend the base Component\nclass MyComponent extends Component {\n\n  // this is the only required method to implement\n  getTemplate() {\n    // define here your custom template\n    return function (locals) {\n\n      //return ... jsx ...\n\n    };\n  }\n\n  // you can optionally override the default getLocals method\n  // it will provide the locals param to your template\n  getLocals() {\n\n    // in locals you'll find the default locals:\n    // - path\n    // - error\n    // - hasError\n    // - label\n    // - onChange\n    // - stylesheet\n    var locals = super.getLocals();\n\n    // add here your custom locals\n\n    return locals;\n  }\n\n\n}\n\n// as example of transformer: this is the default transformer for textboxes\nMyComponent.transformer = {\n  format: value =\u003e Nil.is(value) ? null : value,\n  parse: value =\u003e (t.String.is(value) \u0026\u0026 value.trim() === '') || Nil.is(value) ? null : value\n};\n\nvar Person = t.struct({\n  name: t.String\n});\n\nvar options = {\n  fields: {\n    name: {\n      factory: MyComponent\n    }\n  }\n};\n```\n\n# Tests\n\n```\nnpm test\n```\n**Note:** If you are using Jest, you will encounter an error which can\nbe fixed w/ a small change to the ```package.json```.\n\nThe error will look similiar to the following:\n```\nError: Cannot find module './datepicker' from 'index.js' at\nResolver.resolveModule\n```\n\nA completely working example ```jest``` setup is shown below w/ the\n[http://facebook.github.io/jest/docs/api.html#modulefileextensions-array-string](http://facebook.github.io/jest/docs/api.html#modulefileextensions-array-string)\nfix added:\n\n```\n  \"jest\": {\n    \"setupEnvScriptFile\": \"./node_modules/react-native/jestSupport/env.js\",\n    \"haste\": {\n      \"defaultPlatform\": \"ios\",\n      \"platforms\": [\n        \"ios\",\n        \"android\"\n      ],\n      \"providesModuleNodeModules\": [\n        \"react-native\"\n      ]\n    },\n    \"testPathIgnorePatterns\": [\n      \"/node_modules/\"\n    ],\n    \"testFileExtensions\": [\n      \"es6\",\n      \"js\"\n    ],\n    \"moduleFileExtensions\": [\n      \"js\",\n      \"json\",\n      \"es6\",\n      \"ios.js\"    \u003c\u003c\u003c\u003c\u003c\u003c\u003c\u003c\u003c\u003c\u003c\u003c this needs to be defined!\n    ],\n    \"unmockedModulePathPatterns\": [\n      \"react\",\n      \"react-addons-test-utils\",\n      \"react-native-router-flux\",\n      \"promise\",\n      \"source-map\",\n      \"key-mirror\",\n      \"immutable\",\n      \"fetch\",\n      \"redux\",\n      \"redux-thunk\",\n      \"fbjs\"\n    ],\n    \"collectCoverage\": false,\n    \"verbose\": true\n    },\n```\n\n# License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgcanti%2Ftcomb-form-native","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgcanti%2Ftcomb-form-native","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgcanti%2Ftcomb-form-native/lists"}