Handling Radio Buttons in Forms with React (Rails 5.1) - javascript

I'm having difficulty understanding how to handle radio buttons in forms with other types of inputs in React. I'm working from an example that incorporates string type input fields and another for pure radio buttons, but not sure how to get them to play nice together. The text field inputs work fine, but I can't get the radio button feature to work.
The basic theory behind the radio button of the React portion of this form is the checkbox for each option is set when a user clicks on a button and the checked value for that particular button is set to true via a logic check, while the other radio buttons are set to false.
I'm not sure how to integrate the handling of the input fields with the radio buttons.
Any guidance on this would be great!
Here's my form jsx:
class Project_form extends React.Component {
handleChange(e) {
const name = e.target.name;
const obj = {};
obj[name] = e.target.value;
this.props.onUserInput(obj);
}
handleOptionChange(e) {
const name = e.target.id;
const obj = {};
obj[name] = e.target.checked;
this.props.onChangeInput(obj);
}
handleSubmit(e) {
e.preventDefault();
this.props.onFormSubmit();
}
render() {
return (
<form onSubmit={(event) => this.handleSubmit(event)} >
<div className="form-group">
<input
id="project_name"
className="form-control"
type="text"
name="project_name"
placeholder="Enter Your Project Name"
value={this.props.project_name}
onChange={(event) => this.handleChange(event)} />
</div>
<div className="form-group">
<input
id="project_zipcode"
className="form-control"
type="text"
name="project_zipcode"
placeholder="Zipcode"
value={this.props.project_zipcode}
onChange={(event) => this.handleChange(event)} />
</div>
<div className="form-group">
<label>How Soon Do You Want this Project Started?</label>
<div className="radio">
<p><input type="radio"
value="1-2 Weeks"
name="project_timeframe"
id="project_timeframe_1-2_weeks"
checked={this.props.project_timeframe === 'ASAP'}
onChange={(event) => this.handleOptionChange(event)} />
<label>As Soon As Possible</label></p>
<p><input type="radio"
value="2-4 Weeks"
name="project_timeframe"
id="project_timeframe_2-4_weeks"
checked={this.props.project_timeframe === '2-4 Weeks'}
onChange={(event) => this.handleOptionChange(event)} />
<label>2-4 Weeks</label></p>
<p><input type="radio"
value="4-6 Weeks"
name="project_timeframe"
id="project_timeframe_4-6_weeks"
checked={this.props.project_timeframe === '4-6 Weeks'}
onChange={(event) => this.handleOptionChange(event)} />
<label>4-6 Weeks</label></p>
<p><input type="radio"
value="More Than 6 Weeks"
name="project_timeframe"
id="project_timeframe_more_than_6_weeks"
checked={this.props.project_timeframe === 'More Than 6 Weeks'}
onChange={(event) => this.handleOptionChange(event)} />
<label>More Than 6 Weeks</label></p>
</div>
</div>
<div className="form-group">
<input
type="submit"
value="Create Project"
className="btn btn-primary btn-lg"
/>
</div>
</form>
)
}
}
Here's my main component, which sets the state of the app.
class Projects extends React.Component {
constructor(props) {
super(props)
this.state = {
projects: this.props.projects,
project_name: '',
project_zipcode: '',
selectedTimeframeOption: 'ASAP'
}
}
handleUserInput(obj) {
this.setState(obj);
}
handleChangeInput(obj) {
this.setState({
selectedOption: obj.target.value
});
}
handleFormSubmit() {
const project = {
name: this.state.project_name,
zipcode: this.state.project_zipcode,
timeframe: this.state.project_timeframe
};
$.post('/projects',
{project: project})
.done((data) => {
this.addNewProject(data);
});
}
addNewProject(project){
const projects = update(this.state.projects, { $push: [project]});
this.setState({
projects: projects.sort(function(a,b){
return new Date(a['updated_at']) - new Date(b['updated_at']);
})
});
}
render() {
return (
<div>
<h2>Start a New Project</h2>
<a href="/projects/new/"
className="btn btn-large btn-success">Create a New Project</a>
{/* <%= link_to "Create a New Project", new_project_path, class: "btn btn-large btn-success" %> */}
<Project_form
project_name={this.state.project_name}
project_zipcode={this.state.project_zipcode}
project_timeframe={this.state.selectedTimeframeOption}
onUserInput={(obj) => this.handleUserInput(obj)}
onFormSubmit={() => this.handleFormSubmit()} />
{/* <% if #current_user.projects.any? %> */}
<div className="col-md-12">
<h3>Existing Projects</h3>
<div className="table-responsive">
<Project_list projects={this.state.projects} />
</div>
</div>
</div>
)
}
}

Ok, figured it out. Had to play around with getting the event object into the stateful component.
My form file now has this event handler added:
handleOptionChange(e) {
const option = e.target.name;
const obj = {};
obj[option] = e.target.value;
this.props.onUserInput(obj);
}
Then this.props.onUserInput(obj); gets called by the existing method in the Project.jsx file and I got rid of the handleChangeInput method.
handleUserInput(obj) {
this.setState(obj);
}
The state then flows down to the Project_form Component here:
<Project_form
project_name={this.state.project_name}
project_zipcode={this.state.project_zipcode}
project_timeframe={this.state.project_timeframe}
onUserInput={(obj) => this.handleUserInput(obj)}
onFormSubmit={() => this.handleFormSubmit()} />

Related

Displaying a Form From OnClick Button

I am trying to display a form as a popup when I click on a button. I am new to React to I don't really know where to start.
This is the React Component I want to display:
import React, {useState} from 'react';
const initialFormValues = {
firstName: '',
lastName: '',
email: '',
phone: '',
message: ''
}
export default function Contact(){
//set up state
const [contacts, setContacts] = useState([]);
const [formValues, setFormValues] = useState(initialFormValues);
//helper function to trach changes in the input
const inputChange= (name, value) => {
setFormValues({...formValues, [name]: value})
}
const onChange = e => {
const {name, value} = e.target;
inputChange(name, value);
}
//post a new contact to the backend eventually, right now just display the contact to the DOM
const postNewContact = newContact => {
setContacts([newContact, ...contacts])
setFormValues(initialFormValues);
}
const formSubmit = () => {
const newContact = {
firstName: formValues.firstName.trim(),
lastName: formValues.lastName.trim(),
email: formValues.email.trim(),
phone: formValues.phone.trim(),
message: formValues.message.trim()
}
postNewContact(newContact)
}
const onSubmit = e => {
e.preventDefault();
formSubmit();
}
return (
<div className='contact container'>
<h1>This is the contact component</h1>
<form id='contact-form' onSubmit={onSubmit}>
<div className='form-group submit'>
<button id='contact-btn'>SUBMIT CONTACT INFO</button>
</div>
<div className='form-group inputs'>
<label>
First Name:
<input
type='text'
value={formValues.firstName}
onChange={onChange}
name="firstName"
/>
</label>
<label>
Last Name:
<input
type='text'
value={formValues.lastName}
onChange={onChange}
name="lastName"
/>
</label>
<label>
Email:
<input
type='email'
value={formValues.email}
onChange={onChange}
name="email"
/>
</label>
<label>
Phone Number:
<input
type='text'
value={formValues.phone}
onChange={onChange}
name="phone"
/>
</label>
<label>
Message:
<input
type='text'
value={formValues.message}
onChange={onChange}
name="message"
/>
</label>
</div>
</form>
</div>
)
}
I want to display this component as a popup when I click on a "Contact Us" link:
import React from 'react';
import './footer.css'
function Footer()
{
return (
<div className="Footer">
<div className="container">
<div className="row">
<div className="col">
<h4 className="block ">About Us</h4>
</div>
<div className="col">
<h4 className="block" >Contact</h4>
</div>
<div className="col">
<h4><a className="block" href={"https://www.cdc.gov/coronavirus/2019-ncov/faq.html"}>COVID-19 FAQ</a></h4>
</div>
<div className="col">
<h4 className="block"><a className="block" href={"https://www.cdc.gov/coronavirus/2019-ncov/if-you-are-sick/quarantine.html"}>CDC Guidelines</a></h4>
</div>
</div>
</div>
</div>
);
}
export default Footer;
How would I accomplish this? I want the popup to display on click with the following form, and I want the popup to disappear when the user clicks the submit button.
You can either create a popup using HTML + CSS + Javascript as in the link
Custom Popup or use any UI framework like Ant Design and make use of the Modal component.
btn.onclick = function() {
modal.style.display = "block";
}
In react, components are controlled by states in general.
What you will need for toggling the contact form visibility is:
state / setState for the contact form visibility (true / false)
function that triggers state change of the contact form visibility
set the function above at a button with onClick property
With the visibility state, you can set a conditional statement whether the contact form will set the style of display: none or block.

Adding array elements through multiple form fields in React

I am working on a quiz app project to learn to react. I came across a situation where I need to store incorrect options in a quiz question in an array. And later pass over the information to the database.
This is an example JSON format.
{
incorrect_answers:["Jeff Bezos","Satya Nadela","Bill Gates"] }
The incorrect answer is an array and the value needs to be inputted through separate text boxes for each incorrect option like this.
option input form
The part where I am stuck is appending them to the array here is my attempt.
export default class CreateQuiz extends Component{
constructor(props){
super(props);
this.onChangedIncorrectAnswer=this.onChangedIncorrectAnswer.bind(this);
this.onSubmit=this.onSubmit.bind(this);
this.state={
incorrect_answers:[]
}
}
onChangedIncorrectAnswer(e){
const option=e.target.value
this.setState({
incorrect_answers:[...this.state.incorrect_answers,option]
});
}
onSubmit(e){
e.preventDefault();
const quiz = {
incorrect_answers:this.state.incorrect_answers
}
console.log(quiz);
axios.post("http://localhost:3000/quizes",quiz)
.then(res=>console.log(res.data));
window.location='/quiz-list';
}
render(){
return (
<div>
<h3>Create New Quiz</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Incorrect Option 1</label>
<input type="text"
required
className="form-control"
value={this.state.incorrect_answers[0]}
onChange={this.onChangedIncorrectAnswer}
/>
</div>
<div className="form-group">
<label>Incorrect Option 2</label>
<input type="text"
required
className="form-control"
value={this.state.incorrect_answers[1]}
onChange={this.onChangedIncorrectAnswer}
/>
</div>
<div className="form-group">
<label>Incorrect Option 3</label>
<input type="text"
required
className="form-control"
value={this.state.incorrect_answers[2]}
onChange={this.onChangedIncorrectAnswer}
/>
</div>
<div className="form-group">
<input type="submit" value="Submit Quiz" className="btn btn-primary"/>
</div>
</form>
</div>
)
}
}
But the form was not working as expected. When I enter content for the first option in the "Option 1" text box only the first character is stored remaining in "Option 2" and so on.
try this, this would work!!
export default class CreateQuiz extends Component {
constructor(props) {
super(props);
this.onChangedCorrectAnswer = this.onChangedCorrectAnswer.bind(this);
this.onChangedIncorrectAnswer = this.onChangedIncorrectAnswer.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
category: "",
correct_answer: "",
difficulty: "",
type: "",
question: "",
incorrect_answers: ["", "", ""]
};
}
onChangedCorrectAnswer(e) {
this.setState({
correct_answer: e.target.value
});
}
onChangedIncorrectAnswer(e, index) {
const option = e.target.value;
const { incorrect_answers } = this.state;
incorrect_answers[index] = option;
this.setState({
incorrect_answers
});
}
onSubmit(e) {
e.preventDefault();
const quiz = {
category: this.state.category,
correct_answer: this.state.correct_answer,
incorrect_answers: this.state.incorrect_answers,
difficulty: this.state.difficulty,
type: this.state.type,
question: this.state.question
};
console.log(quiz);
axios
.post("http://localhost:3000/quizes", quiz)
.then((res) => console.log(res.data));
window.location = "/quiz-list";
}
render() {
return (
<div>
<h3>Create New Quiz</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Correct Answer</label>
<input
type="text"
required
className="form-control"
value={this.state.correct_answer}
onChange={this.onChangedCorrectAnswer}
/>
</div>
<div className="form-group">
<label>Incorrect Option 1</label>
<input
type="text"
required
className="form-control"
value={this.state.incorrect_answers[0]}
onChange={(e) => this.onChangedIncorrectAnswer(e, 0)}
/>
</div>
<div className="form-group">
<label>Incorrect Option 2</label>
<input
type="text"
required
className="form-control"
value={this.state.incorrect_answers[1]}
onChange={(e) => this.onChangedIncorrectAnswer(e, 1)}
/>
</div>
<div className="form-group">
<label>Incorrect Option 3</label>
<input
type="text"
required
className="form-control"
value={this.state.incorrect_answers[2]}
onChange={(e) => this.onChangedIncorrectAnswer(e, 2)}
/>
</div>
<div className="form-group">
<input
type="submit"
value="Submit Quiz"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
}
}
Your array state is considered as one state only
Why don't you create state on the go when user do any change in the input.
create a state object like
this.state = {incorrect_answers: {}}
In your onChangedIncorrectAnswer
onChangedIncorrectAnswer(e){
const option=e.target.value;
const stateName = e.target.name;
this.setState({
incorrect_answers: {...this.state.incorrect_answers, [stateName]: option }
});
}
use your form element as add name as unique which will be used as state
<div className="form-group">
<label>Incorrect Option 1</label>
<input name="incorrect_answers_0" type="text" required className="form-control" value={this.state.incorrect_answers[incorrect_answers_0]}
onChange={this.onChangedIncorrectAnswer} />
</div>
use that object while saving
onSubmit(e){
let yourIncorrectAnswers = Object.values(this.state.incorrect_answers);
})
}

Having issues with my onClick on a registration form

New to react so I apologize if the solution obvious. Working on a registration form that should create a new account on submit using ajax. I know that I'm supposed to use onChange to gather the information is submitted. After seeing a number of examples I am still unable to get my code to work.
I know that the ajax call for POST works because I put in information myself on vscode to test if on submit it will create which it did. I am having issues with the transition from onChange to my submit form. Hope that was clear. Thank you
I've tried creating a onChange function for my registration template. I am unable to get the results that I've been hoping for
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
payload: {
firstName: "",
lastName: "",
email: "",
password: "",
passwordConfirm: ""
}
};
}
handleChange = event => {
let name = event.target.name;
let value = event.target.value;
this.setState({
[name]: value,
[name]: value,
[name]: value,
[name]: value,
[name]: value
});
};
onClickHandler = evt => {
evt.preventDefault();
let payload = this.state;
console.log(payload);
debugger;
registerService
.register(payload)
.then(this.onActionSuccess)
.catch(this.onActionError);
};
onActionSuccess = response => {
console.log(response);
};
onActionError = errResponse => {
console.log(errResponse);
};
<!-- -->
render() {
return (
<main role="main">
<div className="container">
<h1 id="title" align="center">
Register User
</h1>
</div>
<div className="container">
<div className="col-sm-4 col-sm offset-4">
<form className="form">
<div className="form-group">
<label htmlFor="this.state.firstName">First Name</label>
<input
placeholder="First Name"
type="text"
id="this.state.firstName"
className="form-control"
name="firstName"
value={this.state.firstName}
onChange={this.handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="this.state.lastName">Last Name</label>
<input
placeholder="last Name"
type="text"
id="this.state. lastName"
className="form-control"
value={this.state.lastName}
onChange={this.handleChange}
/>
<div className="form-group">
<label htmlFor="register-email">Email</label>
<input
placeholder="Enter Email"
type="text"
className="form-control"
id="register-email"
value={this.state.email}
onChange={this.handleChange}
/>
</div>
</div>
<div className="form-group">
<label htmlFor="register-password">Password</label>
<input
placeholder="Password"
type="password"
id="register-password"
`enter code here`className="form-control"
value={this.state.password}
onChange={this.handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="register-passwordConfirm">
Confirm Password
</label>
<input
placeholder="Confirm Password"
type="password"
id="register-passwordConfirm"
className="form-control"
value={this.state.passwordConfirm}
onChange={this.handleChange}
/>
</div>
<button onClick={this.onClickHandler} className="btn btn-
primary">
Submit Form
</button>
I am expecting to create a new profile when clicking submit via POST ajax call
Your handleChange function should be
handleChange = event => {
let { name, value } = event.target;
this.setState({
[name]: value,
});
};
You always call your handleChange once at a time. Every time you change something on the input, the function gets different name and different value, which is suffice to add/update the your state.
And I noticed that you also have payload object in state. Modify the state like
this.state = {
firstname: '',
....
}
If you really want to use like
this.state = {
payload: {
firstName: '',
....
}
};
Your onChange function should look like,
handleChange = event => {
let { name, value } = event.target;
this.setState({
payload: {
...this.state.payload, // don't forget to copy all the other values
[name]: value,
}
});
};

Text input unfocused after one character React?

var uuid = require('uuid-v4');
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props){
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
addQuestion = () => {
questionNum++;
this.setState({
questions: this.state.questions.concat(["question","hi"])
});
console.log(this.state.questions);
this.setState({
answers: this.state.answers.concat(["hello","hi"])
});
console.log(this.state.answers);
console.log(questionNum);
console.log(this.state.title);
console.log(this.state.author);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title: <input type="text" value={this.state.title} onChange={this.handleChange} name="title" key={uuid()}/><br/>
Who's the Author? <input type="text" value={this.state.author} onChange={this.handleChange} name="author" key={uuid()}/><br/><br/>
</div>
<div className="questions">
Now let's add some questions... <br/>
{this.addQuestion}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
Both of the inputs on my page unfocus after typing only one character. Here's my code, I'm not entirely sure where I am going wrong here, it all worked just fine yesterday.
If there is anything else that you need to know just leave a comment and I will get to it. Your help is much appreciated, thanks!
When the key given to a component is changed, the previous one is thrown away and a new component is mounted. You are creating a new key with uuid() every render, so each render a new input component is created.
Remove the key from your inputs and it will work as expected.
<div className="Intro">
Give your Quiz a title:
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br/>
Who's the Author?
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br/><br/>
</div>
Tholle's response is correct. However, you should make some adjustments to how you interact with state. I've reworked your code with comments and included the uuid fix.
class App extends Component {
constructor(props){
super(props);
this.state = {
title: "",
author: "",
questions: [],
answers: []
}
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
handleChange({ target }) {
// since you'll always have a synthetic event just destructure the target
const { checked, name, type, value } = target;
const val = type === 'checkbox' ? checked : value;
this.setState({
[name]: val
});
}
addQuestion() {
// never modify state directly -> copy state and modify
const { answers, questions } = Object.assign({}, this.state);
// update your answers
answers.push(["hello", "hi"]);
// update your questions
questions.push(["question", "hi"]);
// now update state
this.setState({
answers,
questions
}, () => {
console.log(this.state);
});
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title: <input type="text" value={this.state.title} onChange={this.handleChange} name="title" /><br/>
Who's the Author? <input type="text" value={this.state.author} onChange={this.handleChange} name="author" /><br/><br/>
</div>
<div className="questions">
Now let's add some questions... <br/>
{this.addQuestion}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;

react.js - how to add element on success in dynamically generated button?

I have dynamically generated component with textfields and buttons. Each button do the ajax request. It's all working fine. However, I want to display the success message or error message on button itself, adding some icon on it. This is where I got stuck. I setup the flag and change the state, but it will change on all the buttons as expected. I also tried to change the current target, but the reference didn't work in success callback. Can someone please help me with this.
const FormGroup = ({index, type, field, value, onChange, spinner, isLoading, error, buttonType, brandList, handleBrandConfiguration, checkAvailability, handleCaseType, options, handlerRemoveItem})=> {
return(
<div>
<div className="form-group">
<label>{index}</label>
<input type="text"
name={field}
className="form-control"
value={value}
onChange={onChange}
/>
<select className="form-control" defaultValue="" onChange={handleBrandConfiguration}>
<option value="">Please select brand</option>
{brandList}
</select>
<select className="form-control" defaultValue="" onChange={handleCaseType}>
<option value="">Please select case template</option>
{options}
</select>
<button
type={buttonType}
className={classname(isLoading ? error ? "button button-danger" : "button button-success" : error ? "button button-danger" : "button button-primary")}
onClick={checkAvailability}>
<i className={classname(spinner ? error ? '': "fa fa-spinner fa-spin": '')}></i> {isLoading ? error ? 'Not Found' :<i className="fa fa-check fa-2" aria-hidden="true"></i> : error ? 'Not Found': 'Check Availability'}</button>
<input
type="button"
className="button button-danger"
value="Remove Item"
onClick={handlerRemoveItem}/>
</div>
</div>
);
};
Thanks
If you move your checkAvailability and result of api request to FormControl component you can set error and success for single component.
For Example:
class FormGroup extends React.Component{
constructor(props) {
super(props);
this.state = {
availabilityResult: null
};
}
callApi(username){
return(axios.get('https://api.github.com/users/' + username));
}
onChange(e){
this.setState({
itemCode: e.target.value
});
}
checkAvailability(e){
const username = this.state.itemCode;
let currentValue = e.currentTarget.value;
//ajax call goes here
this.callApi(username).then(response => {
const login = response.data.login;
this.setState({
availabilityResult: (login === 'mesmerize86')
});
}).catch(err => {
console.log(err);
});
}
render() {
const {index, field, value, handleRemoveItem} = this.props;
let inputState = '';
if (this.state.availabilityResult === true) inputState = 'button-success';
else if (this.state.availabilityResult === false) inputState = 'button-danger';
return(
<div className="form form-inline">
<div className="form-group">
<label>{index}</label>
<input type="text"
name={field}
className="form-control"
value={value}
onChange={this.onChange.bind(this)}
/>
<input
type="button"
className={`button button-primary ${inputState}`}
value="Check Availability"
onClick={this.checkAvailability.bind(this)} />
<input
type="button"
className="button button-danger"
value="Remove Item"/>
</div>
</div>
)
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {
rowList : [],
itemCodes: [],
itemCode: ''
}
}
handlerAddRow(){
const rowList = this.state.rowList.concat(FormGroup);
this.setState({ rowList });
}
handleRemoveItem(){
console.log('remove Item');
}
render(){
const rowList = this.state.rowList.map((row, index) => {
return (<FormGroup key={index} index={index+1} field={`"itemCode_"${index}`} />);
});
return(
<div className="container">
<input type="button" value="Add a row" className="button button-primary" onClick={this.handlerAddRow.bind(this)} /> <i class="fa fa-spinner" aria-hidden="true"></i>
{rowList}
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('app'));

Categories

Resources