I should have both question input and answer input in a <Field/>. Because of that redux-form docs tells me to use <Fields/>.
<Fields names={['question1', 'answer1']}
component={this.renderInputGroupField} />
<Fields names={['question2', 'answer2']}
component={this.renderInputGroupField} />
<Fields names={['question3', 'answer3']}
component={this.renderInputGroupField} />
rendering fields with this
renderInputGroupField(fields){
return(
<div className="form-group d-block">
<div className="form-group input-group">
<select className="form-select" >
<option>Multiple-Choice</option>
<option>Open-Ended</option>
</select>
<input {...fields.question1.input}
type="text"
className="form-input"
placeholder="Type your question..."/>
</div>
<div className="form-group">
<input {...fields.answer1.input}
type="text"
className="form-input"
placeholder="Comma-separated answer choices" />
</div>
</div>
);
}
To make renderInputGroupField work, I should add {...fields.answer1.input} into <input /> as above. Here is the problem. Names that are passed into fields are different and I can't find a way to change ...fields.answer1.input to ...fields.answer2.input dynamically.
I am not sure if I was able to explain it properly. Thanks for your help.
So, it seems you want to use the renderInputGroupField as a reusable component. Quickly testing, it looks like redux-form also sends back that list of names you originally gave it. You should be able to access those properties of fields that you listed in the names array using their index in that array, like below.
return(
<div className="form-group d-block">
<div className="form-group input-group">
<select className="form-select">
<option>Multiple-Choice</option>
<option>Open-Ended</option>
</select>
<input {...fields[fields.names[0]].input}
type="text"
className="form-input"
placeholder="Type your question..."/>
</div>
<div className="form-group">
<input {...fields[fields.names[1]].input}
type="text"
className="form-input"
placeholder="Comma-separated answer choices" />
</div>
</div>
);
Related
I am creating a CV Application project and I have a button that allows the user to Add Work Experience. When the user clicks the button a form pops up and they are able to fill the information out and click Submit.
I'm trying to make it so once the user hits Submit, the form div stays hidden until the user clicks Add Work Experience again. I've made something similar before in vanilla JS where I simply changed the forms class from display: block to display: none but that doesn't seem possible in React.
import React, { Component } from "react";
class WorkExperience extends Component {
render() {
const workExperience = [
{
title: "title",
company: "company",
location: "location",
description: "description",
},
];
return (
<>
<div id="content" className="content">
<h1 className="title">Work Experience</h1>
<div className="work-experience">
<p>Job Title: {workExperience[0].title}</p>
<p>Company: {workExperience[0].company}</p>
<p>Location: {workExperience[0].location}</p>
<p>Description: {workExperience[0].description}</p>
</div>
</div>
<button className="form-btn">+ Add Work Experience</button>
</>
);
}
}
export default WorkExperience;
And here is the form code I am currently using. This is the form I want to show/hide after clicking the Add Work Experience button shown above.
<form>
<label for="title">Job Title</label>
<input id="title" className="form-row" type="text" name="title" />
<label for="company">Company</label>
<input className="form-row" type="text" name="company" />
<label for="location">Location</label>
<input className="form-row" type="text" name="location" />
<label for="description">Job Description</label>
<textarea rows="4" cols="50" name="description"></textarea>
<button className="save">Save</button>
<button className="cancel">Cancel</button>
</form>
You can use an if statement or a ternary to return different jsx. That would look something like this. There are other ways as well, however this is a basic example of something you could do.
<>
{
shouldShow ?
(
<div id="content" className="content">
<h1 className="title">Work Experience</h1>
<div className="work-experience">
<p>Job Title: {workExperience[0].title}</p>
<p>Company: {workExperience[0].company}</p>
<p>Location: {workExperience[0].location}</p>
<p>Description: {workExperience[0].description}</p>
</div>
</div>
<button className="form-btn">+ Add Work Experience</button>
) : (
<form>
<label for="title">Job Title</label>
<input id="title" className="form-row" type="text" name="title" />
<label for="company">Company</label>
<input className="form-row" type="text" name="company" />
<label for="location">Location</label>
<input className="form-row" type="text" name="location" />
<label for="description">Job Description</label>
<textarea rows="4" cols="50" name="description"></textarea>
<button className="save">Save</button>
<button className="cancel">Cancel</button>
</form>
)
}
</>
Where shouldShow is what determines whether the form is showing or not.
The benefit to this is that if the form is showing, the other content is not added to the DOM and vice versa.
shouldShow would be a variable you could add to state, and when the button is clicked, you toggle the state variable, causing a re-render.
https://reactjs.org/docs/state-and-lifecycle.html
You could also choose to render styles depending on whether or not that component is showing, the key being that boolean state variable that is re-rendering the component.
Use Repeater Felilds to add User Work Experience. It's so easy to handle like this.
Repeater Component
import React from "react";
const Repeater = ({ inputFields, setInputFields }) => {
const handleFormChange = (index, event) => {
let data = [...inputFields];
data[index][event.target.name] = event.target.value;
setInputFields(data);
};
const removeFields = (index) => {
let data = [...inputFields];
data.splice(index, 1);
setInputFields(data);
};
return (
<div className="row">
{inputFields.map((input, index) => {
return (
<>
<div className="form-group col-sm-12 col-md-4 mb-3">
<div className="controls">
<input
type="text"
className="form-control inputset"
id="title"
placeholder="title"
name="title"
data-validation-required-message="This field is required"
aria-invalid="true"
required
value={input.title}
onChange={(event) => handleFormChange(index, event)}
/>
<div className="help-block" />
</div>
</div>
<div className="form-group col-sm-12 col-md-4 mb-3">
<div className="date-picker">
<input
type="text"
className="pickadate form-control inputset"
value={input.company}
onChange={(event) => handleFormChange(index, event)}
name="company"
id="pass"
data-validation-required-message="This field is required"
data-toggle="tooltip"
data-trigger="hover"
data-placement="top"
data-title="Date Opened"
data-original-title=""
required
/>
</div>
</div>
<div className="form-group col-sm-12 col-md-4 d-flex mb-3">
<input
type="text"
className="form-control inputset"
id="location"
placeholder="location"
name="location"
data-validation-required-message="This field is required"
aria-invalid="true"
required
value={input.location}
onChange={(event) => handleFormChange(index, event)}
/>
<input
type="text"
className="form-control inputset"
id="description"
placeholder="description"
name="description"
data-validation-required-message="This field is required"
aria-invalid="true"
required
value={input.description}
onChange={(event) => handleFormChange(index, event)}
/>
{inputFields.length === 1 ? null : (
<button
type="button"
className=" d-flex justify-content-center align-items-center ml-1 btn"
onClick={() => {
removeFields();
}}
>
<i className="uil-trash-alt" />
</button>
)}
</div>
</>
);
})}
</div>
);
};
export default Repeater;
Main Component
use these as states and pass the objects to the Repeater Component. First, the state is empty and when the user clicks on the button Add More Experience The files auto-show.
const [inputFields, setInputFields] = useState([
{ degree_title: "", institue: "", end_date: "" },
]);
const addFields = () => {
let newfield = { degree_title: "", institue: "", end_date: "" };
setInputFields([...inputFields, newfield]);
};
<Repeater
inputFields={inputFields}
setInputFields={setInputFields}
addFields={addFields} />
I wish this solution helps you :) Make sure to change the state object according to your requirements.
I am form where i am trying to add a check box for gender selection. I have tried everything but with no result.
Here is my code about the checkbox and a picture of my form
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.gender}
error={errors.gender}
id="gender"
type="checkbox"
className={classnames("", {
invalid: errors.amka
})}
/>
<label htmlFor="gender"><b className="black-text text-darken-1">Gender:</b></label>
<span className="red-text">{errors.gender}</span>
<br></br>
</div>
enter image description here
Okay, first of all you should not use chekckbox for gender selection unless there is very specific reason for that.
Here is the code for checkbox.
<div>
<input
type="checkbox"
name="checkbox"
id="checkbox"
checked={value}
value={value}
onChange={e => {
// perform the function onChange
}}
/>
<label for="checkbox">My Checkbox</label>
</div>
Here is the demo: https://stackblitz.com/edit/react-f5q3n8?file=src/App.js
Objective:
Need to pre-fill the form if the user has address saved in the DB.
Issue:
I am passing down the address object (coming from backend for the logged in user). :
{
city: "CA"
line1: "testline1"
line2: "testline2"
phone: "7772815615"
pin: "1234"
state: "CA"
user: "5eea03a736b70722c83a7b63"
}
Though I am able to console log it, but I am unable to pre-populate it in the form (rendered by the child component)
Child Component
let addressFinalValue = {};
const addressValue = (e) => {
addressFinalValue[e.target.name] = e.target.value;
};
const propsAddress = props.address;
const [address, setAddress] = useState(propsAddress);
console.log(propsAddress); // < -- ABLE TO CONSOLE LOG IT
return (
<div>
<div className="modal-header ">
<h5 className="modal-title text-center">
{address ? 'Please enter your address' : 'Please confirm your address'}
</h5>
<button className="close" data-dismiss="modal" aria-label="close">
<span className="edit-order-button" aria-hidden="true">Edit order</span>
</button>
</div>
<div className="container my-4">
<form onSubmit={submitHandler}>
{error && <p className="login-error" style={{ color: "red" }}>{error}</p>}
<div className="form-group">
<input id="address-line-1" className="form-control" value={propsAddress.line1}
onChange={addressValue} name="line1" type="text" placeholder="Line 1" />
</div>
<div className="form-group">
<input id="address-line-2" className="form-control" value={propsAddress.line2}
onChange={addressValue} name="line2" type="text" placeholder="Line 2" />
</div>
<div className="form-group">
<input id="city" className="form-control" value={propsAddress.city}
onChange={addressValue} name="city" type="text" placeholder="City" />
</div>
<div className="form-group">
<input id="state" className="form-control" value={propsAddress.state}
onChange={addressValue} name="state" type="text" placeholder="State" />
</div>
<div className="form-group">
<input id="pin" className="form-control" value={propsAddress.pin}
onChange={addressValue} name="pin" type="text" placeholder="PIN" />
</div>
<div className="form-group">
<input id="phone" className="form-control" value={propsAddress.phone}
onChange={addressValue} name="phone" type="text" placeholder="Phone Number" />
</div>
<hr />
<button className="btn btn-success">Save Address & Continue</button>
</form>
</div>
</div> );
Is there something I am missing here? I don't know what it is.
I would achieve this using the following:
Make the child component a class with state. Pass the child component a populate prop, with the address values.
<ChildComponent populate={address} />
constructor(props){
this.state = {
...props.populate
}
}
handleAddressChange(e, addressItem){
this.setState({[addressItem]: event.target.value});
}
Then, in your form, set the value of each form item equal to the stateful address value. For example:
<div className="form-group">
<input id="address-line-1" className="form-control" value={this.state.line1}
onChange={(e) => handleAddressChange(e, 'line1')} name="line1" type="text" placeholder="Line 1" />
</div>
Make sure your onChange event handler is updating the child components this.state.address value using setState.
Checkout this file for more info: https://github.com/ccrowley96/grocerylist/blob/master/client/src/components/AddEditModal/AddEditModal.js
I do something very similar.
I have created a form in using React and maintain state for storing form data. The form contains only text fields and radio buttons. There is a 'Proceed' button at the end whose onClick function goes like this :
handleClick(event){
console.log(this.state);
var userID = 1;
firebase.database().ref('registrations/'+userID).set(this.state);
}
There is another function to handle input change like so :
handleInputChange(event){
const target=event.target;
const name=target.name;
var value;
if((target.type==="radio"&&target.checked)||target.type!=="radio") value=target.value;
this.setState({
[name]:value
});
}
I want the console to log the updated state after filling the form and clicking the Proceed button. But when I fill the form and click the button, the state is briefly displayed on the console before disappearing (console goes back to how it was initially) and I see the form data in the URL instead. How do I stop the data being displayed in the URL and log the state data in the console?
I am relatively a beginner in React. So please bear with me if I don't know something very basic.
Thanks in advance.
Edit: Here's render(), and please note that I also updated the click handler.
render() {
return(
<div>
<div className="State">
<div className="Head">
State
</div>
<div className="StateField">
<input
name="state"
type="text"
onChange={this.handleInputChange} />
</div>
<hr />
</div>
<div className="Age">
<div className="Head">
Age
</div>
<div className="AgeField">
<input
name="age"
type="number"
onChange={this.handleInputChange} />
</div>
<hr />
</div>
<div className="Ethnicity">
<div className="Head">
Ethnicity
</div>
<div className="EthnicityField">
<input name="ethnicity" type="radio" value="Hispanic or Latino" onClick={this.handleInputChange} defaultChecked /> Hispanic or Latino
<input name="ethnicity" type="radio" value="Non-Hispanic or Non-Latino" onClick={this.handleInputChange} /> Non-Hispanic or Non-Latino
</div>
<hr />
</div>
<div className="Race">
<div className="Head">
Race
</div>
<div className="RaceField">
<input name="race" type="radio" value="American Indian" onClick={this.handleInputChange} defaultChecked /> American Indian
<input name="race" type="radio" value="Asian" onClick={this.handleInputChange}/> Asian
<input name="race" type="radio" value="Native Hawaiian or Other Pacific Islander" onClick={this.handleInputChange}/> Hawaiian or Other Pacific Islander
<input name="race" type="radio" value="Black or African American" onClick={this.handleInputChange}/> Black or African American
<input name="race" type="radio" value="White" onClick={this.handleInputChange}/> White
</div>
<hr />
</div>
<div className="Sex">
<div className="Head">
Sex
</div>
<div className="SexField">
<input name="sex" type="radio" value="Male" onClick={this.handleInputChange} defaultChecked /> Male
<input name="sex" type="radio" value="Female" onClick={this.handleInputChange}/> Female
</div>
<hr />
</div>
<div className="Height">
<div className="Head">
Height
</div>
<div className="HeightField">
<input name="height" type="number" placeholder="In inches" onChange={this.handleInputChange}/>
</div>
<hr />
</div>
<div className="Weight">
<div className="Head">
Weight
</div>
<div className="WeightField">
<input name="weight" type="number" placeholder="In pounds" onChange={this.handleInputChange}/>
</div>
<hr />
</div>
<div className="ProceedButton">
<button name="Proceed" onClick={this.handleClick} >Proceed</button>
</div>
</div>
);
And now I also realized that when I add the state to firebase realtime database, height, sex and weight fields are empty. Please point out if anything's wrong.
The default type of a button element is "submit". If you don't do anything to prevent it, clicking a button will submit a form that is an ancestor element of the button.
All the symptoms you describe are consistent with the button being inside a form that has not been shown in the post.
Try addding `type="button" to the button definition:
<button name="Proceed" type="button" onClick={this.handleClick} >Proceed</button>
I have created two hidden fields where I am adding array to make dropdown. select2 array are depending on the first select2. So when I select in first select2 I will call select2 event to filter another select2.
All this is working correct. But I am cloning also same. But I don't know how to clone event of select2. I could not filter also. Somebody can help
My Sample code
My jsbin https://jsbin.com/monusupuni/edit?html,js,output
<div class="midcontainer pad20">
<div class="content-area fullWidth whiteBg">
<div class="pad15">
<div class="flightRows">
<div class="row flightRow">
<p><strong><span id="lbFlight">Flight 1</span></strong></p>
<div class="depCol1">
<label for="seldcity1" id="lbDeptCity"></label><br>
<input type="hidden" id="seldcity1" name="seldcity1" class="styled wth190 seldcity" />
</div>
<div class="depCol2">
<label for="selacity1" id="lbArrivalCity"></label><br>
<input type="hidden" id="selacity1" name="selacity1" style="width: 210px;" class="styled wth190 selacity" />
</div>
<div class="depCol1">
<label for="selddate1" id="lbDeptDate"></label><br />
<input name="selddate1" type="text" id="selddate1" autocomplete="off" class="datepicker calIcon">
</div>
<div class="searchBtnHolder">Add another Flight</div>
<div class="clear"></div>
<hr />
</div>
</div>
</div>
I'm not sure to understand your exact desire. Still, did you know that jQuery clone() method takes 2 arguments: withDataAndEvent and deepWithDataAndEvent.
So when you use .clone(), it will only clone the element, not the event associated with. You'll need to use .clone(true,true) to copy all events attached.
http://api.jquery.com/clone/