How would you read the input value ?
On the reacjs site I see very complicated way !!
https://reactjs.org/docs/forms.html
I just want to read the value and submit it via ajax fetch() request. I.e. I don't need to manage bindings, events and such ...
The easiest way by far to read values from html controls is by using an event handler.
export default class myComponent extends Component {
person = {};
onChange = field => e => {
this.person[field] = e.target.value;
};
render() {
return (
<Input
id="firstName"
name="firstName"
autoComplete="firstName"
autoFocus
onChange={this.onChange('FirstName')}
/>
);
}
}
In the above code snippet we are basically telling react to fire the onChange member on an update of firstName control update. Our method will receive an event e, that has a handle to our control and we can basically probe it's value member to get what's typed in (much like jquery's $('#element').value()).
Why is it the easiest method? because it's generic enough to allow you to handle multiple inputs in a react component. Notice that, I'm also instructing React to pass me the control name in addition to the event, and using this method I can basically know exactly which of my inputs (in case of multiple) caused the event to fire.
Reading user input value is feasible and recommended via event handlers.
Below example would explain how to read input value and send it to the backend via fetch when Form is submitted
class Test extends Component{
constructor(props){
super(props);
this.state = {
name: “”
}
}
handleChange = event => {
this.setState({name: event.target.value});
}
handleSubmit = () => {
//send the value via fetch backend I.e., this.state.name
}
render(){
const { name } = this.state;
render(
<form onSubmit={this.handleSubmit}
<label>
Name:
<input type="text" value={name} onChange={this.handleChange} name="name" />
</label>
<input type="submit" value="Submit" />
</form>
)
}
}
Related
I run into the validation issue using multiple components decorated with same form name.
Let's say we have SimpleForm1 and SimpleForm2. When rendering Only SimpleForm1 with the name field validation works as expected, as well as when rendering SimpleForm2 with the surname field. But when rendering them both on a single page validation for SimpleForm1 is broken.
The question is how to avoid such behaviour and make both validation functions work.
Here is a fiddle which illustrates my problem
It's not a good idea to use same names for multiple forms.
As i understand you need to dynamically add form inputs(SimpleForm2 in your example) and have possibility to submit both forms with one button.
If yes, so you can add just an input to first form, you don't need second form.
Form:
const SimpleFormComponent1 = props => {
const {handleSubmit, pristine, reset, submitting, renderBoth} = props;
const onSubmit = (values) => {
alert(JSON.stringify(values));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
...
{
renderBoth &&
<Field
name="surname"
type="text"
component={renderField}
label="Surname"
validate={validateSurname}
/>
}
...
</form>
)
};
Render function:
render() {
const {renderBoth} = this.state;
return (
<div>
<div className='forms'>
<SimpleForm renderBoth={renderBoth}/>
</div>
<button
type='button'
onClick={this.handleClick}>
{renderBoth ? 'Show Single' : 'Show Both'}
</button>
</div>
);
}
Updated example
SOLUTION 1
I was having interference with invalid and valid props of the Redux Form because I wrote the validate function on ReduxForm() in every file of my main form component.
You haven't to change the form name to solve this. You have to put the validation function on the parent component.
Here is an example:
[CONTEXT]
I have 3 Components:
1. Main component to put the form ('editUserForm') elements (, , ...)
2. Field1 of the form ('editUserForm') that changes user's complete name.
3. Field2 of the form ('editUserForm') that changes user's email.
[SOLUTION]
MAIN COMPONENT:
Inside the main frame, you call reduxForm() (it creates a decorator with which you use redux-form to connect your form component to Redux. More info here Redux Form docs). Your code would be like this:
import...
class MainFrame ... {
...
<form ...>
<Field1 />
<Field2 />
</form>
...
}
const validate ({ name, email }, props) => {
errors={}
// Validation around Field1
if (name === ...) errors.name = "Name error passed to Field1 component";
// Validation around Field2
if (email === ...) errors.email= "Email error passed to Field2 component";
return errors;
}
...
export default reduxForm({
form: 'editUserForm',
validate // <--------- IMPORTANT: Only put this function on the parent component.
})(MainComponent);
FIELD1 & FIELD2 COMPONENTS:
This code is for the 2 children components. IMPORTANT: You call reduxForm() without validate Redux Form docs synchronous function.
import...
class MainFrame ... {
...
const { invalid } = this.props;
...
<inputs
error={invalid}
>
...
</input>
...
}
// IMPORTANT: Don't put the validation function in Field1 and Field2 components, because their local validations will interfere with the validation of the other Field component.
reduxForm({
form: 'editUserForm'
})
Now, the props: valid and invalid will work perfectly inside the children components (Field1 and Field2).
SOLUTION 2
User Redux Form FormSection (docs) to split forms into smaller components that are reusable across multiple forms.
I have a simple form as below:
some.component.html
<form class="example-form" novalidate (ngSubmit)="onSubmit()" autocomplete="off" [formGroup]="testform">
<input type="text" formControlName="name" class="form-control" placeholder="Enter name" required/>
<app-show-errors [control]="claimform.controls.name"></app-show-errors>
<button type="submit" (click)="onSubmit()">Next</button>
</form>
some.component.ts
ngOnInit() {
this.testform= new FormGroup({
name: new FormControl('', { validators: Validators.required})
}, {updateOn: 'submit'});
}
onSubmit() {
if (this.testform.valid) {
alert('saving data');
} else {
this._validationService.validateAllFormFields(this.testform);
}
}
validationService.ts
validateAllFormFields(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
} else if (control instanceof FormGroup) {
this.validateAllFormFields(control);
}
});
}
Reference
Problem
The form will validate on submit if left blank, but even after filling the value when I check this.testform.valid it returns false. But if I remove updateOn:'submit' on form then it validates on blur of input control and when value is entered it validates form return true. Not sure if updateOn is working fine or not or whether I've implemented this in a proper way. Could someone point me in the right direction.
in your HTML you have two calls to onSubmit() function, from submit button:
<button type="submit" (click)="onSubmit()">Next</button>
and from the form:
<form class="example-form"
ovalidate
(ngSubmit)="onSubmit()"
autocomplete="off"
[formGroup]="testform">
The first call to be triggered is the button's trigger, which actually does nothing in terms of updating your reactive form, since you set FormGroup's option to {updateOn: 'submit'}. The second call to be triggered is the form's trigger, which does actual form update.
Here is FormGroup directive config:
#Directive({
selector: '[formGroup]',
providers: [formDirectiveProvider],
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
exportAs: 'ngForm'
})
as we can see in host property DOM form's submit (triggered by hitting ENTER while focused within form or clicking form's submit button) will call onSubmit() function:
onSubmit($event: Event): boolean {
(this as{submitted: boolean}).submitted = true;
syncPendingControls(this.form, this.directives);
this.ngSubmit.emit($event);
return false;
}
which then will call syncPendingControls() function:
export function syncPendingControls(form: FormGroup, directives: NgControl[]): void {
form._syncPendingControls();
directives.forEach(dir => {
const control = dir.control as FormControl;
if (control.updateOn === 'submit' && control._pendingChange) {
dir.viewToModelUpdate(control._pendingValue);
control._pendingChange = false;
}
});
}
which updates a model at last.
So, in your case, just remove (click)="onSubmit()" from the submit button:
<button type="submit">Next</button>
also you do not need required DOM element property on your input, since you set it using Reactive Forms API validators: Validators.required and since you set your form to novalidate which cancels HTML5 form validation.
In my project we are building a form with React and Redux-Form. We have a single information that is composed by the value of two inputs. But the values of each input is combined and validated together.
The first implementation try was by connecting each component with Field. It let us update the state properly but we couldn't validate all the values together with the validate prop.
The second Try was using Fieldscomponent but it does not have a validate prop. We thought in create a Pull Request for it but the API for it isn't clear yet, since what we want to validate the combination of the two values and the behavior of the Fields props (such as parse and format) is different, executing the function for each input inside Fields component separately.
I know it is possible to create a component and use Field to connect with the application state, but I didn't want to manage things as the touched prop, or the callbacks to update the state, or other things that I even have noticed, since Redux-Form has all of it done.
The fact is that I end up with an implementation but it didn't looked very elegant. I'd like you to take a look at the implementation and give your opinion, sugest other solutions and even if this solution is not implemented in Redux-Form yet we could maybe open a pull request for that.
Here is an example implementation
Simple form container
import SimpleForm from 'app/simpleForm/components/simpleForm'
import { reduxForm } from 'redux-form'
export default reduxForm({
form: 'simpleForm'
})(SimpleForm)
Simple form component
import React from 'react'
import { Field } from 'redux-form'
import MultiInputText from 'app/simpleForm/components/multiInputText'
const onSubmit = values => alert(JSON.stringify(values))
const validateAddress = (value) => {
if (!value) return 'Address is empty'
if (!value.street) return 'Street is empty'
if (!value.number) return 'Number is empty'
return null
}
const SimpleForm = ({ handleSubmit }) => {
return (
<form onSubmit={ handleSubmit(onSubmit) }>
<Field label="Home Address" name="home" component={MultiInputText} type="text" validate={validateAddress}/>
<Field label="Work Address" name="work" component={MultiInputText} type="text" validate={validateAddress}/>
<button type="submit">Submit</button>
</form>
)
}
export default SimpleForm
MultiInputText component
import React from 'react'
import { Fields, FormSection } from 'redux-form'
const renderAddreessInputs = ({ street, number, error }) => (<div>
<input {...street.input} type="text" />
<input {...number.input} type="text" />
{ street.meta.touched && number.meta.touched && error && <span className="error">{error}</span> }
</div>)
const MultiInputText = (props) => {
const { input: { name }, label, meta: { error }} = props
const names = [
'street',
'number'
]
return (<div>
<label htmlFor={name}>{label}</label>
<FormSection name={name}>
<Fields names={names} component={renderAddreessInputs} error={error}/>
</FormSection>
</div>)
}
export default MultiInputText
I see two options:
1) Use record-level validation.
reduxForm({
form: 'addressForm',
validate: values => {
const errors = {}
if(!home) {
errors.home = 'Address is empty'
}
// etc, etc. Could reuse same code for home and work
return errors
}
})
2) Create a single input that handles a complex value.
<Field name="home" component="AddressInput"/>
...
const AddressInput = ({ input, meta }) =>
<div>
<input
name={`${input.name}.street`}
value={(input.value && input.value.street) || ''}
onChange={event => input.onChange({
...input.value,
street: event.target.value
})}/>
...other inputs here...
</div>
That's total pseudocode, but I hope it gets the point across: a single input can edit a whole object structure.
Personally, I'd choose Option 1, but I prefer record-level validation over field-level validation in general. The nice thing about Option 2 is that a single AddressInput could be reused across the application. The downside is that you don't get specific field-level focus/blur/dirty/pristine state.
Hope that helps...?
The redux-form asyncValidation is great but it only works on blur. Is it possible to make it happen during key presses, and throttled? So it runs only every 300ms and on final value?
Is it possible? The short answer is yes.
Is it tricky? Well, as you mentioned, theres the fact that the asyncValidation for redux-form option only works for the onBlur and you'd instead want it to work onChange.
So you could fork and add this feature into redux-form, so you can do this:
#reduxForm({
form: 'login',
asyncValidate: validator,
//asyncBlurFields: ['username','password'],
asyncChangeFields: ['username','password'], /* add this new option */
})
For the deboucing part, you'd want to somehow debounce the onChange handler rather than the validate function itself, which brings up another option...
redux-form exports its internal action creators which may be enough to hack it together. Particularly, there's the stopAsyncValidation action creator that lets you pass field-level async errors directly into a form. Pair that with the onChange prop for Field, and you actually have the right pieces to get it done like this:
import React from 'react'
import { Field, reduxForm, stopAsyncValidation } from 'redux-form'
import _debounce from 'lodash.debounce'
import renderField from './renderField'
#reduxForm({form: 'debouncedOnChangeValidation'})
class DebouncedOnChangeValidationForm extends React.Component {
customValidate = () => {
const { form, dispatch } = this.props
dispatch(stopAsyncValidation(form, { username: "thats wrong..." })) /* pass in async error directly! */
}
debounceValidate = _debounce(this.customValidate, 1000) /* decorate with debounce! */
render() {
const { handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="text"
component={renderField} label="Username"
onChange={this.debounceValidate} /* bind validation to onChange! */
/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
}
Additionally, to access the form values for performing validation, you'd need to use the getFormValues selector.
Of course this won't be as robust as a more built-in solution, but it may work well enough for some use cases.
i am trying to build a Ui component in Reactjs which combines a checkbox and a text input attched to it (instead of a text label) so that if the checkbox is checked , the user can change the text input , and if its unchecked the user will not be able to do so
the final goal is to render outside of the component all of textinputs valus which left checked as a list or as a menu item.
Its should look like this :
Checkbox with Text input
anyone knows how should i do this ? im new to reactjs and got a bit confused how to pass logic between two components(as in here between the checkbox and the text input and between the "combo" component and the outer rendered list) .
thanks in advance !
EDIT1:
well i managed to build the component but i cant make the children call the parent handler (handlerCheckbox , handlerInput)in order to actually make the magic happen.
anything im doing wrong ?
this is the child:
class CheckboxTxtInput extends React.Component{
constructor(props){
super(props);
console.log(props.isChecked)
}
handleCheckboxChild(e) {
this.props.handleCheckbox(e,this.props.id)
}
handleInputChild(e){
this.props.handleInput(e,this.props.id)
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckboxChild} defaultChecked={this.props.isChecked} />
<input type="text" value={this.props.inputValue} disabled={!this.props.isChecked} onChange={this.handleInputChild}/>
</div>
)
}
}
This is the parent:
export default class Text extends React.Component {
constructor(props) {
super(props);
this.state = {
textItems: [{id:0,inputValue:'text',isChecked:true},{id:1,inputValue:'text',isChecked:true}
,{id:2,inputValue:'text',isChecked:true},{id:3,inputValue:'text',isChecked:true}]
};
this.handleCheckbox = this.handleCheckbox.bind(this);
this.handleInput= this.handleInput.bind(this);
}
handleCheckbox(e,id) {
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].isChecked = e.target.value;
this.setState(stateCopy);
}
handleInput(e,id){
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].text = e.target.value;
this.setState(stateCopy);
}
render () {
return (
<div>
<hr className="divider-long"/>
<UI.sectionDividerLabeled label="Show/Hide Text"/>
<hr className="divider-long"/>
<p>Here you can show\hide your text</p>
<div>
<CheckboxTxtInput id={this.state.textItems[0].id} isChecked={this.state.textItems[0].isChecked}
inputValue={this.state.textItems[0].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox} />
<CheckboxTxtInput id={this.state.textItems[1].id} isChecked={this.state.textItems[1].isChecked}
inputValue={this.state.textItems[1].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[2].id} isChecked={this.state.textItems[2].isChecked}
inputValue={this.state.textItems[2].inputValue}
handleInput={this.handleInput} handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[3].id} isChecked={this.state.textItems[3].isChecked}
inputValue={this.state.textItems[3].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
</div>
<RenderText />
</div>
)
}
}
The simplest, React-like way to do this is to have a parent wrapper component - say LabeledCheckbox which contains your Text input and your Checkbox components.
When either of the child components do something, they call a callback provided by the parent, and the parent maintains the state for the two components, passing that state down into the props of both children.
The children in this case would never maintain their own state, instead simply calling callbacks and being prop-fed.
Create one component with checkbox and input field with the state of the checkbox and text field.
And then you can reuse it where you want.
You can do something like this :
class CheckboxTxtInput extends React.Component{
constructor(){
super();
this.state = {
checkbox: false,
inputValue: ""
}
}
handleCheckbox(e){
this.setState({checkbox: e.target.checked})
}
handleInput(e){
this.setState({inputValue: e.target.value})
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckbox.bind(this)} checked={this.state.checkbox}/>
<input type="text" value={this.state.inputValue} disabled={this.state.checkbox} onChange={this.handleInput.bind(this)}/>
</div>
)
}
}
class Test extends React.Component {
render(){
return (
<div><CheckboxTxtInput /></div>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is the fiddle.
Hope this helps.