Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hiddentao/react-native-advanced-forms
Flexible React Native architecture for building and managing forms
https://github.com/hiddentao/react-native-advanced-forms
Last synced: 8 days ago
JSON representation
Flexible React Native architecture for building and managing forms
- Host: GitHub
- URL: https://github.com/hiddentao/react-native-advanced-forms
- Owner: hiddentao
- License: mit
- Created: 2017-07-19T17:06:13.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-05-03T13:18:09.000Z (over 6 years ago)
- Last Synced: 2024-10-28T16:02:31.843Z (11 days ago)
- Language: JavaScript
- Homepage:
- Size: 1.34 MB
- Stars: 13
- Watchers: 5
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
README
# react-native-advanced-forms
[![npm](https://img.shields.io/npm/v/react-native-advanced-forms.svg?maxAge=2592000)](https://www.npmjs.com/package/react-native-advanced-forms)
[![Join the chat at https://discord.gg/bYt4tWB](https://img.shields.io/badge/discord-join%20chat-738bd7.svg?style=flat-square)](https://discord.gg/bYt4tWB)
[![Follow on Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=social&label=Follow&maxAge=2592000)](https://twitter.com/hiddentao)![Demo1](https://github.com/hiddentao/react-native-advanced-forms/raw/master/recordings/rec1.gif "Demo1")
Flexible React Native architecture for building and managing forms.
Features:
* Cross-platform (iOS, Android)
* Allow for flexible form layouts
* Easily manage and validate field input values
* Auto-focussing of next field for better user experience
* Easily integrate your own custom components
* Component-based - use with or without Redux
* Compatible with React Native 0.40+
* Successfully used in production apps## Installation
Use NPM/Yarn to install package: `react-native-advanced-forms`
_Note: You must also have the `prop-types` package installed._
```shell
npm install
```## Demo
A working demo app can be found in the `demo` folder _(Note that you will need
to copy `src` to `demo/src` for the app to successfully build)._## Usage
This code will render a form similar to what can be seen in the demo animation above:
```js
import React from 'react'
import { Button, Alert, StyleSheet, Text, View } from 'react-native'
import Form from 'react-native-advanced-forms'export default class App extends React.Component {
constructor (props, ctx) {
super(props, ctx)this.state = {
firstName: null,
lastName: null,
age: null,
country: null,
}
}render() {
const {
firstName, lastName, age, country
} = this.statereturn (
this.form.validateAndSubmit()}
title="Submit"
color="#841584"
/>
)
}_onFormRef = e => {
this.form = e
}onChange = (values) => {
this.setState(values)
}onSubmit = (values) => {
Alert.alert('Submitted: ' + JSON.stringify(values))
}validate = (values) => {
const ret = Object.keys(this.state).reduce((m, v) => {
if (!values[v] || !values[v].length) {
m[v] = Form.VALIDATION_RESULT.MISSING
}
return m
}, {})if (!ret.age && isNaN(values.age)) {
ret.age = Form.VALIDATION_RESULT.INCORRECT
}return ret
}
}const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: 100,
paddingHorizontal: 30
},
row: {
marginBottom: 20,
},
columns: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
},
field: {
marginRight: 10,
},
ageField: {
width: 60,
},
button: {
width: 80,
marginTop: 15,
},
error: {
marginTop: 10,
},
errorMsg: {
color: 'red'
}
})
```## API and props
### Form
This is the root component for a form and is responsible for co-ordinating
form value changes and auto-focussing components.Properties:
| Prop | Type | Default | Description |
| --------- | --------- | --------- | --------- |
| `submitOnReturn` | `boolean` | `true` | If turned off then the form will NOT be auto-submitted when the user presses the Return key after filling in the final field |
| `onChange` | `function (Object values)` | **required** | Called whenever form values change |
| `onSubmit` | `function (Object values)` | **required** | Called form values have passed validation and form is to be submitted |
| `validate` | `function (Object values)` | **required** | Called to validate form values. Must return an `Object` mapping field name to validation error. If it returns `{}` then it means all fields are valid. |
| `onValidationError` | `function (Array badFieldNames)` | `null` | Called when validation fails. |
| `onFocusField` | `function (String fieldName, Function callback)` | `null` | Called when form is about to auto-focus a field. The `callback` must be invoked for focussing to proceed. |
| `style` | Any | `null` | Styling to apply to form container element. |Methods:
| Method | Returns | Description |
| --------- | --------- | --------- |
| `validateAndSubmit` | `undefined` | Validate the form field values and submit it if validation succeeds. |
| `validate` | `undefined` | Validate the form field values and highlight errors if any. Returns `true` if validation passed. |
| `unfocus` | `undefined` | Unfocus all form fields. |
| `getValues` | `Object` | Get current form field values. |
| `canSubmit` | `boolean` | Get whether current form field values are valid such that form can be submitted. |### Form.Layout
This component works similarly to React Native `View` and is to be used to create whatever type of layout your require for your form. `Form.Layout` instances can be nested within each other to multiple levels without issue.
Properties:
| Prop | Type | Default | Description |
| --------- | --------- | --------- | --------- |
| `style` | Any | `null` | Styling to apply to form container element. |### Form.Section
This is a convenience component which constructs a `Form.Layout` to wrap its children but additionally attaches text above it:
![section](https://github.com/hiddentao/react-native-advanced-forms/raw/master/recordings/section.png "Section")
Properties:
| Prop | Type | Default | Description |
| --------- | --------- | --------- | --------- |
| `title` | `String` | `null` | The title text to show. |
| `style` | Any | `null` | Style to apply to root container. |
| `layoutStyle` | Any | `null` | Style to apply to nested `Form.Layout`. |
| `titleTextStyle` | Any | `null` | Style to apply to title text, is shown. |### Form.Field
This component **must** wrap every actual input element. It is responsible for setting up `onChange` and `onSubmit` handlers as well as pass through focus/unfocus and error display commands from the parent form.
If a `label` gets passed in it will use `Form.LabelGroup` and `Form.Label` to render a label above the wrapped form field component.
| Prop | Type | Default | Description |
| --------- | --------- | --------- | --------- |
| `name` | `String` | **required** | The field name. |
| `label` | `String` | `null` | The field label. |
| `labelStyle` | Any | `null` | The field label container style. |
| `labelTextStyle` | Any | `null` | The field label text style. |
| `labelRightContent` | Any | `null` | What to show in the right-hand side of the field label container (using `Form.LabelGroup`). |
| `style` | Any | `null` | The style for the root container. |
| `onSubmit` | `Function` | `null` | Usually set by the parent `Form`. |
| `onChange` | `Function` | `null` | Usually set by the parent `Form`. |### Form.TextField
A text field designed to work well with a `Form`. It handles validation error display and scroll view integration.
| Prop | Type | Default | Description |
| --------- | --------- | --------- | --------- |
| `value` | Any | `null` | The field value. |
| `error` | Any | `null` | The current field validation error. If set then the field is in "error" mode. |
| `style` | Any | `null` | The style for the text field in non-error mode. |
| `errorStyle` | Any | `null` | The style for the text field in error mode. |
| `getParentScrollView` | `Function` | `null` | Function to get reference to scroll view further up the UI hierarchy (if any). This is used to auto-scroll the scroll view such that the text field is visible whenever it receives focus. |
| `onSubmit` | `Function` | `null` | Usually set by the parent `Form.Field`. |
| `onChange` | `Function` | `null` | Usually set by the parent `Form.Field`. |Methods:
| Method | Returns | Description |
| --------- | --------- | --------- |
| `focus` | `undefined` | Focus this field (should show the keyboard). |
| `unfocus` | `undefined` | Unfocus this field (should hide the keyboard). |
| `getValue` | Any | Get current field value (will return `this.props.values`). |### Form.Label
Text to display above a form field element as its label. Gets automatically rendered by `Form.Field` if a label has been set.
| Prop | Type | Default | Description |
| --------- | --------- | --------- | --------- |
| `style` | Any | `null` | The style for the root container. |
| `textStyle` | Any | `null` | The style for the label text. |### Form.LabelGroup
A helper component which wraps a `Form.Label`, allowing for additional components to be displayed alongside it.
## Flexible layouts
The `Form.Layout` component is key to achieving flexible layouts. By default, if you don't use `Form.Layout` then components will be stacked on top of each other (assuming default `flexDirection` for the `Form`):
```js
```
If you wish to place the first two components next to each other then simply wrap them within a `Form.Layout` with the appropriate styling:
```js
```
And you can nest layouts:
```js
```
_Note: A working example of the above can be found in the `demo` folder._
## Form validation and submission
You must provide a `validate` property to the form, the value of which is a
function which returns which fields have failed validation, for example:```js
render () {
return (
...
)
}validate = (values) => {
const ret = {}if (!values.firstName) {
ret.firstName = Form.VALIDATION_RESULT.MISSING
}if (isNaN(values.age)) {
ret.age = Form.VALIDATION_RESULT.INCORRECT
}return ret
}onSubmit = (values) = {
...
}
```_Note: The default `VALIDATION_RESULT` values can be found in `src/utils.js`:_
If all fields have valid values then `validate()` must return `{}`. If
validation thus succeeds the form will be submitted and the `onSubmit`
handler which you passed in will be called with the `values`.If you try submit the form programmatically (by calling `validateAndSubmit()`)
and some fields have not yet been filled in, then they will be highlighted. On
the other hand, if you submit a
field (i.e. you enter text and then press _done_ or the equivalent on your
keyboard) then the form logic will auto-focus on the next field that needs to
be filled in:![Demo2](https://github.com/hiddentao/react-native-advanced-forms/raw/master/recordings/rec2.gif "Demo2")
_Note: `Form.Field` components will pass an `error` object down to their children (the actual input elements) if a validation error occurs_.
## Custom components
You can integrate your own custom form component into a `Form`, as long as you follow certain rules:
1. Your component must expose `focus()`, `unfocus()` and `getValue()` methods, just like as `Form.TextField` does.
2. Your component must call `this.props.onChange()` when its value changes.
3. Your component must call `this.props.onSubmit()` when it gets "submitted", e.g. for text fields the user may be press _Done_ on the keyboard to submit the input.Let's say we wish to integrate the React Native `Switch` component. Here is how we might define it:
```js
import { Switch } from 'react-native'class CustomSwitch extends Component {
render () {
const { turnedOn } = this.propsreturn (
)
}onPress = () => {
this.props.onChange(!this.props.turnedOn)
}getValue () {
return this.props.turnedOn
}// still need these methods even if they're empty
focus () { }
unfocus () {}
}
```In our form code we would then use it as such:
```js
const { name, isMale } = this.state
```
Your component will receive an `error` prop containing a validation error whenever validation fails. It is up to you if/when you make use of this.
_Note: There is a working example of a custom component (a dropdown using [react-native-modal-filter-picker](https://github.com/hiddentao/react-native-modal-filter-picker)) in the `demo` folder._
## Auto-scrolling to fields (ScrollView)
Sometimes you will need to place your form within a React Native `ScrollView` because it is longer than the height of the screen. In such cases the problem with auto-focussing fields is that the focussed field may not be visible in the current scroll area.
Thus, when a field receives focus it must be able to tell the `ScrollView` to scroll to it such that it's visible. The `Form.TextField` component already does this, as long as you pass in the `getParentScrollView` prop:
```js
{ this.scrollView = e; }}>
{ /* other stuff here */ }
this.scrollView} />
{ /* other stuff here */ }
this.scrollView} />
{ /* other stuff here */ }
```
Internally, `Form.TextField` uses `Form.utils.scrollToComponentInScrollView()` to actually perform the scroll. When building your own form component you can use this method too to achieve the same effect.
## License
[MIT](https://github.com/hiddentao/react-native-advanced-forms/raw/master/LICENSE.md)