add input field when button is clicked - javascript

I have a form for adding recipe where there is ingredients button. A recipe can have many ingredients. When that button is clicked an input field for adding ingredients should be appear beneath ingredient button. What i tried to do is?
import Input from './Input.jsx';
export default class Sell extends Component {
constructor()
{
super();
this.state = {
inputValues : {}
}
this.onHandleSubmit =this.onHandleSubmit.bind(this);
}
onChange( name, { target : { value } })
{
const inputValues = this.state.inputValues;
inputValues[name] = value;
this.setState({ inputValues })
}
onHandleSubmit()
{
console.log('clicked');
const name = `ingrediant-${Object.keys(this.state.inputValues).length}`;
let inputbox = <Input name={ name }
onChange={ this.onChange.bind(this, name )} />
}
onSubmit(event){
event.preventDefault();
}
render() {
return (
<div className="row">
<h2 className="flow-text text-center">Add Recipe</h2>
<form onSubmit={this.onSubmit} className="col offset-s4 s4">
<div className="row">
<div className="input-field col s12">
<input ref="name" id="name" type="text" className="validate flow-text" />
<label htmlFor="name">Name of Recipe</label>
</div>
</div>
<div className="input-field col s12">
<a onClick={this.onHandleSubmit} className="waves-effect waves-light btn btn-block"><i className="material-icons right">add</i>Ingredients</a>
</div>
</div>
{this.state.inputValues}
<div className="row">
<button className="waves-effect waves-light btn btn-block">Submit</button>
</div>
</form>
</div>
);
}
}
How can i create new dynamic(different ref for newly created input box) input field for ingredients when button is clicked?

In your case I would make the input a separate component like
class Input extends Component
{
render()
{
const { name, onChange } = this.props;
return(<div className="row">
<div className="input-field col s12">
<input id={name}
type="text"
className="validate flow-text"
onChange={ this.props.onChange } />
<label htmlFor={name}>Name of Recipe</label>
</div>
</div>)
}
}
And pass an onChange callback from the Sell component, the Sell component will maintain the values of the inputs, and when you want to send the values to the server you have it on the state. Something like:
class Sell extends Component
{
constructor()
{
super();
this.state = {
inputValues = {},
inputs : []
}
}
onChange( name, { target : { value } })
{
const inputValues = this.state.inputValues;
inputValues[name] = value;
this.setState({ inputValues })
}
onHandleSubmit()
{
const name = `incrediant-${this.state.inputs.length}`;
let inputbox = <Input name={ name }
key={this.state.inputs.length}
onChange={ this.onChange.bind(this, name )} />
const inputs = this.state.inputs;
inputs.push( inputbox );
this.setState( { inputs } );
}
...
....
render()
{
...
...
{
this.state.inputs.map( i => i );
}
}
}
In that case you dont have to keep refs

Related

React form does not render the state values I put on constructor

I have a tab component with 2 tabs. The first tab contains a list of employees and a button that transfers you in the second tab (contact form) containing the data of the selected employee. In the first tab I create an object of an employee, send it in the second tab and in the second tab i set the state.name, state.surname with the object values.
The problem is that in order to load the data in the form I need to change back in the first tab and go to the second tab again.
The tabs component
import React from 'react';
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box p={3}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
var importErg = new Boolean(false);
export function getImport(){
return importErg;
}
export const globalErg = {
onoma: "",
epitheto: ""
}
export function getGlobalErg(){
return globalErg;
}
async function getErgByeID(ErgEid){
globalErg.onoma = ""
globalErg.epitheto = ""
await fetch(URL+"/ergazomenoi?eid=eq."+ErgEid)
.then(response => {if(response.ok){
return response.json()
}
else {
alert('Something went wrong')
throw new Error('Something went wrong');
}
})
.then(data => {
globalErg.onoma = data[0].onoma
globalErg.epitheto = data[0].epitheto
}
)
}
export default function SimpleTabs() {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
function more(ergID){
setValue(1);
getErgByeID(ergID);
}
}
return (
<div className="main">
<AppBar position="static" id = "topbar">
<Tabs value={value} onChange={handleChange}>
<Tab label="Employees" {...a11yProps(0)}/>
<Tab label="Contact" {...a11yProps(1)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
<Table />
<Button style={{color: "#fff", background: "#111", marginRight: "2.5px", marginLeft:"2.5px", marginTop:"5px"}} onClick={() => more()}>
Contact
</Button>
</TabPanel>
<TabPanel value={value} index={1} id = 'tab'>
<Contact/>
</TabPanel>
</div>
);
}
the form component
import React, { Component, useEffect } from "react";
import {getGlobalErg} from "./Proswpiko-tabs";
class Personal_info extends Component {
constructor(props){
super(props);
let erg = getGlobalErg();
this.state = {
onoma: erg.onoma,
epitheto: erg.epitheto,
};
}
onomaChangeHandler = (event) => {
this.setState({onoma: event.target.value});
}
epithetoChangeHandler = (event) => {
this.setState({epitheto: event.target.value});
}
render() {
return (
<form onSubmit = {this.SubmitHandler}>
<div >
<p id = "topText" align = "center">
<h2>Contact info</h2>
</p>
<img id="top" src="top.png" alt=""></img>
<div id="form_container">
<form id="form" class="appnitro" method="post" action="">
<div class="form_description">
<h2>Personal info</h2>
</div>
<ul>
<li id = "li_3">
<label class="description" for="element_3" >Όνομα </label>
<span>
<input type ="text" id="nameInput" name= "nameInput" class="element text" maxLength="255" size="15" onChange={this.onomaChangeHandler} value = {this.state.onoma} required/>
<label>Name</label>
</span>
<span>
<input type ="text" id="surNameInput" name= "surNameInput" class="element text" maxLength="255" size="14" onChange={this.epithetoChangeHandler} value = {this.state.epitheto} required/>
<label>Surname</label>
</span>
</li>
</ul>
</form>
<div id="footer">
</div>
</div>
<img id="bottom" src="bottom.png" alt=""></img>
</div>
</form>
);}}
export default Personal_info;
It seems that since the setState func is async It didnt had time to fill the imput boxes.
Solution: Added setTimeout and now everything works fine

How to pass the default value back as input value if you don't want the value to be changed?

I have assigned default values to the input tag. When I try to submit without changing the values of the input tag, the value passed is undefined.
What I want is that if the input value is changed, to update the state value with the new value and if the input tag value is not changed, then to pass the default value to the state.
I am new to React.
import React, { Component } from 'react';
import Firebase from '../Firebase';
import _ from 'lodash';
import '../App.css';
class Modify extends Component {
constructor(props) {
super(props);
//state
this.state = {
personDetails: {},
detail: ''
};
//bind
this.renderData = this.renderData.bind(this);
this.handleChange = this.handleChange.bind(this);
this.saveEdit = this.saveEdit.bind(this);
}
//lifecycle
componentDidMount() {
Firebase.database().ref('/nannydetails').on('value', (snapshot) => {
this.setState({
personDetails: snapshot.val()
})
})
}
//handle change
handleChange(event) {
this.setState({
[event.target.name]: event.target.value.toLowerCase()
});
}
//render data
renderData() {
return _.map(this.state.personDetails, (personDetail, key) => {
return(
<div className="pt-2 pb-1 m-2 border border-dark bg-warning row" key={key}>
<div className="col-md-3 col-xs-3 text-right">
<img src={require('./profile.png')} alt="cam"/>
</div>
<div className="col-md-9 col-xs-9 p-2">
<div className="headline">
<h1>Full Name: </h1>
<input className="form-control" type="text" name="fullName" defaultValue={personDetail.fullname} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>Contact Number: </h1>
<input className="form-control" type="text" name="phone" defaultValue={personDetail.phone} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>Experience: </h1>
<input className="form-control" type="text" name="experience" defaultValue={personDetail.experience} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>City: </h1>
<input className="form-control" type="text" name="city" defaultValue={personDetail.city} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>State: </h1>
<input className="form-control" type="text" name="state" defaultValue={personDetail.state} onChange={this.handleChange}/>
</div>
<button className="btn btn-success" type="button" onClick={() => this.saveEdit(key)}><i className="fa fa-pencil-square-o"></i> Save Edit</button>
<button className="btn btn-danger" type="button" onClick={() => this.handleDelete(key)}><i className="fa fa-trash"></i> Delete</button>
</div>
</div>
)
});
}
//handle save edit
saveEdit(key) {
// Firebase.database().ref(`/nannydetails/${key}`).push({
// fullname: this.state.fullname,
// phone: this.state.phone,
// experience: this.state.experience,
// city: this.state.city,
// state: this.state.state
// });
console.log(this.state.fullname);
console.log(this.state.phone);
console.log(this.state.experience);
console.log(this.state.city);
console.log(this.state.state);
}
//handle delete
handleDelete(key) {
const user= Firebase.database().ref(`/nannydetails/${key}`);
user.remove();
}
render() {
return (
<div className="container mt-4">
{this.renderData()}
</div>
);
}
}
export default Modify;
Update the change handler to update/mutate the existing state instead of adding a new property(key), i.e. on this.state.personDetails.X vs. this.state.X
//handle change
handleChange(event) {
// update the personDetails object you set in state on mount
const newPersonDetails = { ...this.state.personDetails };
newPersonDetails[event.target.name] = event.target.value.toLowerCase();
this.setState({
personDetails: newPersonDetails,
});
}
Make this as a controlled component. Provide a initial state in you component and all the updated will done into the state & that will directly reflect into your component.
Initial State:
this.state = {
personDetails: {
fullname :'test'
},
detail: ''
};
Input Field
input className="form-control" type="text" name="fullName" value={this.state.personDetail.fullname} onChange={this.handleChange}
onChange is only triggered when the value of input changes. The defaultValue can be defined in the initial state, and the defaultValue is overwritten when the value of input changes.
The best way to approach is to have a main component take data from any api (firebase in your case) and second child component which will be responsible to take input as props and return input(default/modified) on form action(submit). That way if data changes you get modified data else the default one.
class FormComp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: this.props.form.name,
};
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
submit(e) {
e.preventDefault();
console.log(this.state);
}
handleChange(e) {
this.setState({name: e.target.value});
}
handleChan
render() {
return (
<div>
<form onSubmit={this.submit}>
<input
type="text"
value={this.state.name}
onChange={this.handleChange}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
Refer to that fiddle for example. https://jsfiddle.net/papish19/ys6utv3k/7/

How do I validate textfields in a dynamic array

I have one mother component and one child component. In the child component there are two textfields (name and email) and an add button. When the user presses on add a new set of the same textfields will be rendered. I save those textfields in my mother component in an array. I want to have a validate function that checks of the values are valid. If not i want to give the error props a true value so that the user can see that field is wrong. The only problem i have is figuring out how to give right textfield in the array the error value. Because now every textfield will get the error value.
Child Component:
import React from 'react';
import TextField from '#material-ui/core/TextField';
import AddIcon from '#material-ui/icons/Add';
import Minus from '#material-ui/icons/Delete';
import Button from '#material-ui/core/Button';
class People extends React.Component {
constructor(props){
super(props);
}
render(){
const {classes} = this.props;
return (
<div>
{this.props.people.map((person, index) => (
<div key={person}>
<div className="container">
<div className="row">
<div className="col s2">
<Button
mini
variant="fab"
color="primary"
aria-label="minus"
onClick={e =>
this.props.removeRow(index)}
data-toggle="tooltip"
>
<Minus/>
</Button>
</div>
<div>
<div className="col s5">
<TextField
name="name"
id="standard-dense"
label="Teamlid naam"
margin="dense"
value={person.name}
error={e =>
this.props.validate(e, index)}
onChange={e =>
this.props.onChange(e, index)}
/>
</div>
<div className="col s5">
<TextField
name="email"
id="standard-dense"
label="Teamlid email"
margin="dense"
value={person.email}
error={e =>
this.props.validate(e, index)}
onChange={e =>
this.props.onChange(e, index)}
/>
</div>
</div>
</div>
</div>
</div>
))}
<div className="container">
<div className="row">
<div className="col s2">
<Button
mini
variant="fab"
color="primary"
aria-label="Add"
onClick={this.props.addRow}
data-toggle="tooltip"
className="btn btn-xs btn-primary"
data-original-title="">
<AddIcon/>
</Button>
</div>
<div className="col s5">
</div>
<div className="col s5">
</div>
</div>
</div>
</div>
);
}
};
export default People;
Mother Component:
import React, {Component} from 'react';
import People from './People';
class Form extends React.Component{
constructor(props){
super(props);
this.state = {
people: []
}
}
addRow = () => {
this.setState(previousState => {
return {
people: [...previousState.people, {name: "", email: "", error:false}]
};
});
};
removeRow = index => {
this.setState(previousState => {
const people = [...previousState.people];
people.splice(index, 1);
return { people };
});
};
//onChange functie van de teamleden rijen
onChange = (event, index) => {
const { name, value } = event.target;
this.setState(previousState => {
const people = [...previousState.people];
people[index] = { ...people[index], [name]: value };
return { people };
});
};
validate = (event,index) => {
event.preventDefault();
if(!event.target.value){
return true;
}else{
return false;
}
};
render(){
const {classes} = this.props;
return(
<form>
<People
addRow={this.addRow}
removeRow={this.removeRow}
onChange={this.onChange}
people={this.state.people}
validate={this.validate}
/>
<button onClick={this.validate}>Validate</button>
</form>
);
}
}
export default Form;
I know that the validate function probably needs the index of the wrong textfield. But I don't know how to properly implement it.

How to check/uncheck a list of checkboxes in react

I have a room page and in that page I have a list of sensors attached to that room, those sensors can be selected using a checkbox, like so:
<div className="checkboxRowContent">
{sensors.map(s => {
return (
<div className="checkboxElementWrapper" key={s.id}>
<label htmlFor={`sensor${s.id}`}>
<div className="checkboxLabel">
<Link to={`/sensors/edit/${s.id}`}>{s.name}</Link>
</div>
<input
type="checkbox"
id={`sensor${s.id}`}
name="sensorId"
value={s.id}
checked={s.roomId === values.id}
onChange={handleCheckbox}
/>
<span className="checkbox" />
</label>
</div>
);
})}
</div>
the problem is - this approach prohibits me from unchecking the checkbox (so if in db that sensor is attached to that room - that's it). How could I rewrite this so that I can check/uncheck this checkbox?
in the class you must have state for that,
a sample would be somewhat like this
export default class yourComponent extends React.Component {
state = {
checkedBoxes: []
}
handleCheckbox = (e, s) => {
const checkedBoxes = [...this.state.checkedBoxes];
if(e.target.checked) {
checkedBoxes.push(s)
} else {
const index = checkedBoxes.findIndex((ch) => ch.roomId === s.roomId);
checkedBoxes.splice(index, 1);
}
this.setState({checkedBoxes});
}
render() {
return(
<div className="checkboxRowContent">
{sensors.map(s => {
return (
<div className="checkboxElementWrapper" key={s.id}>
<label htmlFor={`sensor${s.id}`}>
<div className="checkboxLabel">
<Link to={`/sensors/edit/${s.id}`}>{s.name}</Link>
</div>
<input
type="checkbox"
id={`sensor${s.id}`}
name="sensorId"
checked={checkedBoxes.find((ch) => ch.roomId === s.roomId)}
onChange={(e) => handleCheckbox(e, s)}
/>
<span className="checkbox" />
</label>
</div>
);
})}
</div>
)
}
}
A state, checkedBoxes for getting all selected checkboxes.
A handler handleCheckbox for handling checkbox clicks,
You have handleCheckBox and a controlled component. We don't see what you do in the event handler but when it's controlled, you can check it by altering your sensors array (if in state/props) so s.roomId === values.id will be true.
If you don't want it to be controlled, you can probably use defaultChecked which will let you work with it in a different way.
see https://reactjs.org/docs/forms.html#controlled-components
import React, {Component} from 'react';
import axios from 'axios';
const Books = props=>(
<div className='form-group'>
<label>{props.book}
<input type='checkbox' name={props.name} className='form-check' onChange={props.onChange} />
</label>
</div>
)
class Total extends Component{
constructor(props){
super(props);
this.onChangeCheck = this.onChangeCheck.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state={
checkBoxes: [],
books:[]
}
}
componentDidMount() {
axios.get('http://localhost:3000/api/book/').then(resolve=>{
console.log(resolve.data.data);
this.setState({
books:resolve.data.data
}).catch(err=>{
console.log(err)
})
})
}
onChangeCheck(e){
console.log(e.target.name)
if(e.target.checked){
const array = this.state.checkBoxes;
array.push(e.target.name)
this.setState({
checkBoxes:array
})
}else{
const array = this.state.checkBoxes;
const index = array.indexOf(e.target.name);
console.log(index)
array.splice(index,1);
console.log(array);
this.setState({
checkBoxes:array
})
}
}
onSubmit(e){
e.preventDefault();
axios.put("http://localhost:8080/books/getTotal/",this.state.checkBoxes).then(resolve=>{
console.log(resolve)
alert(`Total price of books ${resolve.data}`);
}).catch(err=>{
console.log(err);
})
}
render(){
return(
<div className='card'>
<div className='card-header'>
</div>
<div className='card-body'>
<form className='form' onSubmit={this.onSubmit}>
<div className='form-group'>
{
this.state.books.map(object=>(
<Books name={object._id} book={object.name} onChange={this.onChangeCheck} />
)
)
}
</div>
<div className='form-group'>
<button type='submit' className='btn btn-success'>Get Total</button>
</div>
</form>
</div>
</div>
)
}
}
export default Total;

Handling Radio Buttons in Forms with React (Rails 5.1)

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()} />

Categories

Resources