From 5c243f223d59aab5d64bcd8ff3e2b8c6e9044c33 Mon Sep 17 00:00:00 2001 From: bietkul Date: Fri, 29 Dec 2017 00:05:13 +0530 Subject: [PATCH 01/12] Added RN switch handler --- index.d.ts | 2 +- src/model.js | 4 ++-- src/utils.js | 28 ++++++++++++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/index.d.ts b/index.d.ts index 27a14d4..2ba3e3b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,7 +4,7 @@ export type ValidationErrors = { [key: string]: any }; export type Status = 'VALID'|'INVALID'|'DISABLED'|'PENDING'; -export type InputType = 'checkbox'|'radio'; +export type InputType = 'checkbox'|'radio'|'switch'; export type Handler = { value: any; onChange: (e: any) => void; diff --git a/src/model.js b/src/model.js index 5207c28..760bf82 100644 --- a/src/model.js +++ b/src/model.js @@ -1,4 +1,4 @@ -import { toObservable, isEvent, getHandler } from './utils'; +import { toObservable, isEvent, getHandler, isReactNative } from './utils'; import Subject from "./observable"; import Validators from './validators'; @@ -49,7 +49,7 @@ function getControlValue(event) { } return event.target.value; default: - return event.target.value; + return isReactNative() ? event.nativeEvent.text : event.target.value; } } return event; diff --git a/src/utils.js b/src/utils.js index df12bc5..0e3c470 100644 --- a/src/utils.js +++ b/src/utils.js @@ -74,11 +74,18 @@ export const propsToBeMap = { } export const controlsToBeMap = { ReactNative: { - value: 'value', - onChange: 'onChange', - onBlur: 'onBlur', - editable: 'enabled', - disabled: 'disabled', + switch: { + value: 'value', + onValueChange: 'onChange', + onBlur: 'onBlur', + disabled: 'disabled', + }, + default: { + value: 'value', + onChange: 'onChange', + onBlur: 'onBlur', + editable: 'enabled', + } }, default: { value: 'value', @@ -87,16 +94,21 @@ export const controlsToBeMap = { disabled: 'disabled', } } -export const inputControls = isReactNative() ? controlsToBeMap.ReactNative : controlsToBeMap.default; +export const getAbsoluteValue = (value) => (value === undefined || value === null) ? "" : value; + +export const getInputControls = (inputType) => isReactNative() ? + controlsToBeMap.ReactNative[inputType] || controlsToBeMap.ReactNative.default : controlsToBeMap.default; + export function getHandler(inputType, value, control) { const controlObject = {}; + const inputControls = getInputControls(inputType); Object.keys(inputControls).forEach((key) => { let controlProperty = null; if(key === 'value') { if(control.updateOn !== "change") { - controlProperty = control._pendingValue || ""; + controlProperty = getAbsoluteValue(control._pendingValue); } else { - controlProperty = control.value || ""; + controlProperty = getAbsoluteValue(control.value); } } else { controlProperty = control[inputControls[key]]; From 6e565eb3afb1e29794ba009a269ca5f44b91bdfd Mon Sep 17 00:00:00 2001 From: bietkul Date: Fri, 29 Dec 2017 01:37:27 +0530 Subject: [PATCH 02/12] Added submitted property --- index.d.ts | 16 ++++++++++++---- src/Field.js | 3 +++ src/model.js | 28 ++++++++++++++++------------ 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/index.d.ts b/index.d.ts index 27a14d4..da9bb0d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -378,6 +378,10 @@ declare module "react-reactive-form" { * Length of the control array. */ length: number; + /** + * A form array is submitted if the `handleSubmit` event has been triggered on it. + */ + submitted: boolean; /** * Get the `AbstractControl` at the given `index` in the array. */ @@ -484,10 +488,10 @@ declare module "react-reactive-form" { * Submit action, can be used to tell the form that it has been submitted. * Useful when `updateOn` property is `submit`. * ``` - *
+ * * ``` */ - onSubmit():void + handleSubmit(): void } /** * Tracks the value and validity state of a group of `FormControl` @@ -563,6 +567,10 @@ declare module "react-reactive-form" { validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null, asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null) controls: {[key: string]: AbstractControl} + /** + * A form group is submitted if the `handleSubmit` event has been triggered on it. + */ + submitted: boolean; /** * Registers a control with the group's list of controls. * @@ -671,10 +679,10 @@ declare module "react-reactive-form" { * Submit action, can be used to tell the form that it has been submitted. * Useful when `updateOn` property is `submit`. * ``` - * + * * ``` */ - onSubmit():void + handleSubmit():void } /** * Tracks the value and validation status of an individual form control. diff --git a/src/Field.js b/src/Field.js index 04c2233..62381a9 100644 --- a/src/Field.js +++ b/src/Field.js @@ -30,6 +30,9 @@ export default class Field extends React.Component { // Remove Listener this.removeListener(); } + shouldComponentUpdate() { + return false; + } getComponent() { const { render, control } = this.props; if (control) { diff --git a/src/model.js b/src/model.js index 5207c28..5363264 100644 --- a/src/model.js +++ b/src/model.js @@ -646,13 +646,13 @@ export class FormControl extends AbstractControl { if(!this.dirty) { this.markAsDirty(); } if(!this.touched) { this.markAsTouched(); } this.setValue(this._pendingValue, { validate: true }); - } else if (!this.touched) { - if(this.updateOn === "submit" && !this._pendingTouched) { - this._pendingTouched = true - } else { - this.markAsTouched(); + } else if(this.updateOn === "submit") { + this._pendingTouched = true; + this._pendingDirty = true; + } else { + if(!this.dirty) { this.markAsDirty(); } + if(!this.touched) { this.markAsTouched(); } this.stateChanges.next(); - } } }; /** @@ -740,14 +740,16 @@ export class FormGroup extends AbstractControl { super(coerceToValidator(validatorOrOpts), coerceToAsyncValidator(asyncValidator, validatorOrOpts)); this.controls = controls; + this.submitted = false; this.validatorOrOpts = validatorOrOpts; - this.updateDOM = new Subject(); this._initObservables(); this._setUpdateStrategy(validatorOrOpts); this._setUpControls(); this.updateValueAndValidity({ onlySelf: true, emitEvent: false }); - this.onSubmit = () => { - this._syncPendingControls(); + this.handleSubmit = (e) => { + e.preventDefault(); + this.submitted = true; + if(!this._syncPendingControls()) { this.updateValueAndValidity({ validate: true }) } } } /** @@ -990,14 +992,16 @@ export class FormArray extends AbstractControl { coerceToValidator(validatorOrOpts), coerceToAsyncValidator(asyncValidator, validatorOrOpts)); this.controls = controls; + this.submitted = false; this.validatorOrOpts = validatorOrOpts; - this.updateDOM = new Subject(); this._initObservables(); this._setUpdateStrategy(validatorOrOpts); this._setUpControls(); this.updateValueAndValidity({onlySelf: true, emitEvent: false}); - this.onSubmit = () => { - this._syncPendingControls(); + this.handleSubmit = (e) => { + e.preventDefault(); + this.submitted = true; + if(!this._syncPendingControls()) { this.updateValueAndValidity({ validate: true }) } } } /** From bf5033074919108bbd105a26cb523966f98f528f Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 01:46:25 +0530 Subject: [PATCH 03/12] Update README.md --- README.md | 63 +------------------------------------------------------ 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/README.md b/README.md index e9e22bc..15de062 100644 --- a/README.md +++ b/README.md @@ -16,68 +16,6 @@ It's a library inspired by the [Angular's Reactive Forms](https://angular.io/gui npm install react-reactive-form --save ``` # Basic Example -```js -import React, { Component } from 'react'; -import { FormBuilder, Validators, reactiveForm } from "react-reactive-form"; - -// Create the controls -const loginForm = FormBuilder.group({ - username: ['', Validators.required], - password: ['', Validators.required], - rememberMe: false -}); - -class Login extends Component { - handleReset=(e) => { - loginForm.reset(); - e.preventDefault(); - } - handleSubmit=(e) => { - console.log("Form values", loginForm.value); - e.preventDefault(); - } - render() { - const { - username, - password, - rememberMe - } = this.props; - return ( - -
- - - {username.touched - && username.hasError('required') - && "Username is required"} - -
-
- - - {password.touched - && password.hasError('required') - && "Password is required"} - -
-
- -
- - - - ); - } -} -// React HOC to connect form with component. -export default reactiveForm(Login, loginForm); -``` - -### Note: -While working with larger forms, deep nested forms & [Form Array’s](docs/api/FormArray.md) it’s highly recommended to use the [Field](docs/api/Field.md) component instead of `reactiveForm` method. - -`Field` component subscribes a particular control & only update it when it’s or it’s parent’s state changes, which of course reduces the re-rendering and boost the performance significantly. - ```js import React, { Component } from 'react'; @@ -169,6 +107,7 @@ Try out `react-reactive-forms` in these sandbox versions of the Examples. * [Sync & Async Validation](https://codesandbox.io/s/qq8xq7j2w) * [User Registeration Form With Nested Forms](https://codesandbox.io/s/p2rqmr8qk7) * [Form Array With Dynamic Controls](https://codesandbox.io/s/nw9wxw2nvl) +* [Update On Submit](https://codesandbox.io/s/3qk1ly16j1) Let's make React Reactive Forms better! If you're interested in helping, all contributions are welcome and appreciated. From 3f4f7276d347bde7dd1682f867e64dd21cc6aaf9 Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 01:55:56 +0530 Subject: [PATCH 04/12] Update GettingStarted.md --- docs/GettingStarted.md | 108 +++++------------------------------------ 1 file changed, 13 insertions(+), 95 deletions(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index ace90e5..6d79ee2 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -5,22 +5,6 @@ The basic implementation of reactive forms is super easy but it may be helpful t * [Form Array](api/FormArray.md) * [Form Control](api/FormControl.md) * [Form Builder](api/FormBuilder.md) -## Overview -There are two ways to connect your components to reactive-form. - -### By using `reactiveForm` -You can use the [`reactiveForm`](api/ReactiveForm.md) method. It returns a higher order component -which regulary provides control(mapped) props to your component. -```ts -reactiveForm(ReactComponent: React.SFC|React.ComponentClass, form: FormGroup|FormArray):React.ComponentClass -``` - -### By using `Field` ( recommended ) - -For better performance with large forms & [Form Array’s](api/FormArray.md) it’s highly recommended to use the [Field](api/Field.md) component instead of `reactiveForm` method. - -`Field` component subscribes a particular control & only update it when it’s or it’s parent’s state changes, which of course reduces the re-rendering and boost the performance significantly. - ## Basic Usage Guide ### step 1: Create FormGroup or FormArray @@ -58,100 +42,34 @@ const loginForm = new FormGroup({ ``` ### step2: Connect form with component - -### With `reactiveForm` - -Use the `reactiveForm` method to connect your form group or array to the Component in which you want to use input handlers. -Now you'll start receiving the [mapped control props](api/Props.md) with input handlers. - -In below given example `username.handler` is a function which binds the input element to the `username` control. - -```js -import React, { Component } from 'react'; -import { FormBuilder, Validators, reactiveForm } from "./react-reactive-form"; - -// Create the controls -const loginForm = FormBuilder.group({ - username: ['', Validators.required], - password: ['', Validators.required], - rememberMe: false -}); - -class Login extends Component { - handleReset=(e) => { - loginForm.reset(); - e.preventDefault(); - } - handleSubmit=(e) => { - console.log("Form values", loginForm.value); - e.preventDefault(); - } - render() { - const { - username, - password, - rememberMe - } = this.props; - return ( -
-
- - - {username.touched - && username.hasError('required') - && "Username is required"} - -
-
- - - {password.touched - && password.hasError('required') - && "Password is required"} - -
-
- -
- - -
- ); - } -} -// React HOC to connect form with component. -export default reactiveForm(Login, loginForm); - -``` - -### With `Field` -[Field](api/Field.md) subscribes the component to a particular control's state changes which improves the performance by restricting the re-rendering of other fields. +[Field](api/Field.md) component subscribes a particular control & only update it when it’s or it’s parent’s state changes, which improves the performance by restricting the unnecessary re-rendering of other fields. ```js import React, { Component } from 'react'; import { FormBuilder, Validators, Field } from "react-reactive-form"; -import { AbstractControl } from "react-reactive-form"; - -// Create the controls -const loginForm = FormBuilder.group({ - username: ["", Validators.required], - password: ["", Validators.required], - rememberMe: false -}); export default class Login extends Component { + constructor(props) { + super(props); + // Create the controls + this.loginForm = FormBuilder.group({ + username: ["", Validators.required], + password: ["", Validators.required], + rememberMe: false + }); + } handleReset=(e) => { - loginForm.reset(); + this.loginForm.reset(); e.preventDefault(); } handleSubmit=(e) => { - console.log("Form values", loginForm.value); + console.log("Form values", this.loginForm.value); e.preventDefault(); } render() { return ( (
Date: Fri, 29 Dec 2017 01:57:39 +0530 Subject: [PATCH 05/12] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 15de062..acb1e59 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ npm install react-reactive-form --save ```js import React, { Component } from 'react'; import { FormBuilder, Validators, Field } from "react-reactive-form"; -import { AbstractControl } from "react-reactive-form"; export default class Login extends Component { constructor(props) { From 92eeebfcc42e69371722c4bfcd5d9ee46f8d0e11 Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 02:01:48 +0530 Subject: [PATCH 06/12] Added Submitted Property --- docs/api/FormGroup.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/api/FormGroup.md b/docs/api/FormGroup.md index 202afe0..9e1a6c5 100644 --- a/docs/api/FormGroup.md +++ b/docs/api/FormGroup.md @@ -67,6 +67,11 @@ controls: { ``` ## ```ts +submitted: boolean +``` +A form is submitted if the `handleSubmit` event has been triggered on it. +## +```ts registerControl(name: string, control: AbstractControl): AbstractControl ``` Registers a control with the group's list of controls. @@ -187,14 +192,14 @@ Otherwise, the value property is the best way to get the value of the group. ## ```ts -onSubmit():void +handleSubmit():void ``` Submit action, can be used to tell the form that it has been submitted. -Useful when `updateOn` property is `submit`. +Useful when `updateOn` property is `handleSubmit`. Example ```ts - + ```

From 0783fee57be24ea1fbffe99747facb4a1b8b811f Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 02:04:28 +0530 Subject: [PATCH 07/12] Update FormGroup.md --- docs/api/FormGroup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/FormGroup.md b/docs/api/FormGroup.md index 9e1a6c5..554cf37 100644 --- a/docs/api/FormGroup.md +++ b/docs/api/FormGroup.md @@ -69,7 +69,7 @@ controls: { ```ts submitted: boolean ``` -A form is submitted if the `handleSubmit` event has been triggered on it. +A form is submitted if the `handleSubmit` function has been called on it. ## ```ts registerControl(name: string, control: AbstractControl): AbstractControl From 6b281d5b08c3503364669f29bea57a2f59bbd587 Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 02:05:13 +0530 Subject: [PATCH 08/12] Update FormArray.md --- docs/api/FormArray.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/api/FormArray.md b/docs/api/FormArray.md index 9a7493a..aa52d9b 100644 --- a/docs/api/FormArray.md +++ b/docs/api/FormArray.md @@ -59,6 +59,11 @@ controls: AbstractControl[] ``` ## ```ts +submitted: boolean +``` +A form is submitted if the `handleSubmit` function has been called on it. +## +```ts at(index: number): AbstractControl ``` Get the [AbstractControl](AbstractControl.md) at the given index in the array. @@ -177,14 +182,14 @@ Otherwise, the `value` property is the best way to get the `value` of the array. ## ```ts -onSubmit():void +handleSubmit():void ``` Submit action, can be used to tell the form that it has been submitted. Useful when `updateOn` property is `submit`. Example ```ts - + ```

Note: This document is a derivative of ["Form Array Document"](https://angular.io/api/forms/FormArray) by Google, From d21dec1b84984379c7a3b766132426648271725f Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 02:22:39 +0530 Subject: [PATCH 09/12] Added Switch Handler Property --- docs/api/FormControl.md | 106 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/docs/api/FormControl.md b/docs/api/FormControl.md index efc87a9..6150f24 100644 --- a/docs/api/FormControl.md +++ b/docs/api/FormControl.md @@ -118,9 +118,111 @@ Function needs to be called whenever a blur event triggers. ```ts handler: (inputType?: InputType, value?: string) => Handler; ``` -Returns the props required to bind a control with an input element. +Returns the props required to bind a control to a native input element. -For more details see the handler section of [props](Props.md). +Note: +* `inputType` parameter is required for `checkbox`, `radio` and `switch`( React Native ) components. +* `value` parameter only can be used for `radio` buttons to assign a value to a particular button. + +Example + +```ts + +``` +Binds a `text` input. +## +```ts + +``` +Binds a `date` input. +## +```ts + +``` +Binds a `checkbox` input. +## +```ts + Male + Female + Other +``` +Binds a `radio` input. +## +```ts + +``` +Binds a React Native `TextInput` component. +## +```ts + +``` +Binds a React Native `Switch` component. + +A `handler` object can have these properties: + +```ts +value: any; +``` +Sometimes this value can be different from the actual value of `control`. + +For example, if the `updateOn` property is `blur` or `submit` than the value property of handler will be `_pendingValue` +of the control. + +The `_pendingValue` is the value of a control which is not validated yet which means the actual value of the +control is different. + +So, this `value` is just to control the input element, for actual value of the control you can use the `value` property +of the mapped control prop. +## +```ts +onChange: (e: any) => void; +``` +Function needs to be called whenever a value change event triggers. +## +```ts +onBlur: (e: any) => void; +``` +Function needs to be called whenever a `blur` event triggers. +## +```ts +disabled: boolean; +``` +Tells the input element about the `disabled` status. +## +```ts +checked?: boolean; +``` +Checked property for `checkbox` and `radio` buttons. +## +```ts +editable?: boolean; +``` +React Native uses `editable` property to tell the `TextInput` about the `enabled` status. +## +```ts +type?: string; +``` +Returns the type of input element in case of `checkbox` & `radio` buttons. + + +Although `handler` works well with all kind of inputs, you can also bind your custom input +components. + +Example + +```ts + ( + + )} +``` + +Binds a React Native `DatePickerIOS` component. Note: This document is a derivative of ["Form Control Document"](https://angular.io/api/forms/FormControl) by Google, under [CC BY](https://creativecommons.org/licenses/by/4.0/). From 3a126d31a671da28e2aa57da4ebad5d0746a3d2b Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 02:23:37 +0530 Subject: [PATCH 10/12] Update FormControl.md --- docs/api/FormControl.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/FormControl.md b/docs/api/FormControl.md index 6150f24..ba56d89 100644 --- a/docs/api/FormControl.md +++ b/docs/api/FormControl.md @@ -220,6 +220,7 @@ Example onDateChange={onChange} /> )} + /> ``` Binds a React Native `DatePickerIOS` component. From 5a4c91b562b0951f0a63aff7a481304486e1b3bc Mon Sep 17 00:00:00 2001 From: Kuldeep Saxena Date: Fri, 29 Dec 2017 02:25:03 +0530 Subject: [PATCH 11/12] Update FormControl.md --- docs/api/FormControl.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/FormControl.md b/docs/api/FormControl.md index ba56d89..9373451 100644 --- a/docs/api/FormControl.md +++ b/docs/api/FormControl.md @@ -158,6 +158,7 @@ Binds a React Native `TextInput` component. ``` Binds a React Native `Switch` component. +## A `handler` object can have these properties: ```ts From c0d28e53781356730ea80b9ae2f7d33ca16fb565 Mon Sep 17 00:00:00 2001 From: bietkul Date: Fri, 29 Dec 2017 02:43:37 +0530 Subject: [PATCH 12/12] version update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b22c384..11937cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-reactive-form", - "version": "1.0.7", + "version": "1.0.8", "description": "Angular like Reactive Forms in React", "keywords": [ "forms",