There is a requirement in development to validate each entry in the cell and provide operations for adding and deleting rows.
Since the Form component can easily validate data, we thought of using the From component and the Table component to implement this requirement. Can achieve all the function points in the requirements, but the scheme is not perfect, friends can refer to 👉 link here.
The renderings are here
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from "prop-types";
import "antd/dist/antd.css";
import { Form, Input, Table, Select, AutoComplete, Icon } from "antd";
import "./map.css";
class SetMapping extends React.Component {
static propTypes = {
form: PropTypes.shape({}).isRequired,
};
state = {
mappingState: [],
appnameList: [],
tagList: []
};
componentDidMountSyslogIPValidator = () => ({rule, value, callback) => {syslogIPValidator: (rule, value, callback) => {if(value && value.trim() ! = ="") {const reg = / ^ (\ d {1, 3} | \ *) \. (\ d {1, 3} | \ *) \. (\ d {1, 3} | \ *) \. (\ d {1, 3} | \ *) $/ I;if(! reg.test(value.trim())) { callback("IP address format error");
} else if (this.checkIfExistedIP(value)) {
callback("Duplicate IP address");
} else{ callback(); }}}}); checkIfExistedIP = value => { const { form } = this.props; const { getFieldValue } = form; const mapping = getFieldValue("mapping") | | []; const mappingArray = mapping ? mapping.filter(one => one.ip === value) : [];if (mappingArray.length > 1) {
return true;
}
return false; }; // Add a line of data addOneMapping = () => {const {form} = this.props; const { getFieldsValue, validateFields } = form; const { mappingState } = this.state; const mapping = getFieldsValue().mapping || mappingState; // If there is any data in the form --> check if the previous one is completeif(mapping && mapping.length ! == 0) { validateFields(["mapping"], { force: true }, errors => {
if(! errors) { mapping.push({ ip:"",
appname: "",
tag: "",
charset: ""}); this.setState({ mappingState: mapping }); }}); }else {
mapping.push({
ip: "",
appname: "",
tag: "",
charset: ""}); this.setState({ mappingState: mapping }); }}; // mappingState is the data source used to render the table. RemoveOneMapping => {const {form} = this.props; const { getFieldValue } = form; const mapping = getFieldValue("mapping");
mapping.splice(index, 1);
this.setState({
mappingState: mapping
});
form.setFieldsValue({
mapping
});
};
render() {
const { mappingState } = this.state;
const { form, appnameList, tagList } = this.props;
const { getFieldDecorator } = form;
const PLACEHOLDER = "Please enter";
const tableTitle = [
{
title: "ip",
dataIndex: "ip",
key: "ip",
width: 205,
render: (ip, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].ip`, {
initialValue: ip || "",
validateFirst: true,
validateTrigger: ["onChange"."onFocus"],
rules: [
{
required: true,
message: "Please enter IP address"
},
this.syslogIPValidator()
]
})(
<Input
size="small"
style={{ width: "170px" }}
autoComplete="off"
placeholder={PLACEHOLDER}
/>
)}
</Form.Item>
)
},
{
title: "appname",
dataIndex: "appname",
key: "appname",
width: 205,
render: (appname, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].appname`, {
initialValue: appname || "",
validateFirst: true,
validateTrigger: ["onChange"."onBlur"],
rules: [
{
required: true,
message: "Please enter appName"
}
]
})(
<AutoComplete
size="small"
dataSource={appnameList}
style={{ width: "170px"}} placeholder={PLACEHOLDER} filterOption={(inputValue, option) => option.props.children .toUpperCase() .indexOf(inputValue.toUpperCase()) ! == -1 } /> )} </Form.Item> ) }, { title:"tag",
dataIndex: "tag",
key: "tag",
width: 205,
render: (tag, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].tag`, {
initialValue: tag || "",
validateFirst: true,
validateTrigger: ["onChange"."onBlur"],
rules: [
{
required: true,
message: "Please enter tag"
}
]
})(
<AutoComplete
size="small"
dataSource={tagList}
style={{ width: "170px"}} placeholder={PLACEHOLDER} filterOption={(inputValue, option) => option.props.children .toUpperCase() .indexOf(inputValue.toUpperCase()) ! == -1 } /> )} </Form.Item> ) }, { title:"charset",
dataIndex: "charset",
key: "charset",
width: 110,
render: (charset, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].charset`, {
initialValue: charset || "utf-8"
})(
<Select size="small">
<Select.Option value="utf-8" style={{ fontSize: 12 }}>
utf-8
</Select.Option>
<Select.Option value="gbk" style={{ fontSize: 12 }}>
gbk
</Select.Option>
</Select>
)}
</Form.Item>
)
},
{
title: "Operation",
key: "operation",
width: 60,
render: (operation, one, index) => (
<Icon
style={{ height: "42px" }}
type="delete"
onClick={() => this.removeOneMapping(index)}
/>
)
}
];
return (
<div className="setMapping">
<Table
bordered
size="small"
className="tableBody"
rowKey={(record, index) => index}
columns={tableTitle}
dataSource={mappingState}
pagination={false}
/>
<span className="addNew"OnClick ={this.addonemapping}> Add a new mapping </span> </div>); } } const SetMappingForm = Form.create({ name:"register" })(SetMapping);
ReactDOM.render(<SetMappingForm />, document.getElementById("root"));
Copy the code
AntDesign also has a similar implementation, you can check it out, 👉 link here. This question is also discussed on Github, and other partners’ methods are linked here at 👉