I have an application where user have to input data about it and after saving that form, the data should be outputed in a certain form.
const ParrentForm = () => {
const [carNr, setCarNr] = useState([]);
const onFinish = (values) => {
const { firstName, lastName } = values;
const user = {
firstName,
lastName,
things: {
cars: []
}
};
console.log(user);
};
const onFinishFailed = (errorInfo) => {
console.log("Failed:", errorInfo);
};
const setFloorsNrHandler = (nr) => {
setCarNr(Array.from({ length: nr }, (v, k) => k));
};
return (
<div>
<Form
name="main"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="First Name"
name="firstName"
rules={[{ required: true, message: "Please input your first name!" }]}
>
<Input />
</Form.Item>
<Form.Item
label="Last Name"
name="lastName"
rules={[{ required: true, message: "Please input your last name!" }]}
>
<Input />
</Form.Item>
<Form.Item
name="nrOfCars"
label="Cars"
rules={[{ type: "number", min: 0, max: 15 }]}
>
<InputNumber onChange={setFloorsNrHandler} />
</Form.Item>
{carNr.map((f, k) => {
return (
<Form.Item key={k}>
<InnerForm cKey={k} />
</Form.Item>
);
})}
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
);
};
export default ParrentForm;
The expected data should look like this:
{
"user": {
"firstName": "Bill",
"lastName": "M.",
},
"things": {
"cars": [
{
"nr": 1,
"carsList": [
{
"name": "Car 1",
"nr": 1
},
{
"name": "Audi",
"nr": 2
},
,
{
"name": "Mercedes",
"nr": 3
}
]
},
{
"nr": 2,
"carsList": [
{
"name": "Bmw",
"nr": 1
}
]
}
]
}
}
How to modify the data inside onFinish function to get the result above?
demo: https://codesandbox.io/s/modest-cache-o0g65?file=/OuterForm.js:136-1755
First you have to destruct (doc) the form values object combining with spread operator (doc) to get the car-related values (I stored in variable carValues). After that, the only things left to do is manipulate with that variable, here I combined the use of Object.entries and Array.prototype.map
There a part with Number(key), because Object.entries() transforms object in to array of key-value pair, with key's type of string, so you have to cast it to Number first for 1-based index
const onFinish = values => {
const { firstName, lastName, nrOfCars, ...carValues } = values
const cars = Object.entries(carValues).map(([key, value]) => ({
nr: Number(key) + 1,
carsList: (value.cars || []).map((car, carIndex) => ({
nr: carIndex + 1,
name: car.name
}))
}))
const user = {
firstName,
lastName,
things: {
cars
}
}
console.log(JSON.stringify(user, null, 2))
}
Forked codesandbox for implementation
Related
I have a form in reactjs application where I render some radio buttons.
const items = [
{
id: 1,
car: "BMW",
colors: [
{
id: 1,
text: "red"
},
{
id: 2,
text: "blue"
}
]
},
{
id: 2,
car: "Audi",
colors: [
{
id: 1,
text: "green"
},
{
id: 2,
text: "white"
}
]
}
];
const Demo = () => {
const onFinish = (values) => {
console.log("Success:", values);
};
const onFinishFailed = (errorInfo) => {
console.log("Failed:", errorInfo);
};
return (
<Form
{...layout}
name="basic"
initialValues={{
remember: true
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
{items.map((i) => {
return (
<Form.Item
label={i.car}
key={i.car}
name={"carId" + i.id}
rules={[{ required: true, message: "Please" }]}
>
<Radio.Group>
{i.colors.map((color) => {
return (
<Radio key={color.text} value={color.text}>
{color.text}
</Radio>
);
})}
</Radio.Group>
</Form.Item>
);
})}
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
Now when I save data I get:
{
carId1: "red",
carId2: "white"
}
Issue: I want to get data after saving in this way:
[
{
carId: 1,
colors: ["red"]
},
{
carId: 2,
colors: ["white"]
}
]
Question: How to get the same structure that I described above after saving the form?
demo: https://codesandbox.io/s/basic-usage-antd4102-forked-o3to7?file=/index.js:320-1730
I've added a link in comments with a simple data restructure solution, here
in case of it's not opening,
I've just updated the onFinish method to be:
const onFinish = (values) => {
const results = [];
items.forEach((carItem) => {
results.push({
carId: carItem.id,
colors: [values["carId" + carItem.id]]
});
});
console.log("Success:", results);
};
so you have the needed data and can structure it in any shape you want.
This is my state:
view screenshot
the part in the circle is the part of the state that I want to change
**This is my checkbox input **
{this.state.data.map((elm) => (
<div className={classes.rowContainer}>
<h3>{elm.name}</h3>
{elm.groupes.map((group) => (
<input
className={classes.checkBox}
name={elm.name}
groupes={group.groupName}
checked={group.value}
type='checkbox'
onChange={this.changeValue}
/>
))}
</div>
))}
This is the object passed to component:
const data = [
{
name: 'Créer un groupe',
groupes: [
{ name: 'commercial', value: true },
{ name: 'manager', value: false }
]
},
{
name: 'Détruire un groupe ',
groupes: [
{ name: 'commercial', value: false },
{ name: 'manager', value: false }
]
}
]
To update the specific boolean value for a given checkbox, you can make use of the .map() calls indexes from the arrays, and pass those to the onChange handler of the checkbox inputs to update the correct value in the state.
To update the state itself safely and without mutation, you'll need to deep copy the data array (using a round trip through JSON in this case), then update the right value using the indexes, then assign the new data to the state.
Here is a working snippet that illustrates how this works:
const data = [{
name: 'Créer un groupe',
groupes: [
{ name: 'commercial', value: true },
{ name: 'manager', value: false }
]
}, {
name: 'Détruire un groupe ',
groupes: [
{ name: 'commercial', value: false },
{ name: 'manager', value: false }
]
}];
class App extends React.Component {
constructor(props) {
super(props);
this.state = { data };
}
changeValue = (sectionIndex, groupIndex) => {
const dataCopy = JSON.parse(JSON.stringify(this.state.data));
const group = dataCopy[sectionIndex].groupes[groupIndex];
group.value = !group.value;
this.setState({ data: dataCopy });
}
render() {
return (
<div>
{ this.state.data.map((elm, i) => (
<div key={`${i}`}>
<h3>{elm.name}</h3>
{ elm.groupes.map((group, j) => (
<input key={`${i}${j}`} name={elm.name}
checked={group.value} type="checkbox"
onChange={() => this.changeValue(i, j)} />
)) }
</div>
)) }
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Each of the user relations object looks like this:
"userRelations": [
{
"relatedUser": {
"id": 4,
"firstName": "Jack",
"lastName": "Miller"
},
"type": "FRIEND"
},
{
"relatedUser": {
"id": 3,
"firstName": "Rhena",
"lastName": "Tahoma"
},
"type": "CONTACT"
}
]
Currently, my code renders all relatedUsers. However, I only want to render those which have "type": "contact". Is it
possible to check for this condition within the return
type RelatedUser = {
firstName: string,
lastName: string,
id: string,
phoneNumber: string,
};
type RelationType = {
type: string,
};
export const ContactList: React.FunctionComponent<UserProps> = ({ data }) => {
if (!data) return null;
return (
<View style={styles.users}>
{data.users.nodes[0].userRelations.map(
(item: { relatedUser: RelatedUser, type: RelationType}) => {
const userName = item.relatedUser.firstName.concat(' ').concat(item.relatedUser.lastName);
// if (item.type: "CONTACT"){
return (
<View style={styles.item} key={item.relatedUser.id}>
<Text style={styles.userName}>{userName}</Text>
</View>
);
},
)}
</View>
);
};
Instead of doing that inside map's return I would suggest to chain .filter() with map():
userRelations.filter(({ type }) => type === 'CONTACT').map(/* your code*/)
I have a nested array and I would like to render this array with JSX code such that key like array1 should be display once and the text should be displayed as list and then continue the same loop with the next array. I have tried using map function and for loop but could not achieve success.
sampleArray : [{"array1": [ {"text": "abc"},{"text": "def"}, {"text": "ghi"}],
"array2":[{"text": "efg"}, {"text": "fgh"}]]
Please suggest me map function to iterate over this array to render into JSX code. Thanks
Hey I don't know if you made a mistake in your object, but here are two versions of this implementation with your object and with my update:
Ignore typings did it in TypeScript
export const Test = ({ className }: Test) => {
const sampleArray = [
{
array1: [{ text: 'abc' }, { text: 'def' }, { text: 'ghi' }],
array2: [{ text: 'efg' }, { text: 'fgh' }],
},
]
const sampleArray2 = [
{
array1: [{ text: 'abc' }, { text: 'def' }, { text: 'ghi' }],
},
{
array2: [{ text: 'efg' }, { text: 'fgh' }],
},
]
return (
<>
{/* YOUR VERSION */}
<div>
{Object.keys(sampleArray[0]).map((key) => {
return (
<div>
<div id="title">{key}</div>
{sampleArray[0].map((item) => {
return <div>{item.text}</div>
})}
</div>
)
})}
</div>
{/* UPDATED VERSION */}
<div>
{sampleArray2.map((key) => {
const title = Object.keys(key)[0]
return (
<div>
<div id="title">{title}</div>
{key[title].map((item) => {
return <div>{item.text}</div>
})}
</div>
)
})}
</div>
</>
)
}
How can I get the selected element when I submit the form. I'm using reactjs autosuggest.
My Parent Component
<Form
onSubmit={this.submitFormadd}
layout="horizontal"
ref="myformadd"
>
<MyAutosuggest
data={this.props.users}
id="river"
name="river"
placeholder="Enter River Name"
onChange={this.onChange}
required="true"
/>
<MyAutosuggest
data={this.props.drivers}
id="driver"
name="driver"
placeholder="Enter Driver Name"
onChange={this.onChange}
/>
<fieldset>
<Row layout="horizontal">
<input className="btn btn-primary" formNoValidate={true} type="submit" defaultValue="Submit" />
</Row>
</fieldset>
</Form>
You have set onChange wrongly for the autosuggests. Change:
onChange={this.onChange}
to
onChange={this.onChange.bind(this)}
Then store the selected elements in the onChange method. When the submit is triggered, you will have the elements already stored.
<Autosuggest
id="river"
name="river"
placeholder="Enter River Name"
onChange={this.onChange}
required="true"
data={this.props.users}
getSuggestionValue={this.getSuggestionValue.bind(this)}/>
I think the above code would help, in getSuggestionValue, you will get value, which you can use,
for example:
getSuggestionValue(value){
this.setState({ val:value}); //or anything else
}
while submitting (example):
submitFunction(){
console.log(this.state.val); //receive value here
}
changes in code from codepen
const languages = [
{
name: 'C',
year: 1972
},
{
name: 'C#',
year: 2000
},
{
name: 'C++',
year: 1983
},
{
name: 'Clojure',
year: 2007
},
{
name: 'Elm',
year: 2012
},
{
name: 'Go',
year: 2009
},
{
name: 'Haskell',
year: 1990
},
{
name: 'Java',
year: 1995
},
{
name: 'Javascript',
year: 1995
},
{
name: 'Perl',
year: 1987
},
{
name: 'PHP',
year: 1995
},
{
name: 'Python',
year: 1991
},
{
name: 'Ruby',
year: 1995
},
{
name: 'Scala',
year: 2003
}
];
// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters
function escapeRegexCharacters(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function getSuggestions(value) {
const escapedValue = escapeRegexCharacters(value.trim());
if (escapedValue === '') {
return [];
}
const regex = new RegExp('^' + escapedValue, 'i');
return languages.filter(language => regex.test(language.name));
}
function getSuggestionValue(suggestion) {
return this.props.getValue(suggestion.name,this.props.id);
}
function renderSuggestion(suggestion) {
return (
<span>{suggestion.name}</span>
);
}
class MyAutosuggest extends React.Component {
constructor() {
super();
this.state = {
value: '',
suggestions: []
};
}
onChange = (_, { newValue }) => {
const { id, onChange } = this.props;
this.setState({
value: newValue
});
onChange(id, newValue);
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render() {
const { id, placeholder } = this.props;
const { value, suggestions } = this.state;
const inputProps = {
placeholder,
value,
onChange: this.onChange
};
return (
<Autosuggest
id={id}
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
);
}
}
class App extends React.Component {
onChange(id, newValue) {
console.log(`${id} changed to ${newValue}`);
}
getValueForInput(val,id){
console.log(val,id);
}
render() {
return (
<div>
<MyAutosuggest
id="type-c"
placeholder="Type 'c'"
onChange={this.onChange}
getValue={this.getValueForInput.bind(this)}
/>
<MyAutosuggest
id="type-p"
placeholder="Type 'p'"
onChange={this.onChange}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Here is how I did it.
onChange = (event, { newValue, method }) => {
let bookObj;
if (method == "click") {
this.props.bookiesList.filter(book=> {
if (book.name.trim() === newValue.trim()) {
bookObj= book
}
});
}
};