Giter Site home page Giter Site logo

tcomb-form-native's Introduction

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) => 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:

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

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:

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

<Form type={Model} options={options} />

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.

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

var options = {
  auto: 'placeholders'
};

<Form type={Person} options={options} />

Placeholders

Set auto: 'none' if you don't want neither labels nor placeholders.

var options = {
  auto: 'none'
};

Fields order

You can sort the fields with the order option:

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:

var value = {
  name: 'Giulio',
  surname: 'Canti',
  age: 41,
  gender: 'M'
};

<Form type={Model} value={value} />

Fields configuration

You can configure each field with the fields option:

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:

var options = {
  fields: {
    name: {
      placeholder: 'Your placeholder here'
    }
  }
};

Label

You can set the label with the label option:

var options = {
  fields: {
    name: {
      label: 'Insert your name'
    }
  }
};

Help message

You can set a help message with the help option:

var options = {
  fields: {
    name: {
      help: 'Your help message here'
    }
  }
};

Help

Error messages

You can add a custom error message with the error option:

var options = {
  fields: {
    email: {
      // you can use strings or JSX
      error: 'Insert a valid email'
    }
  }
};

Help

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:

var options = {
  hasError: true,
  error: <i>A custom error message</i>
};

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.
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
});

...

<t.form.Form
  ref="form"
  type={Schema}
  context={{locale: 'it-IT'}}
/>

You can even define getValidationErrorMessage on the supertype in order to be DRY:

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: facebook/react-native#17530 (comment)

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:

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:

var options = {
  fields: {
    gender: {
      order: 'asc' // or 'desc'
    }
  }
};

Options isCollapsed

You can determinate if Select is collapsed:

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:

var options = {
  fields: {
    gender: {
      onCollapseChange: () => { console.log('collapse changed'); }
    }
  }
};

DatePicker component

Implementation: DatePickerIOS

Example

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:

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

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:

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

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:

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:

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:

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:

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:

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

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:

// if account type is 'other' return the UnknownAccount type
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount

Customizations

Stylesheets

See also Stylesheet guide.

tcomb-form-native comes with a default style. You can customize the look and feel by setting another stylesheet:

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:

var Person = t.struct({
  name: t.String
});

var options = {
  fields: {
    name: {
      stylesheet: myCustomStylesheet
    }
  }
};

Or per form:

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:

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:

var Person = t.struct({
  name: t.String
});

function myCustomTemplate(locals) {

  var containerStyle = {...};
  var labelStyle = {...};
  var textboxStyle = {...};

  return (
    <View style={containerStyle}>
      <Text style={labelStyle}>{locals.label}</Text>
      <TextInput style={textboxStyle} />
    </View>
  );
}

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:

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:

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:

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:

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:

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:

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:

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

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

tcomb-form-native's People

Contributors

abdifardin avatar adamrainsby avatar alexcouret avatar alvaromb avatar bartonhammond avatar bkrem avatar compojoom avatar danilvalov avatar emilioicai avatar ericraio avatar francescjimenez avatar garylesueur avatar gcanti avatar hakoniemi avatar hitabis avatar jebschiefer avatar kenleezle avatar kevinhury avatar koenpunt avatar lukaszgoworko avatar moreno97 avatar nemo avatar piranna avatar rbinksy avatar rodrigobdz avatar thebergamo avatar tibuurcio avatar tonycoco avatar txssseal avatar zergov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tcomb-form-native's Issues

Correct way to update domain model programmatically?

Loving this library, thanks for porting it to React-Native!

As per the readme:

3. No need to update forms when domain model changes

How do you update the domain model programmatically?

..or at least return the current state of all inputs (not just ones that pass validation via getValue)?

Edit: Just noticed that onChange will pass current state of all inputs

I might be going about this the wrong way but it doesn't appear that the domain model object is updatable or elements of the domain model (or generated inputs) are being mounted as writable props on this.refs.form.props.

Use cases:

  1. Radio inputs (multiple inputs, only one can be selected at once)
  2. Updating one input based on the value of another (country -> state -> city selectors)
  3. Progressive/expanding forms (user enters email -> secondary email confirmation input appears)
  4. Field to field validation (secondary email input validates against primary email input)

Most React-ish way I found was to assign the forms default values (as per this) to the parent's state and update as needed with setState. Unfortunately this expectedly will reset any other user-modified inputs to their initial default values.

Also tried extending this.state.values with this.refs.form.getValue()'s response (along with any changes I want to inject), but this will expectedly reset any inputs that do not pass validation (since they are not returned by getValue) which effectively clears the users progress and any error message.

Edit: didn't realize that getValue only returns if all inputs validate. Thought it returned data for individual fields that validated successfully.

Radio input example:

var ChildInfo = t.struct({            
  birthDate: t.Dat,
  male: t.maybe(t.Bool),
  female: t.maybe(t.Bool),
});

var SignupChildView = React.createClass({
  getInitialState: function(){
    return {
      values: {}
    }
  },
  _onChange: function() {
    var value = this.refs.form.getValue();
    if (value.male === true) { // if male checkbox is selected, deselect the female checkbox

      var _values = this.state.values
      _values['female'] = false
      this.setState({values: _values})

    }
    if (value.female === true) { // if female checkbox is selected, deselect the male checkbox

      var _values = this.state.values
      _values['male'] = false
      this.setState({values: _values})

    }

  },
  render: function() {
    return (
      <View style={styles.container}>
        {/* display */}

        <Form
          ref="form"
          type={ChildInfo}
          value={this.state.values}
          options={options}
          onChange={this._onChange}
        />
        <TouchableHighlight style={styles.button} onPress={this._onSubmit} underlayColor='#99d9f4'>
          <Text style={styles.buttonText}>Next</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

Maybe there's a way to do this? If not, would be awesome to have the option of defining the domain model itself as a part of component state to more easily leverage React's reconciliation.

Thanks in advance for your feedback, and again for this awesome library!

Multiline text

Using a multiline textinput generates an error message. The field is visible as a single line text input and is not editable.

Error shown in Xcode debug output:
[RCTLog][tid:0x7fe59435fbc0][RCTUIManager.m:710]>No manager class found for view with module name "RCTTextView"

Model:

var Visit = t.struct({
  date: t.Dat,
  name: t.Str,
  email: t.Str,
  company: t.maybe(t.Str),
  comment: t.maybe(t.Str),
});

Options:

var options = {
  auto: 'placeholders',
  fields: {
    date: {
      label: 'Datum van bezoek',
      mode: 'date'
    },
    name: {
      placeholder: 'Naam'
    },
    email: {
      keyboardType: 'default' //'email-address'
    },
    company: {
      placeholder: 'Bedrijfsnaam'
    },
    comment: {
      placeholder: 'Opmerking',
      multiline: true
    }
  }
};

I'm not sure if the form generator is causing this issue or that it is coming from React Native itself.
I created the project with the react-native-cli tool.

borderBottomColor

I'm trying to style the border of text fields more "material design" style. However, specifying borderBottomColor, etc doesn't seem to be working. Any suggestions?

Applying stylesheet doesn't seem to work

I'm loading and applying the bootstrap stylesheet, or at least, I think I am. Nothing seems to be happening:

var React = require('react-native');
var t = require('tcomb-form-native');
var styles = require('../Styles');
var formStyles = require('tcomb-form-native/lib/stylesheets/bootstrap');

var Form = t.form.Form;
Form.stylesheet = formStyles;
[....]
<Form
ref="form"
 type={Login}
options={options}
/>

Any suggestions?

Tab / next to next field

It would be great to be able to tab from one field to the next or display "Next" on keyboard.

this.refs.form.getComponent is no longer working?

testing with latest release and sample app and getting red screen

this.refs.form.getComponent is not a function

is there a new way to get the underlying component, I have retest and rebuilt the latest... what is the latest version of react-native that this component has been tested with?

When I look in the debugger, I dont see the method...

screenshot 2015-05-05 21 37 48

password field no longer hides input

I just upgraded to 0.2.2 and I noticed that the password field doesn't seem to hide input anymore. My config:

j_password:{
  label:i8n.getMessage('ui.password'),
  placeholder:i8n.getMessage('ui.password'),
  error: i8n.getMessage('errors.password'),
  password:true
}

Set State with modified options resets values

I remember asking on tcomb-validation some time ago how to deal w server side exceptions and create "manual validation exceptions".

The answer was to call setState with an options object, containing the field, hasError:true and the modified validation exception message.

However, this leads to:

  1. the state of the form being reset to null
  2. the values of the form still being visible in the UI

I tried two things:

  1. just calling setState, with the affected field. But this leads the other fields to be removed
  2. calling setState with a "full" modified options object containing the affected fields, hasError:true and modified validation messages.

Add required field additional string

Would like to see that required fields may also hold an additional string like optional have " (optional)".

Alterations to index.js:

t.form.Form.i18n = {
optional: ' (optional)',
required: ''
};

Alterations to components.js

line 100: return ctx.label + (this.typeInfo.isMaybe ? this.getI18n().optional : this.getI18n().required);

InvalidDate is not detected as invalid

When date field displayed as a DatePicker and the value for it was not explicitly set before submitting the form, the submitted value is equal InvalidDate

In index.js in tcomb-validation there is

validators.enums = function validateIrreducible(x, type, path) {
    return {
      value: x,
      errors: type.is(x) ? [] : [ValidationError.of(x, type, path)]
    };
  };

type.is(x) returns true in case of InvalidDate that is why no error was detected. Probably there is a need to check something like this:

if (Object.prototype.toString.call(x) === '[object Date]'  &&  isNaN(v.getTime())))
  // create an error

or set the value for the date field to current date

Could you please take a look?

can't change the style for the textinput border

I tried to change the style for the textbox in a way that the border is thinner and only the bottom border is showing. The thinner part worked but not the hiding

  textbox: {
    normal: {
      ...
      borderTopColor: '#ffffff',
      borderBottomColor: BORDER_COLOR,
      borderLeftColor: '#ffffff',
      borderRightColor: '#ffffff',
      borderWidth: 0.5,
      marginBottom: 5
    },

Question: Material design

Google Material design is having a huge design impact. How would I implement Material Design for tcomb? Also given the fact that there are some projects that supply the design and animations for react and for bootstrap?

Add es6 support for getValue()

Hi,

please add es6 class support for the getValue().
When I define a new component as a class, I can't use the this.ref.form.getValue() function to get
the values.

Example class:

class CreateEvent extends React.Component {

  componentDidMount() {
        //this.refs.form.getComponent('menu').refs.input.focus();
    }

  onPress() {
     console.log('Save button pressed.')
    // 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() {
    return (
      <ScrollView
        horizontal={false}
        contentInset={{top: -50}}
        style={[Styles.scrollView, Styles.horizontalScrollView]}>
        <View style={Styles.container}>
          <Form
            ref="form"
            type={Person}
            options={options}
          />
          <TouchableHighlight style={Styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
            <Text style={Styles.buttonText}>Save</Text>
          </TouchableHighlight>
        </View>
      </ScrollView>
    );
    }
}

Error:

Error: Cannot read property 'form' of undefined
 stack: 
  Object.CreateEvent.onPress                       index.ios.bundle:55308
  React.createClass.touchableHandlePress           index.ios.bundle:34825
  TouchableMixin._performSideEffectsForTransition  index.ios.bundle:33004
  TouchableMixin._receiveSignal                    index.ios.bundle:32928
  TouchableMixin.touchableHandleResponderRelease   index.ios.bundle:32731
  executeDispatch                                  index.ios.bundle:12786
  forEachEventDispatch                             index.ios.bundle:12774
  Object.executeDispatchesInOrder                  index.ios.bundle:12795
  executeDispatchesAndRelease                      index.ios.bundle:12174
 URL: undefined
 line: undefined
 message: Cannot read property 'form' of undefined```

Cannot read property 'is' of undefined (React Native v0.10.0)

Just updated React Native to 0.10.0 and tcomb-form-native to 2.4.0. Now when I try to load a screen containing a tcomb form I get Cannot read property 'is' of undefined

The top of the stack trace is:

Form.render
http://localhost:8081/index.ios.bundle:54433

integration with webpack

From this file: https://github.com/gcanti/tcomb-form-native/blob/master/lib/util/React.js

var React;
try {
  React = require.call(this, 'react');
} catch(e) {
  React = require('react-native');
}
module.exports = React;

This results in the following error while using webpack (via mjohnston/react-native-webpack-server),

WARNING in ./~/tcomb-form-native/lib/util/React.js
Critical dependencies:
12:10-17 require function is used in a way in which dependencies cannot be statically extracted
 @ ./~/tcomb-form-native/lib/util/React.js 12:10-17

I'm not really aware of another solution, but maybe once facebook/react-native#1331 is implemented?

Question: How to pass in standard options for t.dat?

I would like to change the mode and minuteInterval.

Model:

var form = t.struct({
    name: t.Str,
    birthdate: t.Dat
});

Options:

var options = {
  fields: {
    birthdate: {
      mode: 'time',
      minuteInterval: 10,
    }
  }
};

Form component

 <Form
    ref="form"
    type={form}
    onChange={this._onChange}
    options={options}
/>

name and birthdate render, but I am unable to modify the datePicker. Anything that I'm overlooking? Thanks!

this.props.keyboardType undefined

Hi,

I'm trying to implement a view using the tcomb-forms and am running into the issue below:

message: undefined is not an object (evaluating 'keyboardTypeConsts[
crossPlatformKeyboardTypeMap[this.props.keyboardType] ||
this.props.keyboardType
]')"

I'm simply trying to recreate the form described in the readme. Any idea what might be causing this?

t.Str with default value not editable

Hi

I currently have a form in which I fill in information from the beginning, because I use it like a "edit" tool. But I found that when a form is created with a t.Str the value is not editable! When typing, the value resets back to the value set at the beginning, though the value given by both onChange callback and by getValue() has changes (though only those within the last frame). But this is not the case if the field is optional, then everything works fine.

recreate :

var Person = t.struct({
  name: t.Str,              // a required string
  otherName: t.maybe(t.Str)
});


var Test = React.createClass({
  getInitialState: function() {
    return{
      value:{name:'test', otherName:'test'}
    }
  }

  render: function(){
  console.log("Render ", this.state.value);
    <Form
    ref="form"
    type={Person}
    value={this.state.value}
    onChange={this.onChange}
     />
  },

  onChange: function(value)
    {
        console.log("OnChange ", value);
        this.setState({value: value});
    }
});

disable on submit

Hi,
Is there a way to disable the submit button after clicking on it?

Thanks!

Question: How to find a component from Form or Struct?

Hi, I'm so sorry about my english.
I use tcomb-form-native and think that it's a good library.
Sometime, I want to find a component from Form or Struct.
What is the easiest solution?

This is my stupid solution.

class Struct extends Component {
 ... 
  findComponentByName(name) {
    return this.refs[name];
  }
}

class Form {
  ...
  findComponentByName(name) {
    return this.refs.input.findComponentByName(name);
  }
}

TextInput does not scroll as you type more

Thank you for the great library :-). I am extensively using it for our open source app.

One problem:

When you use react-native's TextInput component and type more than is visible, the text in the TextInput gets scrolled to the left while you are typing. Your's does not scroll. It allows to type as much as you want and will submit the whole value but you don’t see the rightmost part of the text which you typed.

Password field type?

Awesome plugin!

Is there a way to set a field type as password so the characters become dotted out and not viewable while typing?

Thanks,
-Tim

this.refs.form is undefined

Do you see anything wrong here?

refs is undefined.

'use strict';

var React = require('react-native');
var t = require('tcomb-form-native');
var { AppRegistry, StyleSheet, Text, View, TouchableHighlight, Component } = React;

var Form = t.form.Form;

var PaymentType = t.enums({
M: 'Cash',
F: 'Check'
});

var VendorNote = t.struct({
VendorName: t.Num,
PaymentType: PaymentType,
PaymentAmount: t.Num,
Notes:t.Str
});

var options = {};

class VendorNoteEntry extends Component {
render() {
return (

            <Form
                ref="form"
                type={VendorNote}
                options={options}
                />
            <TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
                <Text style={styles.buttonText}>Save</Text>
            </TouchableHighlight>
        </View>
    );
}

onPress() {
    // Getting error here
    console.log(this.refs.form.getValue());
}

}

var styles = StyleSheet.create({
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20,
backgroundColor: '#ffffff',
},
title: {
fontSize: 30,
alignSelf: 'center',
marginBottom: 30
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
button: {
height: 36,
backgroundColor: '#48BBEC',
borderColor: '#48BBEC',
borderWidth: 1,
borderRadius: 8,
marginBottom: 10,
alignSelf: 'stretch',
justifyContent: 'center'
}
});

module.exports = VendorNoteEntry;

Recommend making peerDependencies more flexible

Since most upgrades don't break anything, I recommend changing

"react-native": "^0.5.0",

to

"react-native": ">=0.5.0",

this prevents people getting stuck during upgrade processes in npm.

field values are null but text is still shown

Hi,
I have a login form with a validation on it. Once a user passes validation, his credentials are checked on the server side. If the login fails, the component renders an error. I can see that the value state for these fields has been cleared and if I press the submit button again, the tcomb validation fails (as it is supposed to). However, the text is still shown in the text fields.

Here is my code, thanks:

'use strict';

var React = require('react-native');
var tcomb = require('tcomb-form-native');
var I18n = require("../modules/i18n");
var Buttons = require("./buttons");
var LoginActions = require('../actions/login_actions');
var LoginStore = require('../stores/login_store');

var {
  StyleSheet,
  View,
  Text
} = React;

var LoginForm = tcomb.struct({
  email: tcomb.Str,
  password: tcomb.Str
});

var loginOptions = {
  auto: 'placeholders',
  fields: {
    email: {
      keyboardType: 'email-address'
    },
    password: {
      password: true,
      secureTextEntry: true
    }
  }
};

var Form = tcomb.form.Form;
var Login = React.createClass({
  getInitialState() {
    return {
      loginFailed: LoginStore.get("loginFailed"),
      value: {
        email: null,
        password: null
      }
    };
  },

  componentDidMount() {
    LoginStore.addLoginInfoClearedListener(this.resetForm);
    LoginStore.addLoginStartedListener(this.loginToggled);
  },

  componentWillUnmount() {
    LoginStore.removeLoginInfoClearedListener(this.resetForm);
    LoginStore.removeLoginStartedListener(this.loginToggled);
  },

  loginToggled() {
    this.setState({
      loginFailed: LoginStore.get("loginFailed"),
      loginInProcess: LoginStore.get("loginInProcess")
    });
  },

  resetForm() {
    this.loginToggled();
    this.setState({
      value: {
        email: null,
        password: null
      }
    })
  },

  validateForm() {
    var value = this.refs.form.getValue();
    if (value) { LoginActions.login(value); }
  },

  render() {
    return (
      <View style={styles.container}>
        {this.renderLoginFailed()}
        <Form
          ref="form"
          type={LoginForm}
          options={loginOptions}
          value={this.state.value}
        />
        <Buttons onPress={this.validateForm} text={"login"} />
        {this.renderLoginInProcess()}
      </View>
    );
  },

  renderLoginFailed() {
    if (this.state.loginFailed) {
      return (
        <Text>Your login credentials are wrong</Text>
      );
    }
  },

  renderLoginInProcess() {
    if (this.state.loginInProcess) {
      return (
        <Text>Login in process...</Text>
      );
    }
  }
});

var styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    marginTop: 60,
    padding: 20,
    backgroundColor: '#ffffff',
  },
  title: {
    fontSize: 30,
    alignSelf: 'center',
    marginBottom: 30
  }
});

module.exports = Login;

placeholders for string and number type fields but labels for dates

options: {
  auto: 'placeholders'
}

It looks great with placeholders BUT date type fields are loosing their label and there is no way to identify them

Is it possible to add auto option override for specific field something like this for example

options: {
  fields: {
     expires: {
        auto: 'labels' 
    }
  }
}

or ignore placeholders setting for dates alltogether

Different results from screenshot

I'm getting this-
image

... with this code below. Could you update your ReadMe with your exact code, if it's different from what's used to make the screenshot?

// index.ios.js

var React = require('react-native');
var { AppRegistry, Text, TouchableHighlight, View } = React;
var t = require('tcomb-form-native');

var Person = t.struct({
  name: t.Str,              // a required string
  surname: t.maybe(t.Str),  // a optional string
  age: t.Num,               // a required number
  rememberMe: t.Bool        // a boolean
});

var options = {}; // optional rendering options...

var AwesomeProject = React.createClass({

  onPress: function () {
    var value = this.refs.form.getValue();
    if (value) {
      console.log(value);
    }
  },

  render: function() {
    return (
      <View>
        <t.form.Form
          ref="form"
          type={Person}
          options={options}
          />
        <TouchableHighlight onPress={this.onPress}>
          <Text>Save</Text>
        </TouchableHighlight>
      </View>
    );
  }
});

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

Missed keystrokes

I'm seeing a lot missed keystrokes on my forms (iPhone 6 simulator). Seems to me that the rendering cycle is to blame and I'm not sure if the same problem would occur on the live phone but it shouldn't happen in the first place.. Perhaps an input box should use onBlur to update state or use some kind of buffer.

Warning message

Hi,
I just added the form and it looks great but the following message appears in the console:
Warning: Don't set .props.style of the React component. Instead, specify the correct value when initially creating the element or use React.cloneElement to make a new element with updated props.

The message goes away when I remove the Form element.

Code:

'use strict';

var React = require('react-native');
var tcomb = require('tcomb-form-native');
var I18n = require("../modules/i18n")
var Incidents = require('./incidents');

var {
  StyleSheet,
  Text,
  View,
  ListView,
  NavigatorIOS,
  TouchableHighlight
} = React;

var LoginForm = tcomb.struct({
  login: tcomb.Str,
  password: tcomb.Str
});

var loginOptions = {
  fields: {
    password: {
      password: true
    }
  }
};

var Form = tcomb.form.Form;
var Login = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Form
          ref="form"
          type={LoginForm}
          options={loginOptions}
        />
        <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',
  },
  title: {
    fontSize: 30,
    alignSelf: 'center',
    marginBottom: 30
  },
  buttonText: {
    fontSize: 18,
    color: 'white',
    alignSelf: 'center'
  },
  button: {
    height: 36,
    backgroundColor: '#48BBEC',
    borderColor: '#48BBEC',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  }
});

module.exports = Login;

Style for focused textinput's

Hi,

Thanks for this awesome lib!!

The addition of a new style "type" for focused textinputs would be amazing, sth along the lines of a slightly thicker border maybe, to supplement the "normal" and "error" types.

Doable?

Manually setting state on validation exception with password field generates error

So, I process server side exceptions by setting state against a tcomb form

var options = Object.freeze({
  fields:{
    j_username:{
      label:i8n.getMessage('ui.email'),
        placeholder:i8n.getMessage('ui.email'),
        error: i8n.getMessage('errors.email'),
        keyboardType:'email-address',
        autoCorrect:false
    },
    j_password:{
      label:i8n.getMessage('ui.password'),
        placeholder:i8n.getMessage('ui.password'),
      error: i8n.getMessage('errors.password'),
        password:true
    }
  }
});

[...]

    LoginActions.login(value.j_username,value.j_password)
    .then(status)
    .catch(function(err){
         this.setState({
            options:utils.processServerExceptions(options,err)
         });

So, processServerException just loops over the errors and if the fieldname matches, it will return a copy of the object with the relevante field getting a new error message and hasError:true

So, if the password field is the one generating the exception, I get this error, presumably because iOS doesn't allow you to set the password field

Error setting property 'secureTextEntry' of RCTTextField with tag #228:RCTTextField does not have setter for secureTextEntry' property.

Manual focus on input

Is somehow possible to focus form field? On reacts TextInput component is possible call focus method.

Support for modal screens (two-page form edit)?

Thanks for great component. The problem I can't figure out how to implement standard iOS two-screen approach for editing data, like first screen displays data with ">" arrows, after user clicks, user will could adjust that value. For example for gender it will display "Gender Male >" on first screen, and UIPicker within modal on second. Or text area "About me data >" on first screen and UITextView with modal on second?

The properties over 'passProps' are not reactive.

let firstRoute = {
name: 'XXX',
component: ItemListPage,
rightCorner: AddItemIcon,
rightCornerProps: {_onPressNewItemIcon: this._onPressNewItemIcon},
passProps: {_itemData: this.state._itemData,
newItemModalVisible: this.state.newItemModalVisible,
}
};

  return (
    <Router
      firstRoute={firstRoute}
      headerStyle={styles.header}
      />

  );

I can render the value of 'newItemModalVisible' in the 1st route. but the value is not updated when 'newItemModalVisible' is updated.

Does anyone have the same issue?

Thanks,
Chen

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.