Reputation: 1235
I'm trying to create a form that dynamically maps fields based on a JSON response.
I currently have a dummy object that I'm setting in state, but the final stage will receive the data from an external API call. The dataset may change over time with key/values being added or deleted based on business needs. In lieu of this I need to create a smart component that takes an initial set of data and maps a "Read-only" field for every key/value pair in the object.
The second concern is the actual form layout. I have an initial scaffolding below, and I've only hard-coded the columns and rows. How can I implement logic that will create two column rows from the data response?
Any thoughts/help on this is hugely appreciated!
Code:
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Form, Button, Input, Row, Col } from 'antd';
import "antd/dist/antd.css";
import './style.css';
class OpportunityDetails extends Component {
constructor() {
super();
this.state = {
disabled: true,
formLayout: 'horizontal',
opportunityDetails: [
{
CustomerName: "",
Field2: "Some data",
Field3: "Some data",
Field4: "Some data",
Field5: "Some data",
Field6: "Some data",
Field7: "Some data",
Field8: "Some data",
Field9: "Some data",
Field10: "Some data",
Field11: "Some data",
Field12: "Some data",
Field13: "Some data",
Field14: "Some data",
Field15: "Some data"
}
]
};
this.toggleSwitch = this.toggleSwitch.bind(this)
}
toggleSwitch() {
this.setState(previousState => ({
disabled: !previousState.disabled,
enabled: previousState.disabled
}))
}
modifyRoute(){
alert("Sending you to the modify floor");
}
uploadRoute(){
alert("Sending you to the upload sector!")
}
render() {
const { disabled } = this.state;
const { enabled } = this.state;
return (
<div className={}>
/// Button Group
<Row type="flex" justify="space-around">
<Col span={4}>
<Button disabled={disabled} onClick={this.modifyRoute}>Modify Docs</Button>
</Col>
<Col span={4}>
<Button disabled={disabled} onClick={this.uploadRoute}>Upload Docs</Button>
</Col>
<Col span={4}>
<Button disabled={enabled} onClick={this.toggleSwitch}>
Unlock Quote
</Button>
</Col>
</Row>
/// Form section with columns and rows for all key/value pairs
<Row type="flex" justify="space-around">
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
<Col span={10}>
<Form.Item label={key}>
<Input placeholder="{value} />
</Form.Item>
</Col>
</Row>
<Row type="flex" justify="space-around">
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
</Row>
<Row type="flex" justify="space-around">
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
</Row>
<Row type="flex" justify="space-around">
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
<Col span={10}>
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
</Col>
</Row>
</div>
);
}
}
render(<OpportunityDetails />, document.getElementById('root'));
Upvotes: 1
Views: 5252
Reputation: 4563
<Row type="flex" justify="space-around">
<Col span={10}>
{opportunityDetails.map(detail=>{
return detail.forEach(([key,value])=>(
<Form.Item label={key}>
<Input placeholder={value} />
</Form.Item>
)
)
})}
</Col>
</Row>
Upvotes: 0
Reputation: 755
Take Amir's comment seriously. And once you do, you'd probably restructure your data so it would be more handy (and readable):
I don't know why opportunityDetails
has to be an array, I adjusted to that anyway. Though it will add complexity as you would see
1.) This is how you want your data to look like (you will understand later why):
/*[
{ itemKey: "CustomerName", itemValue: "" },
{ itemKey: "Field2", itemValue: "Some data" }
.... and so on
]*/
opportunityDetails: [
{
CustomerName: "",
Field2: "Some data",
Field3: "Some data",
Field4: "Some data",
Field5: "Some data",
Field6: "Some data",
Field7: "Some data",
Field8: "Some data",
Field9: "Some data",
Field10: "Some data",
Field11: "Some data",
Field12: "Some data",
Field13: "Some data",
Field14: "Some data",
Field15: "Some data"
}
].map(obj => {
const objKeys = Object.keys(obj);
return objKeys.map(itemKey => {
return {
itemKey,
itemValue: obj[itemKey]
};
});
})
the following steps and patterns below are JSX (or react) specific; what I'm trying to say is this is the most common way of rendering an array of JSX elements
in react:
2.) Create a method which will return an array of JSX, I used .map because .forEach will not work (you may want to search why on your own).
renderDynamicElWrapper() {
return this.state.opportunityDetails.map(items => {
return (
<Row type="flex" justify="space-around">
{this.renderDynamicEl(items)}
</Row>
);
});
}
assuming opportunityDetails will have more than 1 item, we need to have another method and iteration similar to the one above
renderDynamicEl(els) {
return els.map(el => {
return (
<Col span={10}>
<Form.Item label={el.itemKey}>
<Input placeholder={el.itemValue} />
</Form.Item>
</Col>
);
});
}
3.) Finally, below would be how the return of your render
method will look like:
return (
<div>
<Row type="flex" justify="space-around">
<Col span={4}>
<Button disabled={disabled} onClick={this.modifyRoute}>
Modify Docs
</Button>
</Col>
<Col span={4}>
<Button disabled={disabled} onClick={this.uploadRoute}>
Upload Docs
</Button>
</Col>
<Col span={4}>
<Button disabled={enabled} onClick={this.toggleSwitch}>
Unlock Quote
</Button>
</Col>
</Row>
{this.renderDynamicElWrapper()}
</div>
);
}
P.S: I would advice to master .map
and how to return an array of jsx from a method because %100 of the time you will encounter the same pattern in React projects.
Upvotes: 3