Add/remove form inputs block dynamically - javascript

I want to repeat the same line of fields when i click on the plus button. I tried to implement this functionality based on an state attribute plus that changed to true when i click on the button then i check if this state attribute is true? add the Fields : null. but it doesn't work and i think i am missing some concept so please some help!
the component state:
this.state = {
plus : false
}
the plusHandler:
plus = (e)=>{
this.setState({
plus: true,
});
}
in the render:
<div className="form-row">
<div className="form-group col-md-5">
<label htmlFor="cRelation">Relation</label>
<select name="cRelation" defaultValue={''} id="cRelation" className="form-control">
<option disabled value=''> select relation</option>
{relationList.map(item => (
<option key={item} value={item}>{item}</option>
)
)}
</select>
</div>
<div className="form-group col-md-6">
<label htmlFor="withConcept">withConcept</label>
<select name="withConcept" defaultValue={''} id="withConcept" className="form-control">
<option value='' disabled> select concept</option>
{(conceptList|| []).map(item => (
<option key={item.conceptId} value={item.conceptId}>{item.conceptName}</option>
))}
</select>
</div>
<div className="form=group align-self-sm-center mt-2">
<button type="button" className="btn btn-sm btn-outline-success m-2" onClick={this.plus}>+</button>
<button type="button" className="btn btn-sm btn-outline-danger pr-2">-</button>
</div>
</div>
{this.state.plus?
<div className="form-row">
<div className="form-group col-md-5">
<label htmlFor="cRelation">Relation</label>
<select name="cRelation" defaultValue={''} id="cRelation" className="form-control">
<option disabled value=''> select relation</option>
{relationList.map(item => (
<option key={item} value={item}>{item}</option>
)
)}
</select>
</div>
<div className="form-group col-md-6">
<label htmlFor="withConcept">withConcept</label>
<select name="withConcept" defaultValue={''} id="withConcept" className="form-control">
<option value='' disabled> select concept</option>
{(conceptList|| []).map(item => (
<option key={item.conceptId} value={item.conceptId}>{item.conceptName}</option>
))}
</select>
</div>
<div className="form=group align-self-sm-center mt-2">
<button type="button" className="btn btn-sm btn-outline-success m-2" onClick={this.plus}>+</button>
<button type="button" className="btn btn-sm btn-outline-danger pr-2">-</button>
</div>
</div>
:null }
this is the output i want:

I'd think of it not as add/remove input fields, but rather as managing your form state to maintain necessary elements visibility.
As long as you're going to access values, selected in those input fields (e.g. upon form submit), instead of using boolean flag, you may need to store dynamic form rows within your state as array of following structure:
[
{rowId:..., selectedOptions:{relation:..., concept...}},
...
]
For simplicity sake, I'd also re-design your dynamic form rows as a separate component.
With that, I'd attach onClick() event handlers of add/remove buttons within row component to callbacks of parent form component that will append/remove array items within its state, thus making corresponding row components appear/disappear.
You may inquiry following live-snippet for complete demonstration of that concept:
const { useState } = React,
{ render } = ReactDOM
const relations = ['relation1', 'relation2', 'relation3'],
concepts = ['concept1', 'concept2', 'concept3']
const FormRow = ({rowId, selectedOptions, onSelect, onAdd, onRemove}) => {
const handleChange = e => onSelect(rowId, e.target.getAttribute('param'), e.target.value)
return (
<div>
<label>Relation:
<select param="relation" onChange={handleChange} value={selectedOptions.relation||''}>
<option value="" disabled>select relation</option>
{
relations.map((rel,key) => <option {...{key}} value={rel}>{rel}</option>)
}
</select>
</label>
<label>With Concept:
<select param="concept" onChange={handleChange} value={selectedOptions.concept||''}>
<option value="" disabled>select concept</option>
{
concepts.map((con,key) => <option {...{key}} value={con}>{con}</option>)
}
</select>
</label>
<button type="button" onClick={onAdd}>+</button>
<button type="button" onClick={() => onRemove(rowId)}>-</button>
</div>
)
}
const Form = () => {
const [rows, setRows] = useState([{rowId:0, selectedOptions:{}}]),
onAddRow = () => {
const maxRowId = Math.max(...rows.map(({rowId}) => rowId))
setRows([...rows, {rowId: maxRowId+1, selectedOptions:{}}])
},
onRemoveRow = id => setRows(rows.filter(({rowId}) => rowId != id)),
onSelectRow = (id, param, val) => {
const rowsCopy = [...rows],
item = rowsCopy.find(({rowId}) => rowId == id)
Object.assign(item, {selectedOptions:{...item.selectedOptions, [param]:val}})
setRows(rowsCopy)
}
return (
<form onSubmit={e => (e.preventDefault(), console.log(rows))}>
{
rows.map(({rowId, selectedOptions}, key) => (
<FormRow
{...{key, rowId, selectedOptions}}
onAdd={onAddRow}
onRemove={onRemoveRow}
onSelect={onSelectRow}
/>
))
}
<input type="submit" value="Submit" />
</form>
)
}
render (
<Form />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

You need a list with the items to render. When the user click in plus button you need add a new element.
Example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { items: [{}], relationList: [], conceptList: [] };
}
addItem = () => {
var { items } = this.state;
items.push({});
this.setState({ items });
}
removeItem = (index) => {
var { items } = this.state;
items.splice(index, 1);
this.setState({ items });
}
render() {
var { items, conceptList, relationList } = this.state;
return (
<div>
{items.map((rowItem, k) => (
<div key={k} className="form-row">
<div className="form-group col-md-5">
<label htmlFor={`cRelation${k}`}>Relation</label>
<select name={`cRelation${k}`} defaultValue={''} id={`cRelation${k}`} className="form-control">
<option disabled value=''> select relation</option>
{relationList.map(item => (
<option key={item} value={item}>{item}</option>
)
)}
</select>
</div>
<div className="form-group col-md-6">
<label htmlFor={`withConcept${k}`}>withConcept</label>
<select name={`withConcept${k}`} defaultValue={''} id={`withConcept${k}`} className="form-control">
<option value='' disabled> select concept</option>
{(conceptList || []).map(item => (
<option key={item.conceptId} value={item.conceptId}>{item.conceptName}</option>
))}
</select>
</div>
<div className="form=group align-self-sm-center mt-2">
<button onClick={this.addItem} type="button" className="btn btn-sm btn-outline-success m-2">+</button>
<button onClick={() => this.removeItem(k)} type="button" className="btn btn-sm btn-outline-danger pr-2">-</button>
</div>
</div>
))}
</div>
);
}
}

Instead of Boolean , use an integer to denote the number of rows like below . plus handler will increment the count .
this.state = {
i: 1
}
Plus Handler
plus = (e) => {
this.setState({
i: this.state.i + 1
});
}
Render function :
rowfunction() {
return (<div className="form-row">
<div className="form-group col-md-5">
<label htmlFor="cRelation">Relation</label>
<select name="cRelation" defaultValue={''} id="cRelation" className="form-control">
<option disabled value=''> select relation</option>
</select>
</div>
<div className="form-group col-md-6">
<label htmlFor="withConcept">withConcept</label>
<select name="withConcept" defaultValue={''} id="withConcept" className="form-control">
<option value='' disabled> select concept</option>
</select>
</div>
<div className="form=group align-self-sm-center mt-2">
<button type="button" className="btn btn-sm btn-outline-success m-2 " onClick={this.plus}>+</button>
<button type="button" className="btn btn-sm btn-outline-danger pr-2">-</button>
</div>
</div>)
}
render() {
var rows = [];
for (let index = 0; index < this.state.i; index++) {
rows.push(this.rowfunction())
}
return rows;
}

Related

I want to get selected value from select options on button click in react.js

I want to get the selected value from the dropdown on button click and then I want to save it in the firebase database. Everything is working fine except dropdown. I also want to add a dropdown value in the firebase database. Anyone can help me how can I get it? I'm trying but it is giving error. Anyone can help me?
import React, { Component } from 'react';
import Select from 'react-select';
import firebase from '../config/firebase.js';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
class PostAd extends React.Component {
constructor() {
super();
this.state = {
selectedOption: null,
ads: [],
};
}
handleClick = () => {
firebase.database().ref('/').child('ads').push(this.state);
console.log(`Option selected:`, selectedOption);
};
handleChange = (e) => {
this.setState({
selectedOption,
[e.target.name]: e.target.value,
});
console.log(`Option selected:`, selectedOption);
};
render() {
const { selectedOption } = this.state;
return (
<div className="container postAd-container">
<h6 className="p-3">CHOOSE A CATEGORY</h6>
<hr />
<Select value={selectedOption} onChange={this.handleChange} options={options} />
<div className="p-3">
<div className="">
<p>Condition *</p>
<button className="btn-attributes" name="new" value="new" onClick={this.handleChange}>
New
</button>
<button className="btn-attributes" name="used" value="used" onClick={this.handleChange}>
Used
</button>
</div>
<div className="pt-2">
<p>Type *</p>
<button className="btn-attributes">Apple</button>
<button className="btn-attributes">Dany Tabs</button>
<button className="btn-attributes">Q Tabs</button>
<button className="btn-attributes">Samsung</button>
<button className="btn-attributes">Other Tablets</button>
</div>
<div className="pt-5">
<p>Ad Title *</p>
<div className="form-group row">
<div className="col-sm-6">
<input
type="email"
name="adTitle"
onChange={this.handleChange}
className="form-control form-control-lg"
/>
<p className="font-11">Mention the key features of your item (e.g. brand, model, age, type) 0 / 70</p>
</div>
</div>
</div>
<div className="pt-5">
<p>Description *</p>
<div className="form-group row">
<div className="col-sm-6">
<textarea name="description" onChange={this.handleChange} className="form-control" rows="3"></textarea>
<p className="font-11">Include condition, features and reason for selling 0 / 4096</p>
</div>
</div>
</div>
</div>
<hr />
<div className="p-4">
<div className="">
<h6>SET A PRICE</h6>
<div className="form-group row">
<div className="col-sm-6">
<div className="input-group mb-2">
<div className="input-group-prepend">
<div className="input-group-text">Rs</div>
</div>
<input type="number" name="price" onChange={this.handleChange} className="form-control" />
</div>
</div>
</div>
</div>
</div>
<div className="form-row pb-3">
<div className="col-md-12 text-center">
<button type="submit" className="btn btn-primary" onClick={this.handleClick}>
Post Ad
</button>
</div>
</div>
</div>
);
}
}
export default PostAd;
Make a seperate function this.handleClickButton and use it for New and Used buttons. instead this.handleChange
handleClickButton = e => {
this.setState({
[e.target.name]: e.target.value
});
};
handleChange = selectedOption => {
this.setState({
selectedOption
},() => {
console.log(`Option selected:`, this.state.selectedOption);
});
};
This code will change the dropdown without any error.
If you would like to manage both with the same function. Following is the solution:
handleChange = selectedOption => {
//onClick it will get e.target.value
if (e.target.value) {
this.setState({
[e.target.name]: e.target.value
});
} else {
//onChange it will get the selected option.
this.setState({
selectedOption: e
});
}
};

How to populate 2nd dropdown based on selection from 1st dropdown using react function component with react hooks and react redux?

I'm trying to populate second drop down based on the selection of first drop down. I have just started with reactJS like a month ago with react hooks and react-redux.
All the answers I found were based on class component and none of them used Redux. I want to solve this with react-hooks and react-redux only. Any help would be really appreciated.
This is the state when the page loads
const AddRequest = ({
addRequest,
getActivities,
activities
}) => {
useEffect(() => {
getActivities();
}, [getActivities]);
const [formData, setFormData] = useState({
activity: "",
subActivity: ""
});
const { activity, subActivity } = formData;
const onChangeHandler = e => {
e.preventDefault();
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const onSubmitHandler = e => {
e.preventDefault();
addRequest(formData, history);
};
let activityOptions = activities.map(activity => (
<option key={activity._id} value={activity.activityName}>
{activity.activityName}
</option>
));
return (
<form className="form-signin" onSubmit={e => onSubmitHandler(e)}>
<div className="form-label-group">
<select
className="form-control"
name="activity"
value={activity}
onChange={e => onChangeHandler(e)}
>
<option>Select Activity</option>
{activityOptions}
</select>
</div>
<div className="form-label-group">
<select
className="form-control"
name="subActivity"
value={subActivity}
defaultValue={{ label: "Select sub Activity", value: 0 }}
onChange={e => onChangeHandler(e)}
>
<option>Select Sub Activity</option>
{activities.subActivities.map((subAct, index) => (
<option key={index} value={subAct}>
{subAct}
</option>
))}
</select>
</div>
<div className="form-label-group">
<input
type="text"
className="form-control"
placeholder="Description (optional)"
value={description}
required
/>
</div>
<hr className="my-4" />
<button
className="btn btn-lg btn-primary btn-block text-uppercase"
type="submit"
>
Submit
</button>
</form>
);
AddRequest.propTypes = {
getActivities: PropTypes.func.isRequired,
addRequest: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
activities: state.activity.activities,
});
export default connect(
mapStateToProps,
{ addRequest, getActivities }
)(withRouter(AddRequest));

How can i get multiple select options in react js.?

So basically iam new in react and iam trying to create multiple select option using axio get method i have a problem that how can i add multiple select option in this file iam trying to do this with check box code below but keep getting error that a string is called on change function. Other than that the checkboxes are not opening due to that function
List item
import React, { Component, Fragment } from "react";
import axios from "axios";
class Home extends Component {
state = {
users: []
};
componentDidMount() {
axios.get("https://jsonplaceholder.typicode.com/users").then(res => {
console.log(res);
this.setState({
users: res.data
});
});
}
showCheckboxes = () => {
let expanded = false;
const checkboxes = document.getElementById("checkboxes");
if (!expanded) {
checkboxes.style.display = "block";
expanded = true;
} else {
checkboxes.style.display = "none";
expanded = false;
}
};
onChangeValue = e => {
const value = e.target.value;
debugger;
};
render() {
const { users } = this.state;
const nameList = users.length ? (
<select className="custom-select">
{users.map((user, i) => {
return (
<option key={i} value={user.name}>
{" "}
{user.name}
</option>
);
})}
</select>
) : (
"No Data"
);
const usernameList = users.length ? (
<select className="custom-select">
{users.map((user, i) => {
return (
<option key={i} value={user.username}>
{user.username}
</option>
);
})}
</select>
) : (
"No Data"
);
const emailList = users.length ? (
<select className="custom-select">
{users.map((user, i) => {
return (
<option key={i} value={user.email}>
{user.email}
</option>
);
})}
</select>
) : (
"No Data"
);
return (
<Fragment>
{nameList}
<hr />
{usernameList}
<hr />
{emailList}
<hr />
<div className="multiselect">
<div className="selectBox">
<select>
<option>Select an option</option>
</select>
<div className="overSelect" onClick="showCheckboxes()"></div>
</div>
<div id="checkboxes">
<label htmlFor="one">
<input type="checkbox" id="one" />
First checkbox
</label>
<label htmlFor="two">
<input type="checkbox" id="two" />
Second checkbox
</label>
<label htmlFor="three">
<input type="checkbox" id="three" />
Third checkbox
</label>
</div>
</div>
</Fragment>
);
}
}
export default Home;
this line :
<div className="overSelect" onClick="showCheckboxes()"></div>
to
<div className="overSelect" onClick={this.showCheckboxes}></div>

How can I append objects to the body of a component in React?

I am trying to set up some functionality on this React component so that a user can add and remove empty radio button options to a page that a user can type text into. The only issue that I am having is that I am relatively new to React and am not 100% how to do this.
import React, { Component } from 'react';
class TextRadio extends Component {
constructor() {
super();
state = {
textValue: ""
}
};
handleInputChange = event => {
const value = event.target.value;
const name = event.target.name;
this.setState({
[name]: value
});
}
addBox = () => {
}
removeBox = () => {
}
render() {
return(
<div>
<div className="form-check">
<input className="form-check-input" type="radio" id="" name="" value="" />
<label className="form-check-label" for="">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button type="button" className="btn btn-danger" onClick={this.removeBox}>
Remove Option
</button>
</div>
);
}
}
export default TextRadio;
The result that I am expecting to happen is to have it so the component can add and remove radio button options from the page depending on the button that the user presses
i was completed just your addBox and RemoveBox functions, i hope that's help you
import React, { Component } from "react";
class TextRadio extends Component {
constructor() {
super();
this.state = {
radioButtons: []
};
}
handleInputChange = event => {
const value = event.target.value;
const name = event.target.name;
};
addBox = () => {
this.setState(prevstate => {
let radioButtons = prevstate.radioButtons;
if (radioButtons.length === 0) {
radioButtons.push({
id: 1,
name: "radiobutton",
value: "test"
});
return {
radioButtons: radioButtons
};
} else {
radioButtons.push({
id: radioButtons[radioButtons.length - 1].id + 1,
name: "raiodButton_" + (radioButtons[radioButtons.length - 1].id + 1),
value: radioButtons[radioButtons.length - 1].value
});
return {
radioButtons: radioButtons
};
}
});
};
removeBox = () => {
this.setState(prevstate => {
let radioButtons = prevstate.radioButtons;
if (radioButtons.length !== 0) {
radioButtons.pop(radioButtons[radioButtons.length - 1]);
return {
radioButtons: radioButtons
};
} else {
return { radioButtons: radioButtons };
}
});
};
render() {
return (
<div>
<div className="form-check">
{this.state.radioButtons.map(radiobutton => {
return (
<div>
<input
className="form-check-input"
type="radio"
id={radiobutton.id}
name={radiobutton.name}
value={radiobutton.value}
/>
<label className="form-check-label" for="">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
);
})}
</div>
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button
type="button"
className="btn btn-danger"
onClick={this.removeBox}
>
Remove Option
</button>
</div>
);
}
}
export default TextRadio;
https://codesandbox.io/embed/confident-browser-tmojp
I was playing around with your idea and made some changes in the code, just to show you an example, how you can dynamically create new components and store them in applications state and then render out to user based on their actions.
I created new component just for form UI: option, input field and remove button. If user clicks on the Add Option, new item of the component is added to application state and then render out. Remove button is used to remove Item from state.
class TextRadio extends Component {
state = {
optionInputs: []
};
addBox = () => {
const optionInputsUpdated = [
...this.state.optionInputs,
<OptionInput id={uuid.v4()} remove={this.removeBox} />
];
this.setState({ optionInputs: optionInputsUpdated });
};
removeBox = id => {
const optionInputsUpdated = this.state.optionInputs.filter(
item => item.props.id !== id
);
this.setState({ optionInputs: optionInputsUpdated });
};
render() {
return (
<div>
{this.state.optionInputs.map((optionInput, idx) => {
return (
<div key={idx} test="123">
{optionInput}
</div>
);
})}
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
</div>
);
}
}
const OptionInput = props => {
return (
<div className="form-check">
<input
className="form-check-input"
type="radio"
id=""
name="radio"
value=""
/>
<label className="form-check-label" for="">
<input className="form-control" type="text" placeholder="" />
</label>{" "}
<button
type="button"
className="btn btn-danger"
onClick={() => props.remove(props.id)}
>
Remove Option
</button>
</div>
);
};
Hope this gives you better understanding, how to achieve your goal.
If you need additional help, just post a comment under this answer, and I will update demo to help you.
Here is DEMO I created from your code: https://codesandbox.io/s/nice-ganguly-s4wls
first you have to initialize an empty array state
this.state={
radioButtons : [{input:''}]
}
then in your return statement you have to loop through the radioButtons array and show the radio button with input
{
this.state.radioButtons.map(item => (
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
))
}
then in your addBox function append an object on every click
addBox = () => {
this.setState({radioButtons:[...this.state.radioButtons, {input:''}]})
}
function to remove a radio button object
removeBox = () => {
let radioArray = this.state.radioButtons
radioArray.pop()
this.setState({radioButtons:radioArray})
}
Final code Looks like this :
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component{
constructor(props){
super(props);
this.state={
radioButtons :[{input:''}]
}
}
addBox = () => {
this.setState({radioButtons:[...this.state.radioButtons, {input:''}]})
}
removeBox = () => {
let radioArray = this.state.radioButtons
radioArray.pop()
this.setState({radioButtons:radioArray})
}
render(){
return(
<div>
{
this.state.radioButtons.map(item => (
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
))
}
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button type="button" className="btn btn-danger" onClick={this.removeBox}>
Remove Option
</button>
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
codepen Example

Way to clear selection in React Select

is there a way to clear the selected value in the react select dropdown menu after choosing an option with a press of a clear button? Thank you.
import Select from 'react-select';
const TransactionDetailsPanel = props => {
const clearQuery = () => {
inputRef.current.value=null;
};
return (
<>
<div className="columns is-gapless is-marginless">
<Select className="column is-3" options={options} onChange={updateSelection}
ref={selectRef} placeholder="Advanced Detail Search" />
<input className="column is-3" type="text"
ref={inputRef} placeholder="Enter query here..."/>
<div className="buttons">
<button className="button" onClick={updateQuery}>Details Search</button>
<button className="button" onClick={clearQuery}>Clear</button>
</div>
</div>

Categories