React - How to handle nested forms and its value - reactjs

I have to setup one form which have nested values in single form
Basically I need to send data in below format to API
Payload: {"name": "The org name", "detail": "The org detail", "attributes": {"address": {"lines":[], "city":"", "state": "", "country": "India", "zip": ""}, "contacts":[{"name": "abc", "phone": "PH"}, {"name": "x", "phone": "PH"}] }}
I used react-bootstrap for handling forms.
below are the currrent code of my form
constructor(props) {
super(props);
this.state = this.getInitialState();
}
getInitialState() {
const initialState = {
organizationForm: {
name: '',
detail: '',
type: 'org',
attributes: {
contacts: [{
name: '',
phone: ''
}],
address: {
lines: [],
city: '',
state: '',
country: '',
zip: ''
}
}
},
errors: {}
};
return initialState;
}
handleChange(e) {
const organizationForm = this.state.organizationForm;
var key = e.target.name;
var value = e.target.value;
organizationForm[key] = value;
this.setState({
organizationForm
});
}
Below are the code of form
<Col className="create-content-wrap" sm={12}>
<form className="">
<FormGroup className="custom-form-group required row">
<ControlLabel className="custom-form-control-label col-sm-3">
Name
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="name"
value={organizationForm.name}
onChange={this.handleChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group required row">
<ControlLabel className="custom-form-control-label col-sm-3">
Detail
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="detail"
componentClass="textarea"
value={organizationForm.detail}
onChange={this.handleChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group row">
<ControlLabel className="custom-form-control-label col-sm-3">
Address
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="lines"
componentClass="textarea"
value={organizationForm.lines}
onChange={this.handleAddressChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group row">
<ControlLabel className="custom-form-control-label col-sm-3">
City
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="city"
value={organizationForm.attributes.address.city}
onChange={this.handleAddressChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group row">
<ControlLabel className="custom-form-control-label col-sm-3">
State
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="state"
value={organizationForm.attributes.address.state}
onChange={this.handleAddressChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group row">
<ControlLabel className="custom-form-control-label col-sm-3">
Country
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="country"
value={organizationForm.attributes.address.country}
onChange={this.handleAddressChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group row">
<ControlLabel className="custom-form-control-label col-sm-3">
Zipcode
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="number"
name="zip"
value={organizationForm.attributes.address.zip}
onChange={this.handleAddressChange.bind(this)}
/>
</FormGroup>
<FormGroup className="custom-form-group row">
<ControlLabel className="custom-form-control-label col-sm-3">
Contacts
</ControlLabel>
<FormControl
className="custom-form-control col-sm-9"
type="number"
name="contacts"
value={organizationForm.attributes.contacts}
onChange={this.handleChange.bind(this)}
/>
</FormGroup>
</form>
</Col>
I am noobie in react js world. How can i bind nested fields of address and contacts ?

You can add couple of methods to handle for address and attributes like below
Way - 1
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="city"
value={organizationForm.attributes.address.city}
onChange={this.handleAddressChange(this)}
/>
handleAddressChange = (e) => {
const organizationForm = this.state.organizationForm;
let address = organizationForm.attributes.address;
var key = e.target.name;
var value = e.target.value;
address[key] = value;
organizationForm.attributes.address = address;
this.setState({
organizationForm
});
}
This way your form is loosely coupled also . So if any change happens in the child object will not affect the other one. Similarly you can add for all the nested objects like address, attribute etc.
Way - 2
You can do the same by below but you need to handle which object your modifing with in the same method .
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="city"
value={organizationForm.attributes.address.city}
onChange={event => { this.handleChange(event, this.state.organizationForm.attributes.address ,'address'); }}
/>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="city"
value={organizationForm.attributes.address.city}
onChange={event => { this.handleChange(event, this.state.organizationForm.attributes.contacts ,'contacts'); }}
/>
<FormControl
className="custom-form-control col-sm-9"
type="text"
name="city"
value=onChange={event => { this.handleChange(event, this.state.organizationForm ,'organizationForm'); }}
/>
handleChange = (e , object , type) => {
const organizationForm = this.state.organizationForm;
var key = e.target.name;
var value = e.target.value;
object[key] = value;
if(type === 'address'){
organizationForm.attributes.address = object;
} else if (type === 'contacts'){
organizationForm.attributes.contacts = object;
}
this.setState({
organizationForm : organizationForm
});
}

I suggest that you keep your component state flat, just one key for each field in the form. When you submit the form, then you can format it into the shape necessary for the API.

This might not work for all cases, but it worked for me and might be helpful to anyone searching this now. I had a form that rendered different input elements, but it also rendered a button that opened up a modal component with a form unrelated to the larger form surrounding it. To solve the problem of this nested form: first, I removed the form elements in the modal (the <form>) and left just the <input> inside the component. Then, in that element, I added:
onChange = {e => setValue(e.target.value)}
After this, I made a state variable that looked like this: const [value, setValue] = useState("");. And finally, I added a button under the input with something like onClick={handleSubmit} and defined a function that submitted the form with value as the result of the form submission.
So essentially, I replaced the internal state of the nested form with React state, removed the form elements and kept the input, and then created a button that would handle my submission. That way, I bypassed having nested forms by just having an input element instead of a full form.

Related

how to avoid repeating pattern when creating form in react

I am going to update the form with each keystroke with useState Hook this way I have to add an onChange event listener plus a function for each input and as you can imagine it's going to be lots of functions how can I avoid repeating myself?
function firstNamInput(){
}
function lastNameInput(){
}
function emailInput(){
}
function phoneInput(){
}
function addressInput(){
}
function genderInput(){
}
function jobInput(){
}
function ageInput(){
}
in react we try to collect form data on every keystroke but in the past, we used to collect form data when submit clicked. so because of this new approach, we have to listen for every keystroke on every input! isn't this crazy? yes, kind of but there is a way to go around I am going to explain it to you :
first I am going to talk about Rules you have to know about inputs:
we are going to use a useState Hook and we pass an object to it which contain:
a property for every input that's single
a property for group inputs (for example if there is checkboxes for gender we create only one property for gender)
what attributes every input should have?
name (it's going to be equal to its property in the useState)
value or checked (as a rule of thumb if the inputs gets true or false its usually checked vice versa )
onChange event
so let's create useState I am going to have a total of 7 properties in the object:
const [formData, setFormData] = React.useState(
{
firstName: "",
lastName: "",
email: "",
comments: "",
isFriendly: true,
employment: "",
favColor: ""
}
)
we need to create a function for the onChange event i am going to make it as reusable as possible it's going to get form data from each input and update it in our Hook.
function handleChange(event) {
const {name, value, type, checked} = event.target
setFormData(prevFormData => {
return {
...prevFormData,
[name]: type === "checkbox" ? checked : value
}
})
}
now we are going to add inputs look at note 2 again and now you are ready
import React from "react"
export default function Form() {
const [formData, setFormData] = React.useState(
{
firstName: "",
lastName: "",
email: "",
comments: "",
isFriendly: true,
employment: "",
favColor: ""
}
)
function handleChange(event) {
const {name, value, type, checked} = event.target
setFormData(prevFormData => {
return {
...prevFormData,
[name]: type === "checkbox" ? checked : value
}
})
}
function handleSubmit(event) {
event.preventDefault()
// submitToApi(formData)
console.log(formData)
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="First Name"
onChange={handleChange}
name="firstName"
value={formData.firstName}
/>
<input
type="text"
placeholder="Last Name"
onChange={handleChange}
name="lastName"
value={formData.lastName}
/>
<input
type="email"
placeholder="Email"
onChange={handleChange}
name="email"
value={formData.email}
/>
<textarea
value={formData.comments}
placeholder="Comments"
onChange={handleChange}
name="comments"
/>
<input
type="checkbox"
id="isFriendly"
checked={formData.isFriendly}
onChange={handleChange}
name="isFriendly"
/>
<label htmlFor="isFriendly">Are you friendly?</label>
<br />
<br />
<fieldset>
<legend>Current employment status</legend>
<input
type="radio"
id="unemployed"
name="employment"
value="unemployed"
checked={formData.employment === "unemployed"}
onChange={handleChange}
/>
<label htmlFor="unemployed">Unemployed</label>
<br />
<input
type="radio"
id="part-time"
name="employment"
value="part-time"
checked={formData.employment === "part-time"}
onChange={handleChange}
/>
<label htmlFor="part-time">Part-time</label>
<br />
<input
type="radio"
id="full-time"
name="employment"
value="full-time"
checked={formData.employment === "full-time"}
onChange={handleChange}
/>
<label htmlFor="full-time">Full-time</label>
<br />
</fieldset>
<br />
<label htmlFor="favColor">What is your favorite color?</label>
<br />
<select
id="favColor"
value={formData.favColor}
onChange={handleChange}
name="favColor"
>
<option value="red">Red</option>
<option value="orange">Orange</option>
<option value="yellow">Yellow</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="indigo">Indigo</option>
<option value="violet">Violet</option>
</select>
<br />
<br />
<button>Submit</button>
</form>
)
}

`getByRole` can't find the name of the appropriate textbox

Here is the test for the :
it("changes the state when body input is changed", () => {
render(<CommentForm />)
// let input = screen.getByLabelText("Your Name");
let input = screen.getByRole("textbox", { name: "Your Comment" });
fireEvent.change(input, { target: { value: "MyComment" } });
expect(input.value).toEqual("MyComment");
});
With the commented line it works (when I search with getByLabelText). Here is what I am getting when I try to find it with getByRole:
Unable to find an accessible element with the role "textbox" and name "Your Comment"
Here are the accessible roles:
document:
Name "":
<body />
--------------------------------------------------
generic:
Name "":
<div />
Name "":
<div
class="input-group"
/>
Name "":
<div
class="input-group"
/>
--------------------------------------------------
heading:
Name "Post a Comment":
<h2 />
--------------------------------------------------
textbox:
Name "Your Name":
<input
id="author-name"
name="author"
type="text"
value=""
/>
Name "":
<textarea
cols="30"
id="body"
name="body"
rows="10"
/>
So it seems like the name is empty but I am not sure why that is.
And here is the actual component:
import React from "react";
import useInput from "../hooks/useInput";
const CommentForm = ({ onSubmit }) => {
const { value: author, reset: resetAuthor, bind: bindAuthor } = useInput("");
const { value: body, reset: resetBody, bind: bindBody } = useInput("");
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({ author, body }, resetInputs);
};
const resetInputs = () => {
resetAuthor();
resetBody();
};
return (
<form onSubmit={handleSubmit}>
<h2>Post a Comment</h2>
<div className="input-group">
<label htmlFor="author-name">Your Name</label>
<input id="author-name" type="text" name="author" {...bindAuthor} />
</div>
<div className="input-group">
<label htmlFor="body">Your Comment</label>
<textarea
id="body"
name="body"
cols="30"
rows="10"
{...bindBody}
></textarea>
</div>
<button type="submit">Submit</button>
</form>
);
};
export default CommentForm;
Can anyone see the issue here. I am sure it should work and I don't see any reason why the author input can be grabbed with getByRole and name but this one can't.
I came across this problem when using a React Hook Form Controller for a Material UI TextField. Even though my TextField element had a label prop, no aria-label value was present in the HTML until I added the inputProps={{ "aria-label": "Email" }} prop to the TextField component.
Note: the aria-label is the value that the getByRole "name" actually refers to in this case. See docs for details.
<Controller
control={control}
defaultValue=""
id="email"
name="email"
render={({ onChange, ref }) => (
<TextField
autoComplete="email"
className={classes.textField}
error={Boolean(errors.email)}
fullWidth
helperText={errors.email && errors.email.message}
inputProps={{ "aria-label": "Email" }}
inputRef={ref}
label="Email"
onChange={onChange}
/>
)}
rules={{
pattern: {
message: "invalid email address",
value: EMAIL_REGEX,
},
required: true,
}}
/>
I think, your textarea has name=body, and you're looking for name=Your Comment. Try to use body instead.
Your use of <label htmlFor> is incorrect. Because you've given htmlFor, id and body the same values the DOM is getting confused and assigning the label content ("Your Comment") as the name. It's working with author by accident.
So:
<label htmlFor="author-name">Your Name</label>
<input id="author-name" type="text" name="author" />
Should be able to be checked with:
screen.getByRole('textbox', {name: author})
For the textarea if you do:
<label htmlFor="body">Your Comment</label>
<textarea
id="body"
name="bodyInput"
>
Then:
screen.getByRole('textarea', {name: body})
Should give you the result you're looking for.
Notice in both cases I'm checking the element's name value in the element where id matches the value of htmlFor. I am not checking the value of the <label> text itself.

Radio Buttons in React using Bootstrap always post same value

I am not getting an error, but the radio buttons I am writing are always sending the same value to the database, and I am not sure why or how to fix it.
UPDATE. I have attached the complete code. Hopefully this provides more detail. Everything is working but the radio buttons. Can someone shed some light as to how I can fix this?
import React from "react";
import axios from "axios";
import Form from 'react-bootstrap/Form'
import Row from 'react-bootstrap/Col'
import Button from 'react-bootstrap/Button'
import './Form.css'
class Home extends React.Component {
constructor(props) {
super(props);
this.state = { tickets: [] };
this.firstName = React.createRef();
this.lastName = React.createRef();
this.email = React.createRef();
this.category = React.createRef();
this.content = React.createRef();
this.urgency = React.createRef();
}
componentDidMount() {
this.getData();
}
getData = () => {
// Java Spring Boot uses port 8080
let url = "http://localhost:8080/tickets";
// axios.get(url).then(response => console.log(response.data));
axios.get(url).then(response => this.setState({ tickets: response.data }));
};
addTicket = () => {
let url = "http://localhost:8080/tickets";
axios.post(url, {
firstName: this.firstName.current.value,
lastName: this.lastName.current.value,
email: this.email.current.value,
category: this.category.current.value,
content: this.content.current.value,
urgency: this.urgency.current.value
}).then(response => {
// refresh the data
this.getData();
// empty the input
this.firstName.current.value = "";
this.lastName.current.value = "";
this.email.current.value = "";
this.content.current.value = "";
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<Form.Group className="Input">
<Form.Control type="text" name="firstName" placeholder="First Name" ref={this.firstName} />
</Form.Group>
<Form.Group className="Input">
<Form.Control type="text" name="lastName" placeholder="Last Name" ref={this.lastName} />
</Form.Group>
<Form.Group className="Input">
<Form.Control type="text" name="email" placeholder="Email" ref={this.email} />
</Form.Group>
<br></br>
<Form.Group className="dropdown">
<Form.Label>Select a Category:</Form.Label>
<Form.Control as="select" ref={this.category}>
<option value="hardware">Hardware</option>
<option value="software">Software</option>
<option value="internet">Internet</option>
<option value="other">Other</option>
</Form.Control>
</Form.Group>
<br></br>
<Form.Group className="Issue">
<Form.Label>Please Describe Your Issue:</Form.Label>
<Form.Control as="textarea" rows="7" ref={this.content} />
</Form.Group>
<fieldset>
<Form.Group as={Row}>
<Form.Label className="radio" column sm={12}>
Select the Urgency Level:<br></br>
</Form.Label>
<Form.Check className="radioButtons"
type="radio"
label="Urgent"
name="selectedOption"
value="Urgent"
ref={this.urgency}
/>
<Form.Check className="radioButtons"
type="radio"
label="Standard"
name="selectedOption"
value="Standard"
ref={this.urgency}
/>
<Form.Check className="radioButtons"
type="radio"
label="Low Priority"
name="selectedOption"
value="Low Priority"
ref={this.urgency}
/>
</Form.Group>
</fieldset>
<Button variant="secondary" type="button" className="submit" onClick={this.addTicket}>Submit Ticket</Button>
</form>
</div>
);
}
}
export default Home;
The issue is that you are pointing this.urgency to all of your Radio components 1 by 1. Here is a brief simulation of running your code from top to bottom:
<Form.Group as={Row}>
<Form.Label className="radio" column sm={12}>
Select the Urgency Level:<br></br>
</Form.Label>
<Form.Check
value="Urgent"
ref={this.urgency} // <-- this.urgency is now pointing to this 1st component
/>
<Form.Check
value="Standard"
ref={this.urgency} // <-- this.urgency is now pointing to this 2nd component
/>
<Form.Check
value="Low Priority"
ref={this.urgency} // <-- this.urgency is now pointing to this 3rd component
/>
</Form.Group>
So the final reference of this.urgency when the code is finished running is the 3rd component. So when you access this.urgency.current.value it will always return the 3rd component's value (i.e., Low Priority)
In React, you normally use state to hold these values - use ref sparsely and only if you really have to.
Here is an example of a solution:
constructor(props) {
super(props);
this.state = {
tickets: [],
urgency: "" // state for urgency
};
<Form.Group as={Row}>
<Form.Label className="radio" column sm={12}>
Select the Urgency Level:<br></br>
</Form.Label>
<Form.Check
value="Urgent"
onChange={() => this.setState({ urgency: "Urgent" })}
/>
<Form.Check
value="Standard"
onChange={() => this.setState({ urgency: "Standard" })}
/>
<Form.Check
value="Low Priority"
onChange={() => this.setState({ urgency: "Low Priority" })}
/>
</Form.Group>
axios
.post(url, {
urgency: this.state.urgency
})
you ref three elements to the same ref. you can walk around this without changing your login and adding state by helper function. in that way you don't change behavoir of your code (is not perferct ...) but you avoid rendering forced by setState.
import React from "react"
import Form from 'react-bootstrap/Form'
import Row from 'react-bootstrap/Col'
import Button from 'react-bootstrap/Button'
class Home extends React.Component {
constructor(props) {
super(props);
this.state = { tickets: [] };
this.firstName = React.createRef();
this.lastName = React.createRef();
this.email = React.createRef();
this.category = React.createRef();
this.content = React.createRef();
this.urgency = React.createRef();
}
componentDidMount() {
}
getData = () => {
};
addTicket = () => {
console.log({
firstName: this.firstName.current.value,
lastName: this.lastName.current.value,
email: this.email.current.value,
category: this.category.current.value,
content: this.content.current.value,
urgency: this.urgency.current.value
})
};
handleCheckBoxChange(ref, event){
if(event.target.checked){
ref.current = {value: event.target.value}
}
}
render() {
return (
<div>
<form onSubmit={this.addTicket}>
<Form.Group className="Input">
<Form.Control type="text" name="firstName" placeholder="First Name" ref={this.firstName} />
</Form.Group>
<Form.Group className="Input">
<Form.Control type="text" name="lastName" placeholder="Last Name" ref={this.lastName} />
</Form.Group>
<Form.Group className="Input">
<Form.Control type="text" name="email" placeholder="Email" ref={this.email} />
</Form.Group>
<br></br>
<Form.Group className="dropdown">
<Form.Label>Select a Category:</Form.Label>
<Form.Control as="select" ref={this.category}>
<option value="hardware">Hardware</option>
<option value="software">Software</option>
<option value="internet">Internet</option>
<option value="other">Other</option>
</Form.Control>
</Form.Group>
<br></br>
<Form.Group className="Issue">
<Form.Label>Please Describe Your Issue:</Form.Label>
<Form.Control as="textarea" rows="7" ref={this.content} />
</Form.Group>
<fieldset>
<Form.Group as={Row}>
<Form.Label className="radio" column sm={12}>
Select the Urgency Level:<br></br>
</Form.Label>
<Form.Check className="radioButtons"
type="radio"
label="Urgent"
name="selectedOption"
value="Urgent"
onClick={this.handleCheckBoxChange.bind(this, this.urgency)}
/>
<Form.Check className="radioButtons"
type="radio"
label="Standard"
name="selectedOption"
value="Standard"
onClick={this.handleCheckBoxChange.bind(this, this.urgency)}
/>
<Form.Check className="radioButtons"
type="radio"
label="Low Priority"
name="selectedOption"
value="Low Priority"
onClick={this.handleCheckBoxChange.bind(this, this.urgency)}
/>
</Form.Group>
</fieldset>
<Button variant="secondary" type="button" className="submit" onClick={this.addTicket}>Submit Ticket</Button>
</form>
</div>
);
}
}
export default Home;

Npm reactjs -input -validators

index.js:1446 Warning: React does not recognize the validatorErrMsg prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase validatorerrmsg instead. If you accidentally passed it from a parent component, remove it from the DOM element.
in input (created by t)
in div (created by t)
in t (created by t)
in div (created by t)
in t (created by t)
in div (created by t).
This is the Error which stuck my brain since two days..
The required js file include following code .
class SharedComponent extends Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
data: {},
};
}
handleChange(event, inputValue, inputName, validationState, isRequired) {
const value = (event && event.target.value) || inputValue;
const { data } = this.state;
data[inputName] = { value, validation: validationState, isRequired };
this.setState({
data,
});
const formData = formInputData(this.state.data);
const isFormValid = formValidation(this.state.data);
console.log('shared component',formData+isFormValid);
}
handleSubmit(event) {
event.preventDefault();
const isFormValid = formValidation(this.state.data);
if (isFormValid) {
// do anything including ajax calls
this.setState({ callAPI: true });
} else {
this.setState({ callAPI: true, shouldValidateInputs: !isFormValid });
}
}
render() {
const passwordValue = this.state.data.password && this.state.data.password.value;
return (
<form className="example">
<Row>
<Col md={6}>
<Field required
label="Full Name" name="fullName" placeholder="First Last"
onChange={this.handleChange}
value={this.state.data.fullName}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
</Col>
<Col md={6}>
<Field
validator="isEmail" required
label="Email" name="email" placeholder="Email"
onChange={this.handleChange}
value={this.state.data.email}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
</Col>
</Row>
<Row>
<Col md={6}>
<Field
validator="isAlphanumeric" required minLength={8}
minLengthErrMsg="Short passwords are easy to guess. Try one with atleast 8 characters"
label="Create a password" name="password" type="password" placeholder="Password"
onChange={this.handleChange}
value={this.state.data.password}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
</Col>
<Col md={6}>
<Field
validator="equals" required comparison={passwordValue}
validatorErrMsg="These passwords don't match. Try again?"
label="Confirm password" name="confirmPassword" type="password" placeholder="Password"
onChange={this.handleChange}
value={this.state.data.confirmPassword}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
</Col>
</Row>
<Field
required
requiredErrMsg="Enter your address so we can send you awesome stuff"
label="Address" name="address" placeholder="1234 Main St"
onChange={this.handleChange}
value={this.state.data.address}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
<Field
label="Address 2"
name="address2" placeholder="Apartment, studio, or floor"
onChange={this.handleChange}
value={this.state.data.address2}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
<Row>
<Col md={6}>
<Field
maxLength={20} required label="City"
name="inputCity"
onChange={this.handleChange}
value={this.state.data.inputCity}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
</Col>
<Col md={3}>
<label htmlFor="inputState">State</label>
<select
name="inputState" className="form-control"
onChange={this.handleChange}
value={this.state.data.inputState ? this.state.data.inputState.value : ''}
>
<option>Choose...</option>
<option value="ALABAMA">ALABAMA</option>
<option value="ALASKA">ALASKA</option>
<option value="ARIZONA">ARIZONA</option>
<option>...</option>
</select>
</Col>
<Col md={3}>
<Field
validator="isPostalCode" locale="US" required maxLength={10}
validatorErrMsg="Enter a valid US Zip"
label="Zip" name="inputZip"
onChange={this.handleChange}
value={this.state.data.inputZip}
shouldValidateInputs={this.state.shouldValidateInputs}
/>
</Col>
</Row>
<button
type="submit" onClick={this.handleSubmit} className="btn btn-danger"
>Sign up
</button>
{this.state.callAPI
?
<pre className="resultBlock">
{JSON.stringify(formInputData(this.state.data), null, 4)}
</pre>
: null
}
</form>
);
}
}
This is my js file for rect js form with validation .
Can Anyone help to walk through it ..THANKS.
The message you're seeing means that the Field component isn't itself doing anything with the validatorErrMsg prop; instead, it's simply passing it down to the DOM node (probably an input element). That property has no special meaning to input elements, so it has no effect.
You need to check the documentation for whatever library you're getting Field from. That should specify what props you can use.
Alternatively, if you created the Field component yourself, you need to implement the logic for handling the validatorErrMsg prop yourself, within that component.

how to update dynamically generated form in react js

I have a code here where I have to do update operation. I'm generating the form fields dynamically based on the result of get operation. The form fields are pre-populated. On change event on the input field in triggering an error data.map is not a function on keypress. I can change for single fields with different names. But unable to update json array state. Please help. I have attached the code snippet below.
export default class PocDetail extends React.Component {
constructor(props) {
super(props);
this.state={
isLoading:true,
editData:[],
};
}
componentDidMount(){
this.renderData();
}
renderData(){
var self = this;
const getOrgId =
window.location.href.substr(window.location.href.lastIndexOf('/') + 1);
const url = "some api";
get(url+"Organisation/"+getOrgId+"/pointOfContacts/").then(function (response) {
console.log(response.data);
self.setState({
editData:response.data,
isLoading:false
})
}).catch(function (error) {
console.log(error);
});
}
putData =(e)=>{
e.preventDefault();
}
//edit permission for input fields
inputChangedHandler = (event) => {
this.setState({ editData: { ...this.state.editData,[event.target.name]:event.target.value} });
// May be call for search result
}
//the view
render() {
const {isLoading,editData} = this.state;
if(isLoading){
return <Loader />;
}
return (
<div>
<Row style={{paddingTop:'20px',paddingBottom:'40px',marginLeft:'auto',marginRight:'auto'}}>
<Col lg="8" md="8" sm="8">
<Form id="bankForm" onSubmit={this.putData}>
<ToastContainer store={ToastStore}/>
{
editData.map(data=>{
return(<Row key={data.id}>
<Col lg="6" md="6" sm="6">
<FormGroup>
<Label className="createLabelStyle" for="title">Title</Label>
<Input className="createInputStyle" type="text" name="title" id="title" autoComplete="off" value={data.title} onChange={(event)=>this.inputChangedHandler(event)} required/>
</FormGroup>
<FormGroup>
<Label className="createLabelStyle" for="first_name">First name</Label>
<Input className="createInputStyle" type="text" name="first_name" id="first_name" value={data.first_name} onChange={(event)=>this.inputChangedHandler(event)} autoComplete="off" required/>
</FormGroup>
<FormGroup>
<Label className="createLabelStyle" for="contact_number">Contact Number</Label>
<Input className="createInputStyle" type="text" name="contact_number" id="contact_number" autoComplete="off" value={(data.contact_number==null)?"":data.contact_number} onChange={(event)=>this.inputChangedHandler(event)} required/>
</FormGroup>
</Col>
<Col lg="6" md="6" sm="6">
<FormGroup>
<Label className="createLabelStyle" for="email">Email ID</Label>
<Input className="createInputStyle" type="text" name="email" value={data.email} onChange={(event)=>this.inputChangedHandler(event)} id="email" autoComplete="off" required/>
</FormGroup>
<FormGroup>
<Label className="createLabelStyle" for="last_name">Last name</Label>
<Input className="createInputStyle" type="text" name="last_name" value={data.last_name} onChange={(event)=>this.inputChangedHandler(event)} id="last_name" autoComplete="off" required/>
</FormGroup>
</Col>
</Row>);
})
}
<Button className="createSubmitStyle">Add</Button>{' '}
<Button className="createSubmitStyle">Update</Button>
</Form>
</Col>
</Row>
</div>
);
}
}
In the renderData function variable editData is an Array that's why it is rendering your form properly but in the inputChangedHandler function you are updating editData to an Object hence the issue
Please try this snippet:
editData!=undefined &&editData.map(data=>{});
I found out the solution. Adding this code below solved the problem. I needed to add index with map to update the property value of object.
inputChangedHandler(i,event){
let data = [...this.state.editData];
data[i][event.target.name] = event.target.value;
console.log(data[i][event.target.name]);
this.setState({editData:data});
}

Resources