preface
Edit display effect
Projects have more forms of writing, the use of antd form always want to write many of the same FormItem, form too much, more than code length is too heavy and complicated, is not conducive to the late maintenance, so for general simple form for the packaging, to achieve the result of rendering the form by using data, change the Dom to maintain data maintenance.
- Dependent library version:
- Antd 4.13
- My moment 2.29.1
0. Utility class methods
const setObjDefaultValue = (obj, value = null) = > {
return new Proxy(obj, {
get(obj, prop) {
return prop inobj ? obj[prop] : value; }}); };// Determine the type
const is = {
types: [
"Array"."Boolean"."Date"."Number"."Object"."Function"."RegExp"."String"."Window"."HTMLDocument"]};// eslint-disable-next-line array-callback-return
is.types.map((item) = > {
is[item] = (function (type) {
return function (obj) {
return Object.prototype.toString.call(obj) === `[object ${type}] `;
};
})(item);
});
const callFun = (result) = > {
is.Function(result) ? result() : null;
};
export { is, callFun, setObjDefaultValue };
Copy the code
1. Encapsulate form entries
import React, { Component } from "react";
import {
Cascader,
Input,
TreeSelect,
InputNumber,
Transfer,
Slider,
Switch,
Button,
Radio,
Rate,
Select,
Checkbox,
DatePicker,
Col,
Row,
TimePicker,
Upload,
Icon,
message,
Tag,
Tooltip
} from "antd";
import { is, setObjDefaultValue } from ".. /utils";
import PropTypes from "prop-types";
const { Option, OptGroup } = Select;
const { TreeNode } = TreeSelect;
const { Dragger } = Upload;
const { Group: RadioGroup } = Radio;
const { Group: CheckboxGroup } = Checkbox;
const { TextArea, Group: InputGroup, Password: InputPassword } = Input;
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
class SingleFormItem extends Component {
constructor(props) {
super(props);
this.UploadButtonRef = React.createRef(null);
}
onEventChange = (event) = > {
this.setValue(event.target.value);
};
onValueChange = (value) = > {
this.setValue(value);
};
onDateChange = (date, dateString) = > {
this.setValue(dateString);
};
setValue = (value) = > {
const { onChange } = this.props;
onChange(value);
};
onButtonClick = (obj) = > {
const { text, buttonClick } = this.props;
buttonClick(text, obj);
};
render() {
const {
type = "",
mixin = {},
text,
manualSubmit,
tooltipMixin
} = this.props;
let { onEventChange: onChange } = this;
if(["Checkbox"."Select"."InputNumber"."Slider"."Switch"."Rate"."Cascader"."TreeSelect"."Button"
].includes(type)
) {
onChange = this.onValueChange;
} else if(["MonthPicker"."RangePicker"."WeekPicker"."DatePicker"].includes(type)
) {
onChange = this.onDateChange;
}
letmixinObj = { onChange, ... mixin };const getRadio = () = > {
const { radios = [] } = mixinObj;
delete mixinObj.radios;
const radioItems = radios.map((item) = > {
const { value, label } = item;
return (
<Radio key={value} value={value}>
{label}
</Radio>
);
});
return <Radio.Group {. mixinObj} >{radioItems} </Radio.Group>;
};
const getSelect = () = > {
const { options = [], next } = mixinObj;
delete mixinObj.options;
let optionItems = options.map((item) = > {
const { value, label } = item;
return (
<Option key={value} value={value}>
{label}
</Option>
);
});
if (next) {
optionItems = options.map((item) = > {
const { value, label } = item;
const children = item[next];
return (
<OptGroup key={value} label={label}>
{children.map((item) => {
const { value, label } = item;
return (
<Option key={value} value={value}>
{label}
</Option>
);
})}
</OptGroup>
);
});
}
return (
<Select {. mixinObj} allowClear>
{optionItems}
</Select>
);
};
const getTags = () = > {
let { text: texts = [] } = this.props;
const main = texts.map((item) = > {
const { name } = item;
return <Tag key={name}>{name.toUpperCase()}</Tag>;
});
return <>{main}</>;
};
const getUpload = () = > {
const UploadClick = () = > {
const { uploadButtonClick } = this.props;
const flag = uploadButtonClick();
if (flag) {
console.log(flag);
this.UploadButtonRef.current.click(); }};return (
<>
<Button onClick={UploadClick}>{text}</Button>
<Upload
{. mixinObj}
onChange={(info)= > {
const { status } = info.file;
if (status !== "uploading") {
console.log(info.file, info.fileList);
}
if (status === "done") {
message.success(
`${info.file.name} file uploaded successfully.`
);
this.onButtonClick(info);
} else if (status === "error") {
message.error(`${info.file.name} file upload failed.`);
}
}}
>
<Button ref={this.UploadButtonRef}></Button>
</Upload>
</>
);
};
let itemTypeObjs = {
Text: (
<Tooltip placement="topRight" {. tooltipMixin} title={text}>
<span>{text}</span>
</Tooltip>
),
Input: <Input {. mixinObj} / >,
InputPassword: <InputPassword {. mixinObj} / >,
TextArea: <TextArea allowClear {. mixinObj} / >,
InputNumber: <InputNumber {. mixinObj} / >,
Slider: <Slider {. mixinObj} / >,
Switch: <Switch {. mixinObj} / >,
Rate: <Rate {. mixinObj} / >,
Tag: getTags,
Cascader: <Cascader {. mixinObj} / >,
DatePicker: <DatePicker {. mixinObj} / >,
MonthPicker: <MonthPicker {. mixinObj} / >,
RangePicker: <RangePicker {. mixinObj} / >,
WeekPicker: <WeekPicker {. mixinObj} / >,
TimePicker: <TimePicker {. mixinObj} / >,
Checkbox: <CheckboxGroup {. mixinObj} / >,
Submit: () = > {
const { label = "Submit" } = mixinObj;
return (
<Button
type="primary"
htmlType="submit"
onClick={(e)= >{ e.preventDefault(); manualSubmit(); }} {... mixinObj} > {label}{" "}</Button>
);
},
Upload: getUpload,
Button: (
<Button
type="primary"
onClick={(e)= >{ this.onButtonClick(); }} {... mixinObj} > {text}{" "}</Button>
),
Dragger: () = > {
const onDraggerChange = (info) = > {
const { status } = info.file;
if(status ! = ="uploading") {
console.log(info.file, info.fileList);
}
if (status === "done") {
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === "error") {
message.error(`${info.file.name} file upload failed.`); }}; mixinObj.onChange = onDraggerChange;return (
<Dragger {. mixinObj} >
<p className="ant-upload-drag-icon">
<Icon type="inbox" />
</p>
</Dragger>
);
},
InputGroup: () = > {
return (
<InputGroup size="large">
<Row gutter={8}>
<Col span={5}>
<Input defaultValue="0571" />
</Col>
<Col span={8}>
<Input defaultValue="26888888" />
</Col>
</Row>
</InputGroup>
);
},
Select: getSelect,
Radio: getRadio,
Transfer: <Transfer {. mixinObj} / >,
TreeSelect: () = > {
const {
idKey = "key",
titleKey = "title",
childrenKey = "children",
treeData = []
} = mixinObj;
const renderTreeNodes = (data) = >
data.map((item) = > {
if (item[childrenKey]) {
return (
<TreeNode key={item[idKey]} title={item[titleKey]}>
{renderTreeNodes(item[childrenKey])}
</TreeNode>
);
}
return <TreeNode key={item[idKey]} title={item[titleKey]} />;
});
return (
<TreeSelect treeDefaultExpandAll showSearch {. mixinObj} >
{renderTreeNodes(treeData)}
</TreeSelect>); }}; itemTypeObjs = setObjDefaultValue(itemTypeObjs,null);
const result = itemTypeObjs[type];
/* if(Radio){ } */
return is.Function(result) ? result() : result;
}
}
SingleFormItem.propTypes = {
/** * name */
type: PropTypes.string,
/** ** is called when the submit button is clicked
mixin: PropTypes.object,
/** ** * is called every time data is changed or a button is clicked
value: PropTypes.string,
/** * is called when the content of a form item is changed
onChange: PropTypes.func,
*/ is called when the ** * button is clicked
buttonClick: PropTypes.func,
*/ is called when the ** * button is clicked
uploadButtonClick: PropTypes.func
};
SingleFormItem.defaultProps = {
type: "".mixin: {},
value: "".onChange: () = > {},
buttonClick: () = > {},
uploadButtonClick: () = > {
return true; }};export default SingleFormItem;
Copy the code
2. Encapsulate form components
import { Form } from "antd";
import React, { useImperativeHandle, forwardRef, useEffect } from "react";
import SingleFormItem from "./singleFormItem";
import { is } from ".. /utils";
const Index = ({ formData, submit, buttonClick, setFieldsValue, uploadButtonClick, resetFormData, ifSetDefaultValue }, ref) = > {
const [form] = Form.useForm();
const onGenderChange = (nameKey, value) = > {
form.setFieldsValue({ nameKey: value });
is.Function(setFieldsValue) && setFieldsValue(nameKey, value);
};
const setFormDefaultValue = () = > {
const currentObj = form.getFieldsValue();
const tempValue = {};
formData.items.map((obj) = > {
const { item, mixin = {} } = obj;
const { name } = item;
if (!currentObj[name] && mixin.defaultValue) {
tempValue[name] = mixin.defaultValue;
}
});
form.setFieldsValue(tempValue);
};
useEffect(() = > {
setFormDefaultValue();
return () = >{}; } []); useEffect(() = > {
setFormDefaultValue();
});
const onSubmit = (values) = > {
if (ifSetDefaultValue) {
formData.items.map((obj) = > {
const { item, mixin = {} } = obj;
const { name } = item;
if(! values[name] && values[name] ! = =0) { values[name] = mixin.defaultValue; }}); }console.log(values);
submit(values);
};
const manualSubmit = () = > {
form.submit();
};
useImperativeHandle(ref, () = > {
return {
getForm() {
return form;
},
getItemsValue() {
return form.getFieldsValue();
},
resetItemsValue() {
form.resetFields(formData.items.map((item) = > item.item.name));
resetFormData && resetFormData();
},
updateSetValue(){ setFormDefaultValue(); }}; });const { form: formObj, items } = formData;
const FromItems = items.map((singleItem) = > {
const {
item,
type,
mixin = {},
value,
config = {},
tooltipMixin = {}
} = singleItem;
const { name } = item;
const { Br = false } = config;
const BrDom = Br ? <br /> : null;
return (
<>
<Form.Item key={name} {. item} className={`from_item_The ${name} `} >
<SingleFormItem
mixin={mixin}
type={type}
text={value}
config={config}
TooltipMixin={tooltipMixin}
manualSubmit={manualSubmit}
buttonClick={(text, obj) = >{ buttonClick && buttonClick(text, obj); }} uploadButtonClick={uploadButtonClick} onChange={(value) => { onGenderChange(name, value); }} / ></Form.Item>
{BrDom}
</>
);
});
return (
<Form
form={form}
{. formObj}
className={"base_antd_form"}
onFinish={onSubmit}
>
{FromItems}
</Form>
);
};
// Index.propTypes = {
/ / / * *
// * Display the form data
/ / * /
// formData: PropTypes.object,
/ / / * *
// * called when the submit button is clicked
/ / * /
// submit: PropTypes.func,
/ / / * *
// * Called every time data is changed or a button is clicked
/ / * /
// update: PropTypes.func,
/ / / * *
// * Called every time data is changed or a button is clicked
/ / * /
// setFieldsValue: PropTypes.func,
// /**
// * If the file selection box is not set, it will be displayed by default
/ / * /
// uploadButtonClick: PropTypes.func,
/ / / * *
// * Whether to set unset content to the default value when submitting
/ / * /
// ifSetDefaultValue: PropTypes.bool,
// };
// Index.defaultProps = {
// formData: {},
// submit: () => {},
// update: () => {},
// setFieldsValue: () => {},
// uploadButtonClick: () => {return true},
// ifSetDefaultValue:true
// };
export default forwardRef(Index);
Copy the code
3. Need to render the data of the table
import moment from "moment";
const today = moment();
function disabledDate(current) {
return current && current < moment().subtract(1."days");
}
const defaultFormDefaultLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7}},wrapperCol: {
xs: { span: 24 },
sm: { span: 17}}};const formNextLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4}},wrapperCol: {
xs: { span: 24 },
sm: { span: 20}}};// Layout with no name and only data on the right
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24.offset: 0
},
sm: {
span: 18.offset: 6}}};const tableFormData = {
form: {
name: "test". defaultFormDefaultLayout },items: [{item: {
label: "Input".name: "Input".rules: [{ required: true.message: "Please enter your metadata name"}},type: "Input".mixin: {
placeholder: "Please enter".defaultValue: "Input"}}, {item: {
label: "Text".name: "Text"
},
type: "Text".value: "showText".tooltipMixin: {
placement: "bottomRight"}}, {item: {
label: "InputPassword".name: "InputPassword".rules: [{ required: true.message: "Can't be empty."}},type: "InputPassword".mixin: {
placeholder: "Please enter"}}, {item: {
label: "InputNumber".name: "InputNumber"
},
type: "InputNumber".mixin: {
placeholder: "Please enter"}}, {item: {
label: "TextArea".name: "TextArea"
},
type: "TextArea".mixin: {
placeholder: "Please enter".rows: 4}}, {item: {
label: "Slider".name: "Slider"
},
type: "Slider".mixin: {
min: 1.max: 20.value: 10}}, {item: {
label: "Switch".name: "Switch"
},
type: "Switch".mixin: {
defaultChecked: false.checkedChildren: "Open"}}, {item: {
label: "Rate".name: "Rate"
},
type: "Rate".mixin: {
defaultValue: 2.5}}, {item: {
label: "Tag".name: "Tag"
},
type: "Tag".value: [{ name: "tag1" }, { name: "tag2"}]}, {item: {
label: "Cascader".name: "Cascader"
},
type: "Cascader".mixin: {
options: [{value: "zhejiang".label: "Zhejiang".children: [{value: "hangzhou".label: "Hangzhou".children: [{value: "xihu".label: "West Lake"}]}]}}, {item: {
label: "DatePicker".name: "DatePicker"
},
type: "DatePicker".mixin: {
defaultValue: moment(today),
disabledDate: disabledDate
}
},
{
item: {
label: "MonthPicker".name: "MonthPicker"
},
type: "MonthPicker".mixin: {
defaultValue: moment(today)
}
},
{
item: {
label: "RangePicker".name: "RangePicker"
},
type: "RangePicker".mixin: {
defaultValue: moment(today),
disabledDate: disabledDate
}
},
{
item: {
label: "WeekPicker".name: "WeekPicker"
},
type: "WeekPicker".mixin: {
defaultValue: moment(today)
}
},
{
item: {
label: "TimePicker".name: "TimePicker"
},
type: "TimePicker".mixin: {
defaultValue: moment(today)
}
},
{
item: {
label: "Checkbox".name: "Checkbox"
},
type: "Checkbox".mixin: {
options: [{label: "Apple".value: "Apple" },
{ label: "Pear".value: "Pear" },
{ label: "Orange".value: "Orange"}].defaultValue: "Apple"}}, {item: {
label: "Radio".name: "Radio"
},
type: "Radio".mixin: {
options: [{label: "Apple".value: "Apple" },
{ label: "Pear".value: "Pear" },
{ label: "Orange".value: "Orange"}].defaultValue: "Apple"}}, {item: {
label: "Transfer".name: "Transfer"
},
type: "Transfer".mixin: {}}, {item: {
label: "TreeSelect".name: "TreeSelect"
},
type: "TreeSelect".mixin: {
idKey: "key".titleKey: "title".childrenKey: "children".treeData: [{key: "001".title: "001".children: [{key: "001001".title: "001001"
},
{
key: "001002".title: "001002"}]}, {key: "002".title: "002".children: [{key: "002001".title: "002001"
},
{
key: "002002".title: "002002"}]}}, {item: {
label: "Select".name: "Select"
},
type: "Select".mixin: {
options: [{label: "Apple".value: "Apple" },
{ label: "Pear".value: "Pear" },
{ label: "Orange".value: "Orange"}].defaultValue: "Apple"}}, {item: {
label: "SelectNext".name: "SelectNext"
},
type: "Select".mixin: {
options: [{label: "Apple".value: "Apple".childer: [{label: "Apple1".value: "Apple1"
},
{
label: "Apple2".value: "Apple2"}]}, {label: "Pear".value: "Pear".childer: [{label: "Pear1".value: "Pear1"}]}, {label: "Orange".value: "Orange".childer: [{label: "Orange1".value: "Orange1"}}]].next: "childer".defaultValue: "Apple1"}}, {item: {
label: "Upload".name: "Upload"
},
type: "Upload".value: "Upload file".mixin: {
name: "files".action: "action"}}, {item: {
name: "button". tailFormItemLayout },type: "Button".value: "Custom button"
},
{
item: {
label: "".name: "Submit". tailFormItemLayout },type: "Submit".mixin: {
label: "Submit button"}}};export { tableFormData };
Copy the code
3. Render form components through data
import "./styles.css";
import "./index.less";
import AntdForm from "./AntdForm";
import "antd/dist/antd.css";
import { tableFormData } from "./Data/tempData";
export default function App() {
return (
<div className="App">
<h1>Data generation form</h1>
<AntdForm formData={tableFormData} />
</div>
);
}
Copy the code