Dynamic Form in react with dynamic fields - javascript

I did looking for but i did not find an answer for my problem.
I need to create a dynamic form with dynamic fields, with the order that the user need.
import { useState } from "react";
const DemoLateral = () => {
const itemDataObject = {
title_item_lateral: '' ,
text_item_lateral: [],
image_lateral: [
{
title_image_lateral: '',
path_image_lateral: '',
}
],
document_lateral: [],
links: [
{
title_link:'' ,
link: ''
}
]
};
const addFields = () => {
let newItemField;
newItemField = itemDataObject;
setItems([...items, newItemField]);
};
const [items, setItems] = useState([]);
const [select, setSelect] = useState([]);
console.log(items);
console.log('select: ', select);
const handleChange = () => {
//let index =
//let name = items[i][e.target.name]=[e.target.value];
console.log();
};
const submitForm = (e) => {
e.preventDefault();
};
console.log(select);
return (
<>
<h3 className="ms-5 mb-5"> AÑADIR ITEMS </h3>
<div className="container">
<form onSubmit={submitForm} className=''>
<div>
{items.map((input, i)=> (
<>
<div className="row align-items-center row mb-4" key={i}>
<label htmlFor="exampleFormControlSelect1">Selecciona el Campo</label>
<div className="col-2" key={i}>
<select className="form-control" id="exampleFormControlSelect1"
onChange={(e) => setSelect([select[i]=e.target.value])} key={i}>
<option>Subtitulo</option>
<option>Imagen</option>
<option>Link</option>
<option>Texto</option>
</select>
</div>
<div className='col-8'>
<input
placeholder="desde From"
id={i}
className='form-control'
value={select[i]}
onChange= {handleChange(i)}
type="text"
required
/>
</div>
<button className="btn btn-danger col-1" >Borrar</button>
</div>
</>
))
}
</div>
<button className="btn btn-success me-4 mt-5" type='submit'>AddSubmit</button>
</form>
<div className="mt-5 text-center">
<button className="btn btn-primary me-4 mb-4" value='items' onClick={addFields}>Add Items</button>
</div>
</div>
</>
);
};
export default DemoLateral;
with this code i try to create a dynamic form with fields, that would be set in the form like the user need:
p.e:
'subtitle'
'image'
'text'
'text'
'link'
'image'
for this i create a select to choose the type of field, and try to put the select in the attribute name, for then when submit all works.
But i can not achieve. :-(
Where is my wrong....
maybe there are other way to do the same kind of form?

Related

dynamic form adding or removing not working after mapping values in react

I made a dynamic form ,but When I save the values ​​that I got with API in ‍haveFaq and map them ,removeFields and addFields does not work
I save the values ​​that I receive from the API in haveFaq like this:
[
{ question: "question1", answer: "answer1" },
{ question: "question2", answer: "answer2" },
{ question: "question3", answer: "answer3" },
];
const [haveFaq, setHaveFaq] = useState([]);
const [tempData, setTempData] = useState([
{
question: "",
answer: "",
},
]);
const handleFormChange2 = (event, index) => {
let data = [...tempData];
data[index][event.target.name] = event.target.value;
setTempData(data);
};
const addFields = () => {
let object = {
question: "",
answer: "",
};
setTempData([...tempData, object]);
};
const removeFields = (index) => {
let data = [...tempData];
data.splice(index, 1);
setTempData(data);
};
Attention
But if we map tempData instead of haveFaq The form works correctly,Because its initial values ​​are empty
<>
{haveFaq.map((form, index) => {
return (
<div key={index + 1}>
<div className="form-row">
<div className="col-md-11 mb-3">
<label className="font-weight-bold" for="">
question
</label>
<input
className="form-control"
name="question"
placeholder="question"
onChange={(event) => handleFormChange2(event, index)}
value={form.question}
/>
</div>
<div className="col-md-1 mt-4 iconSelf">
<p
className="btnRemove"
type="button"
onClick={() => removeFields(index)}
>
❌
</p>
</div>
</div>
<div className="form-row">
<div className="col-md-12 mb-3">
<label className="font-weight-bold" for="">
answer
</label>
<textarea
rows={5}
className="lineHeightAnswer form-control"
name="answer"
placeholder="answer"
onChange={(event) => handleFormChange2(event, index)}
value={form.answer}
/>
</div>
</div>
<hr />
</div>
);
})}
<div>
<button
className="btn btn-info"
type="button"
onClick={addFields}
>
Add New
</button>
</div>
</>

How to reset the Modal input states after clicking the modal close button in React?

After giving the input of mobile number when i close the modal, then again after clicking the Form Submit button, input box shows the previously entered number. I believe model captures the previous state by itself and i want to clear it whenever we open the modal.
After entering the OTP input in Modal, if we click on the Edit mobile number and when we again enter the input in mobile number, then previously entered OTP also shows up. I want to clear it after entering the new mobile number.
I have already tried setting the state to ("") on the onclick events but it doesn't help.
I could really use some help.
Code:
export default function Form() {
// form default hooks
const {register,handleSubmit, formState: { errors, isValid },} = useForm({mode: "onChange",criteriaMode: "all",});
const {register: register2,handleSubmit: handleSubmit2,formState: { errors: errors2 },} = useForm({mode: "onChange",});
const [verifyOTPFlag, setVerifyOTPFlag] = useState(true);
//modal hooks
const [mobileNumberInput, setMobileNumberInput] = useState(true);
const [postVerificationMessage, setPostVerificationMessage] = useState();
const [otpVerificationResult, setOtpVerificationResult] = useState(false);
const [show, setShow] = useState(false);
const [otpInput, setShowOtpInput] = useState(false);
const [number, setNumber] = useState("");
const [OTP, setOTP] = useState("");
const [counter, setCounter] = useState(59);
// post office and aadhar hooks
const [districtName, setDistrictName] = React.useState("");
const [stateName, setStateName] = React.useState("");
//-------------------------modal functionalities start-------------------------
const handleClose = () => {
setShow(false);
setShowOtpInput(false);};
const handleShow = () => {
setShow(true);};
const showOtpInput = () => {
getOTP(number);
setOTP("");
setShowOtpInput(true);
setMobileNumberInput(false);
setOtpVerificationResult(false);
};
const onSubmit = (data) => {
data["district"] = districtName;
data["state"] = stateName;
setMobileNumberInput(true);
setPostVerificationMessage("");
setNumber("");
if (verifyOTPFlag) {
if (isValid) {
setShow(true);
} else {}
}
};
const onSubmit2 = () => {
verifyOTP(OTP)
.then((resp) => {
alert("OTP verification Successful");
setPostVerificationMessage(<p className="text-danger">OTP verification Successful</p>);
setVerifyOTPFlag(false);
setOtpVerificationResult(true);
setShowOtpInput(false);
})
.catch((error) => {
setPostVerificationMessage(<p className="text-danger"> OTP verification Failed. Kindly enter the correct OTP</p> )
setOtpVerificationResult(true);
});};
const onClickEditMobileNo = () => {
setShowOtpInput(!otpInput);
setOtpVerificationResult("");
setOTP("");
setMobileNumberInput(true);
};
return (
<div>
<div className="form-group">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="container mt-2">
<label className="control-label">Full Name : </label>
<input
className="text-uppercase input-group input-group-lg form-control"
type="text"
name="username"
{...register("username", {
required: "Username is Required",
pattern: {
value: /^[a-zA-Z0-9\s,\'-\/:&]*$/,
message: "Entered value does not match valid name format",
},
})}
/>
<p className="errorMsg">{errors.username?.message}</p>
<label className="control-label">C/o : </label>
<input
className="text-uppercase input-group input-group-lg form-control"
type="text"
name="careof"
{...register("careof", {
required: "Name of c/o is Required",
pattern: {
value: /^[a-zA-Z0-9\s,\'-\/:&]*$/,
message: "Entered value does not match valid name format",
},
})}
/>
<p className="errorMsg">{errors.careof?.message}</p>
<label className="control-label">House No./Bldg./Apt : </label>
<input
className="text-uppercase input-group input-group-lg form-control"
type="text"
name="houseno"
{...register("houseno", {
required: "House number is Required",
})}
/>
<p className="errorMsg">{errors.houseno?.message}</p>
<div className="text-center">
<button
className="button btn-primary btn px-3 mr-1"
onClick={onSubmit}
>
Form Submit
</button>
</div>
</div>
</form>
<form onSubmit={handleSubmit2(onSubmit2)}>
{/* modal starts */}
<Modal show={show} onHide={handleClose} backdrop="static">
<Modal.Header
style={{
backgroundImage: "linear-gradient(180deg , #3f55c4 , #95a4f0",
}}
closeButton></Modal.Header>
<Collapse in={mobileNumberInput}>
<div
className="input-field-sb mt-4 ml-5"
style={{ width: "80%" }}
>
<input className="input-sb" maxLength="10" type="text" id="mobile"
// onChange={(e) => setNumber(e.target.value)}
{...register2("mobile", {
required: "Mobile number is Required",
pattern: {
value: /^[5-9]\d{9}$/,
message: "Entered value does not match valid name format",
},
onChange: (e) => setNumber(e.target.value),})}/>
<p className="errorMsg">{errors2.mobile?.message}</p>
<label className="label-sb" htmlFor="mobile">Enter Mobile Number: </label>
</div>
</Collapse>
<Modal.Body>
<Collapse in={otpInput}>
<div>
<div className="d-flex justify-content-center">
<p className="text-center"><strong>{" "} We sent you a code to verify your mobile number{" "}</strong>{" "}
<b className="text-danger">{number}</b>
<span className="mobile-text"> Enter your OTP code here</span>
</p>
</div>
<input className="input-sbj" maxLength="6" onChange={(e) => setOTP(e.target.value)}/>
</div>
</Collapse>
<Collapse in={otpVerificationResult}>
<div className="text-center">{postVerificationMessage}</div>
</Collapse>
<div className="d-flex justify-content-center col-md-12">
{otpInput ? (
<Button className="px-5 align-middle mt-4" variant="secondary" onClick={onSubmit2}>{" "} Verify</Button>) : (
<Button className="px-5 align-middle mt-4" variant="secondary"
onClick={errors2.mobile ? null : showOtpInput}>
{" "} Send OTP </Button>)}
</div>
<div className="row">
<div className={otpInput ? "col-sm-6" : "col-sm-6 d-none"}>
<a className="btn">Resend OTP in 00:{counter}</a>
</div>
<div
className={otpInput? "text-right col-sm-6": "text-right col-sm-6 d-none"}>
<a className="btn" onClick={onClickEditMobileNo}> Edit Mobile number </a>
</div>
</div>
</Modal.Body>
</Modal>
</form>
{/* modal ends */}
</div></div>);}
The easiest way would be just adding e.target.reset() in your onSubmit function.
const onSubmit = (data, e) => {
data["district"] = districtName;
data["state"] = stateName;
setMobileNumberInput(true);
setPostVerificationMessage("");
setNumber("");
e.target.reset();
if (verifyOTPFlag) {
if (isValid) {
setShow(true);
} else {}
}
};
also keep the setNumber("") like this. I think this will solve the problem.

React Hook useRef return style property as null

I am working on a modal in which I have to access the id property of an HTML element to change its display property. I have used the useRef Hook to attain it but got the following errors.
TypeError: Cannot read property 'style' of null
Modal.js:- The code is of 1000 Lines so I am sharing only the part where I have used it.
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const addProjectModal = useRef(null);
const closeAddProjectModal = () => {
addProjectModal.current.style.display = 'block';
};
const renderAddProject = () => {
return (
<div>
<div className="task-accordion" ref={addProjectModal} style={{ display: 'none' }}>
<i
className="fa fa-close close-icon"
id="closeIconn"
onClick={() => closeAddProjectModal()}
aria-hidden="true"
></i>
<h4>ADD Project</h4>
<div className="form-group">
<textarea
id="textAreaAddProject"
type="text"
style={{ height: '30vh' }}
placeholder="Project Title"
name="Title"
className="form-control"
onChange={(e) => {
setprojectTitle(e.target.value);
}}
required
/>
</div>
<div className="col-sm-12 add-mupps-button">
<button type="submit" onClick={() => postProjectApiCall()}>
Save
</button>
</div>
</div>
</div>
);
};
<div className="col-sm-8 last-div padding-0">
{isAddProjectClicked && renderAddProject()}
</div>
You can change the visibility of your model by creating a state like this.
import { useState } from "react";
function App() {
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const [addProjectModal, openAddProjectModal] = useState("none"); // creating the state
const renderAddProject = () => {
return (
<div>
<div className="task-accordion" style={{ display: addProjectModal }}> {/* Set that as display's property value */}
<i
className="fa fa-close close-icon"
id="closeIconn"
onClick={() => openAddProjectModal("block")}
aria-hidden="true"
></i>
<h4>ADD Project</h4>
<div className="form-group">
<textarea
id="textAreaAddProject"
type="text"
style={{ height: "30vh" }}
placeholder="Project Title"
name="Title"
className="form-control"
required
/>
</div>
<div className="col-sm-12 add-mupps-button">
<button type="submit">Save</button>
</div>
</div>
</div>
);
};
return (
<>
{isAddProjectClicked && renderAddProject()}
<button onClick={() => openAddProjectModal("block")}>Click Me</button>
{/* Change the state using any event you want */}
</>
);
}
export default App;
Let me know if you need further support.
If the component is not mounted, the ref is null so you should add condition to it
const closeAddProjectModal = () => {
if(addProjectModal.current) {
addProjectModal.current.style.display = 'block';
}
};
or just use optional chaining
const closeAddProjectModal = () => {
addProjectModal.current?.style.display = 'block';
};
Use react to set the style:
function AddProjectModal(props) {
const [visible, setVisible] = useState(visible);
return <div style={{display: visible?'block':'none'}}>
<button onClick={()=>setVisible(false)}>
Close
</button>
</div>
}
No need for a ref to change the style or a class

Unable to set property of a single element in an array

In an array named admin, I want to show a div on the click of a button "update" when when I do so, the div shows below all the elements of the array. I only want it to show below the selected element.
function Admin(props) {
const [showMe, setShowMe] = React.useState(false);
const [updateName, setupdateName] = React.useState("");
const [updateDesc, setupdateDesc] = React.useState("");
return (
<div>
<div className="adminProducts">
{props.admin.map((x, i) => (
<div>
{showMe ? (
<div className="UpdateSection">
<input
type="text"
placeholder="Product Name"
onChange={e => setupdateName(e.target.value)}
value={updateName}
/>
<br />
<textarea
placeholder="Product Description"
onChange={e => setupdateDesc(e.target.value)}
value={updateDesc}
/>
<button
type="submit"
onClick={e => {
props.UpdateInfo({ updateName, updateDesc }, { x }, i);
setupdateName("");
setupdateDesc("");
}}
>
Save
</button>
</div>
) : null}
<div>{x.name}</div>
<div>
<button onClick={e => setShowMe(!showMe)}>
{showMe ? "Close" : "Update"}
</button>
</div>
</div>
))}
</div>
</div>
);
}
I want to set showMe as true for individual elements in array so that the div with classname UpdateSection only shows for that specific element and not for any other element.
You can save the id of the element you want to be shown:
const [showMe, setShowMe] = React.useState([]);
// ...
const isShown = el => showMe.includes(el.id);
const toggleShown = el => {
setShowMe(shown => {
if (shown.includes(el.id)) {
return shown.filter(id => id !== el.id);
}
return [...shown, el.id];
});
};
//...
return (
<div>
<div className="adminProducts">
{props.admin.map((x, i) => (
<div>
{isShown(x) ? (
//...
<div>
<button onClick={e => toggleShown(x)}>
{isShown(x) ? "Close" : "Update"}
</button>
</div>
</div>
))}
</div>
</div>
);

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.

Categories

Resources