MCPcopy
hub / github.com/gcanti/tcomb-form-native

github.com/gcanti/tcomb-form-native @v0.6.20 sqlite

repository ↗ · DeepWiki ↗ · release v0.6.20 ↗
127 symbols 269 edges 16 files 0 documented · 0%
README

build status dependency status npm downloads

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, reviewing and testing some PRs.

Contents

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

Domain Driven Forms

The tcomb library provides a concise but expressive way to define domain models in JavaScript.

The tcomb-validation library 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 <Form type={Model} /> 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.

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 React-Native, Tcomb, Redux, Parse.com, Jest - 88% coverage

Example

// 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 (
      <View style={styles.container}>
        {/* display */}
        <Form
          ref="form"
          type={Person}
          options={options}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

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

Ouput after a validation error:

Result 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 for a reference documentation).

Adding a default value and listen to changes

The Form component behaves like a controlled component:

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 (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Person}
          value={this.state.value}
          onChange={this.onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

The onChange handler has the following signature:

(raw: any, path: Array<string | number>) => 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

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 (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Type}
          options={this.state.options}
          value={this.state.value}
          onChange={this.onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }

});

How to get access to a field

You can get access to a field with the getComponent(path) API:

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 (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Person}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

How to clear form after submit

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 (
      <View style={styles.container}>
        <Form
          ref="form"
          type={Person}
          value={this.state.value}
          onChange={this.onChange.bind(this)}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

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:

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 (
      <View style={styles.container}>
        <t.form.Form
          ref="form"
          type={this.state.type}
          value={this.state.value}
          onChange={this.onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

Types

Required field

By default fields are required:

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:

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:

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:

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:

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 SwitchIOSs.

Dates

In order to create a date field, use the t.Date type:

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 DatePickerIOSs 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:

// 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) => S

Core symbols most depended-on inside this repo

getLocals
called by 78
lib/components.js
onChange
called by 12
lib/components.js
pureValidate
called by 12
lib/components.js
getTemplate
called by 9
lib/components.js
getI18n
called by 7
lib/components.js
getAuto
called by 5
lib/components.js
validate
called by 5
lib/components.js
getValidationOptions
called by 4
lib/components.js

Shape

Method 72
Function 35
Class 20

Languages

TypeScript100%

Modules by API surface

lib/components.js93 symbols
lib/util.js17 symbols
lib/templates/bootstrap/select.ios.js5 symbols
lib/templates/bootstrap/datepicker.ios.js4 symbols
test/index.js2 symbols
lib/templates/bootstrap/list.js2 symbols
lib/templates/bootstrap/textbox.js1 symbols
lib/templates/bootstrap/struct.js1 symbols
lib/templates/bootstrap/datepicker.android.js1 symbols
lib/templates/bootstrap/checkbox.js1 symbols

Dependencies from manifests, versioned

@types/eslint-plugin-prettier2.2.0 · 1×
@types/prettier1.7.0 · 1×
babel5.8.34 · 1×
babel-core5.8.34 · 1×
eslint4.19.1 · 1×
eslint-config-prettier2.9.0 · 1×
eslint-plugin-prettier2.6.0 · 1×
eslint-plugin-react2.5.2 · 1×
prettier1.7.4 · 1×
prop-types15.5.10 · 1×
react15.6.1 · 1×
tape3.5.0 · 1×

For agents

$ claude mcp add tcomb-form-native \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact