In Redux, how to get user input - forms

I have a form, how to get the use input in the handleSubmit() method?
handleSubmit(e) {
e.preventDefault()
//how to get the user input?
}
render() {
return (
<div className="col-sm-4">
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="user"/>
<input type="text" placeholder="comments"/>
<input type="submit" hidden/>
</form>
</div>
)
}
so far, I know three solutions:
The first one, use refs, but I can see there are lots of people saying that we should avoid using it
The second one, add onChange() to each <input>, e.g.
class Example extends React.Component {
state = {
inputValue: ""
};
handleInputChanged(e) {
this.setState({
inputValue: e.target.value
});
}
render() {
return (
<div>
<input onChange={this.handleInputChanged.bind(this)} value={this.state.inputValue}>
</div>
);
}
}
this one is fine with a few inputs. But if the form has 20 input fields, then there are 20 different onChange methods?
third, use some npm module, like redux-form.
any other suggestion? Thanks

You can actually just do an onChange on the parent form like so:
onChange(e) {
switch(e.target.type) {
case 'checkbox':
this.setState({ [e.target.name]: e.target.checked });
break;
default:
this.setState({ [e.target.name]: e.target.value });
break;
}
}
// in render
<form onChange={this.onChange.bind(this)}>
<input name="foo1" />
<input name="foo2" />
<input name="foo3" />
<input name="foo4" />
<input name="foo5" />
<input name="foo6" />
<input name="foo7" />
<input name="foo8" />
</form>

There are certain libraries like https://github.com/christianalfoni/formsy-react, https://github.com/prometheusresearch/react-forms. These forms have additional functions pre written for form submitting, validations. I think using refs is a tedious and unwanted task if the form is big with the reason being that if it is controlled form you need to access the state value for controlled components which brings unnecessary complications. You can do it but it is better to use prewritten libraries.

Related

react rerendering form causes focus/blur issue on state change

I have a form in a react component that has two change handlers, one for my two draftjs textareas, and one for my other text inputs:
onChangeEditor = (editorStateKey) => (editorState) => {
this.setState({ [editorStateKey]: editorState });
}
handleInputChange(event) {
event.preventDefault();
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
In my render method I have two views that I switch between depending on which view mode I am in, read or edit:
render () {
const Editable = () => (
<div className="editor">
<form className="editor-inner">
<h3>Redigerar: Anbudsbrev</h3>
<h4>Rubrik</h4>
<input type="text" key="text01" name="title" defaultValue={this.state.title} onBlur={this.handleInputChange} />
<h4>Text 1</h4>
<RichEditor editorState={this.state.editorState1} onChange={this.onChangeEditor('editorState1')} name="text01"/>
<h4>Citat</h4>
<input type="text" key="text02" name="quote01" defaultValue={this.state.quote01} onBlur={this.handleInputChange} />
<h4>Text 2</h4>
<RichEditor editorState={this.state.editorState2} onChange={this.onChangeEditor('editorState2')} name="text02" />
<EditorFooter {...this.props} submitForm={this.saveForm} />
</form>
</div>
);
const Readable = () => (
<div>
<h1 className="header66">{this.state.title}</h1>
<div className="text66">{this.state.text01}</div>
<div className="quote100">{this.state.quote01}</div>
<div className="text66">{this.state.text02}</div>
</div>
);
return (
<div>
{ this.props.isInEditMode ? <Editable /> : <Readable /> }
</div>
);
}
When I switch between inputs in my browser I have to click twice in order to get the focus on the right input.
I suspect that this is because a change is triggered on the "blur" event of each input, causing the form to rerender because state is changed. And when the form rerenders, it goes through the { this.props.isInEditMode ? <Editable /> : <Readable /> } which causes the input to lose focus.
The problem is that I don't know how to get around this.
I solved it myself.
It turns out that it was not a good idea to place the Editable and Readable inside of my component as I did. Instead I moved them out to their own components, and it works properly now.
class Editable extends React.Component {
render() {
return (
<div className="editor">
<form className="editor-inner">
<h3>Redigerar: Anbudsbrev</h3>
<h4>Rubrik</h4>
<input type="text" name="title" defaultValue={this.props.title} onChange={this.props.handleInputChange} />
<h4>Text 1</h4>
<RichEditor editorState={this.props.editorState1} onChange={this.props.onChangeEditor('editorState1')} name="text01" />
<h4>Citat</h4>
<input type="text" name="quote01" defaultValue={this.props.quote01} onChange={this.props.handleInputChange} />
<h4>Text 2</h4>
<RichEditor editorState={this.props.editorState2} onChange={this.props.onChangeEditor('editorState2')} name="text02" />
<EditorFooter {...this.props} submitForm={this.props.saveForm} />
</form>
</div>
);
}
};
class Readable extends React.Component {
render() {
return (
<div>
<h1 className="header66">{this.props.title}</h1>
<div className="text66">{this.props.text01}</div>
<div className="quote100">{this.props.quote01}</div>
<div className="text66">{this.props.text02}</div>
</div>
);
}
};

Proper way of clearing forms without redux-form

This is my form
<form className="input-group" onSubmit={this.handleSubmit}>
<input className="form-control"
type="text"
placeholder="Insert name"
autofocus="true" />
<span className="input-group-btn">
<button type="submit" className={classNames}>Add</button>
</span>
</form>
This is my event handler:
handleSubmit(e) {
e.preventDefault();
let name = e.target[0].value;
if (name.length > 0) {
this.props.dispatch(createClassroom(name));
}
}
My question is:
what's the proper "redux way" to clearing the form after submitting it?
Do I need to dispatch a different action or should I use the existing createClassroom action?
Note: I'd rather not use redux-form package.
First, you have to make sure that the <input> is a controlled component by passing its respective value from the state:
const { classroom } = this.props;
// in return:
<input type="text" value={ classroom.name } />
Then, the form can be cleared by ideally submitting a RESET action that your classroom reducer acts upon:
const initialState = {};
function classroomReducer(state = initialState, action) {
switch (action.type) {
// ...
case 'RESET_CLASSROOM':
return initialState;
default:
return state;
}
}

Why doesn't my React Js form accept user input?

I have a simple AddUser component and in the render function I am returning the following html:
<form ref="form" className="users-form" onSubmit={ this.handleAddNew }>
<input ref="username" type="text" name="username" placeholder="username"
value={this.state.username} onChange={function() {}} /><br />
<input ref="email" type="email" name="email" placeholder="email"
value={this.state.email} onChange={function() {}} /><br />
<button type="submit"> Add User </button>
</form>
I am binding the state of username and email to this.state which I am setting to blank in getInitialState like so:
getInitialState() {
return { username: '', email: '' };
}
I am binding state to the form so I can set it to blank after form submission.
The problem with this setup is that the form now renders as readonly.
I cannot get any user input into either text fields. What am I doing wrong?
Your input fields are controlled components, since you are using the value property. This makes the inputs readonly and they will always reflect the value, the variable (in this case, the state variable) holds. You have to explicitly setState onChange since you are setting username field as a state variable.
Read more about it here
onUserNameChange : function(e){
this.setState({username : e.target.value})
},
render: function(){
return ...
<input ref="username" type="text" name="username" placeholder="username"
value={this.state.username} onChange={this.onUserNameChange} /><br />
...
<button type="submit"> Add User </button>
</form>
}
A better way to do this is :
onChange : function(field,e){
this.setState({field: e.target.value});
},
render : function(){
return <form ref="form" className="users-form" onSubmit={ this.handleAddNew }>
<input ref="username" type="text" name="username" placeholder="username"
value={this.state.username} onChange={this.onChange.bind(this,"username")} /><br />
<input ref="email" type="email" name="email" placeholder="email"
value={this.state.email} onChange={this.onChange.bind(this,"email")} /><br />
<button type="submit"> Add User </button>
</form>
}
It looks like you saw the console warning about controlled fields needing an onChange handler and added one just to shut the warning up :)
If you replace your empty onChange handler functions with onChange={this.handleChange} and add this method to your component, it should work:
handleChange(e) {
this.setState({[e.target.name]: e.target.value})
}
(Or for people not using an ES6 transpiler:)
handleChange: function(e) {
var stateChange = {}
stateChange[e.target.name] = e.target.value
this.setState(stateChange)
}
However, if your component is an ES6 class extending React.Component (instead of using React.createClass()), you will also need to ensure the method is bound to the component instance properly, either in render()...
onChange={this.handleChange.bind(this)}
...or in the constructor:
constructor(props) {
super(props)
// ...
this.handleChange = this.handleChange.bind(this)
}

Disable submit button when form invalid with AngularJS

I have my form like this:
<form name="myForm">
<input name="myText" type="text" ng-model="mytext" required />
<button disabled="{{ myForm.$invalid }}">Save</button>
</form>
As you may see, the button is disabled if the input is empty but it doesn't change back to enabled when it contains text. How can I make it work?
You need to use the name of your form, as well as ng-disabled: Here's a demo on Plunker
<form name="myForm">
<input name="myText" type="text" ng-model="mytext" required />
<button ng-disabled="myForm.$invalid">Save</button>
</form>
To add to this answer. I just found out that it will also break down if you use a hyphen in your form name (Angular 1.3):
So this will not work:
<form name="my-form">
<input name="myText" type="text" ng-model="mytext" required />
<button ng-disabled="my-form.$invalid">Save</button>
</form>
Selected response is correct, but someone like me, may have issues with async validation with sending request to the server-side - button will be not disabled during given request processing, so button will blink, which looks pretty strange for the users.
To void this, you just need to handle $pending state of the form:
<form name="myForm">
<input name="myText" type="text" ng-model="mytext" required />
<button ng-disabled="myForm.$invalid || myForm.$pending">Save</button>
</form>
If you are using Reactive Forms you can use this:
<button [disabled]="!contactForm.valid" type="submit" class="btn btn-lg btn primary" (click)="printSomething()">Submit</button>
We can create a simple directive and disable the button until all the mandatory fields are filled.
angular.module('sampleapp').directive('disableBtn',
function() {
return {
restrict : 'A',
link : function(scope, element, attrs) {
var $el = $(element);
var submitBtn = $el.find('button[type="submit"]');
var _name = attrs.name;
scope.$watch(_name + '.$valid', function(val) {
if (val) {
submitBtn.removeAttr('disabled');
} else {
submitBtn.attr('disabled', 'disabled');
}
});
}
};
}
);
For More Info click here
<form name="myForm">
<input name="myText" type="text" ng-model="mytext" required/>
<button ng-disabled="myForm.$pristine|| myForm.$invalid">Save</button>
</form>
If you want to be a bit more strict

Angular form validation: ng-show when at least one input is ng-invalid and ng-dirty

I have the following form in an Angular partial:
<form name="submit_entry_form" id="submit_entry_form" ng-submit="submit()" ng-controller="SubmitEntryFormCtrl" novalidate >
<input type="text" name="first_name" ng-model="first_name" placeholder="First Name" required/><br />
<input type="text" name="last_name" ng-model="last_name" placeholder="Last Name" required/><br />
<input type="text" name="email" ng-model="email" placeholder="Email Address" required/><br />
<input type="text" name="confirm_email" ng-model="confirm_email" placeholder="Confirm Email Address" required/><br />
<span ng-show="submit_entry_form.$invalid">Error!</span>
<input type="submit" id="submit" value="Submit" />
</form>
The trouble I'm having is with the span at the bottom that says "Error!". I want this to show ONLY if one of the inputs is both "ng-dirty" and "ng-invalid". As it is above, the error will show until the form is completely valid. The long solution would be to do something like:
<span ng-show="submit_entry_form.first_name.$dirty && submit_entry_form.first_name.$invalid || submit_entry_form.last_name.$dirty && submit_entry_form.last_name.$invalid || submit_entry_form.email.$dirty && submit_entry_form.email.$invalid || submit_entry_form.confirm_email.$dirty && submit_entry_form.confirm_email.$invalid">Error!</span>
Which is UGLY. Any better way to do this?
Method 1: Use a function on $scope set up by your controller.
So with a better understanding of your problem, you wanted to show a message if any field on your form was both $invalid and $dirty...
Add a controller method:
app.controller('MainCtrl', function($scope) {
$scope.anyDirtyAndInvalid = function (form){
for(var prop in form) {
if(form.hasOwnProperty(prop)) {
if(form[prop].$dirty && form[prop].$invalid) {
return true;
}
}
}
return false;
};
});
and in your HTML:
<span ng-show="anyDirtyAndInvalid(submit_entry_form);">Error!</span>
Here is a plunker to demo it
Now all of that said, if someone has entered data in your form, and it's not complete, the form itself is invalid. So I'm not sure this is the best usability. But it should work.
Method 2: Use a Filter! (recommended)
I now recommend a filter for this sort of thing...
The following filter does the same as the above, but it's better practice for Angular, IMO. Also a plunk.
app.filter('anyInvalidDirtyFields', function () {
return function(form) {
for(var prop in form) {
if(form.hasOwnProperty(prop)) {
if(form[prop].$invalid && form[prop].$dirty) {
return true;
}
}
}
return false;
};
});
<span ng-show="submit_entry_form | anyInvalidDirtyFields">Error!</span>