picture a scence, we need to develop a background application with many forms.you think about it,so yeasy. use Vue's template, use V-model to obtain and set the values of input components, and use v-if, v-show, v-for and other instructions to control the linkage between components. If react is used, it is almost the same, except that JS statements are used in the template. ok, it looks really simple. let's look at a few scenarios.
1.Traditional form development
1.1 Layout level
The above two figures show a very common form about employee reimbursement.This form is developed using the UI framework ant design. Let's take a look at its source code.
The structure of the whole form is like a tree. Like building blocks, we first input the form label, and then insert the card label into the form label. In order to achieve responsive layout, we insert row labels and column labels into the card label,Keep repeating this process. Finally, we built the whole form interface.
However, there are many types of reimbursement, such as business trip reimbursement, transportation reimbursement, communication expense reimbursement, etc. each reimbursement has the same part and many different parts. Each type requires the development of a form. If all types are concentrated on one form, the form will become a lot of logical judgments, and the interaction is very complex and difficult to maintain. If we repeatedly use the code just now to develop forms of different reimbursement types, it will become very tired. And later, if the customer requires to add or delete some input, we will find the corresponding location of the source code to modify, edit and package. If the source code is very long, it is difficult to find
2.Form we expect
Forms have a tree structure, interaction between input components, form validation, etc. can we store all these information in the database? Then the information of the form is obtained through HTTP request on the web page, and the information is parsed and rendered to the browser. What kind of data structure can well describe the tree structure of HTML? Yeah, I did, JSON.
2.1 First, use JSON to describe the form structure
{
"schema": {
"type": "object",
"properties": {
"formlayout": {
"type": "void",
"x-component": "FormLayout",
"x-component-props": {},
"properties": {
"card": {
"type": "void",
"x-component": "Card",
"x-component-props": {
"title": "报销基本信息"
},
"properties": {
"formgrid": {
"type": "void",
"x-component": "FormGrid",
"x-validator": [],
"x-component-props": {},
"properties": {
"gridcolumn1": {
"type": "void",
"x-component": "FormGrid.GridColumn",
"x-validator": [],
"x-component-props": {},
"properties": {
"reimbursementName": {
"type": "string",
"title": "报销名称",
"x-decorator": "FormItem",
"x-component": "Input",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "reimbursementName"
},
"startEndDate": {
"type": "string[]",
"title": "开始结束日期",
"x-decorator": "FormItem",
"x-component": "DatePicker.RangePicker",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {}
},
"payer": {
"title": "付款单位",
"x-decorator": "FormItem",
"x-component": "Select",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "payer"
},
"account": {
"title": "收款账户",
"x-decorator": "FormItem",
"x-component": "Select",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "account"
},
"level": {
"title": "报销人职级",
"x-decorator": "FormItem",
"x-component": "Select",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "level"
},
"standard": {
"type": "string",
"title": "标准",
"x-decorator": "FormItem",
"x-component": "Input.TextArea",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "standard",
"x-pattern": "readPretty",
"default": "-"
},
"phoneNumber": {
"type": "string",
"title": "电话",
"x-decorator": "FormItem",
"x-component": "Input",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "phoneNumber"
},
"amount": {
"type": "string",
"title": "报销金额",
"x-decorator": "FormItem",
"x-component": "Input",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "amount"
}
}
},
"gridcolumn2": {
"type": "void",
"x-component": "FormGrid.GridColumn",
"x-validator": [],
"x-component-props": {}
}
}
}
}
}
}
}
}
}
}
How to render in react? We can write a function component to recursively call react CreateElement method for rendering
const SchemaField = createSchemaField({
components: {
FormGrid,
FormLayout,
FormItem,
DatePicker,
Input,
Card,
Button
},
})
<SchemaField schema={schemainfo.schema}/>
function createSchemaField(options) {
function schemaField(props) {
const schema = props.schema;
return React.createElement(
'div',
{
options: options,
},
render(schema)
)
}
function getComponent(name) {
let component = options.components;
const arr = name.split('.');
while (arr.length) {
component = component[arr.shift()];
}
return component;
}
function render(fieldSchema) {
function renderChildren() {
if (!fieldSchema.properties) {
return null;
}
return React.createElement(Fragment, null, Reflect.ownKeys(fieldSchema.properties).map((key) => {
return render(fieldSchema.properties[key]);
}));
}
if (fieldSchema.type === 'object') {
if (fieldSchema['x-component']) {
const ParentComponent = getComponent(fieldSchema['x-component']);
return React.createElement(ParentComponent, null, renderChildren());
} else {
return renderChildren();
}
} else if (fieldSchema.type === 'void') {
if (fieldSchema['x-component']) {
const ParentComponent = getComponent(fieldSchema['x-component']);
return React.createElement(ParentComponent, null, renderChildren());
} else {
return renderChildren();
}
} else {
const FinnalComponent = getComponent(fieldSchema['x-component']);
if (fieldSchema['x-decorator']) {
const DecoratorComponent = options.components[fieldSchema['x-decorator']];
return React.createElement(DecoratorComponent, {label: fieldSchema.title}, React.createElement(FinnalComponent, fieldSchema['x-component-props']));
}
return React.createElement(FinnalComponent, null);
}
}
// 返回个组件---> react 函数组件
return schemaField
}
2.2 Second, add reactions,validator,props of each input component
{
"schema": {
"type": "object",
"properties": {
"formlayout": {
"type": "void",
"x-component": "FormLayout",
"x-component-props": {},
"properties": {
"card": {
"type": "void",
"x-component": "Card",
"x-component-props": {
"title": "报销基本信息"
},
"properties": {
"formgrid": {
"type": "void",
"x-component": "FormGrid",
"x-validator": [],
"x-component-props": {},
"properties": {
"gridcolumn1": {
"type": "void",
"x-component": "FormGrid.GridColumn",
"x-validator": [],
"x-component-props": {},
"properties": {
"reimbursementName": {
"type": "string",
"title": "报销名称",
"x-decorator": "FormItem",
"x-component": "Input",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "reimbursementName",
"required": true,
},
"startEndDate": {
"type": "string[]",
"title": "开始结束日期",
"x-decorator": "FormItem",
"x-component": "DatePicker.RangePicker",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"required": true,
},
"payer": {
"title": "付款单位",
"x-decorator": "FormItem",
"x-component": "Select",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"x-reactions": {
"run": "$effect(()=>{\r\n setTimeout(() => {\r\n $self.setOptions([\r\n { value: 1, label: '甲单位' },\r\n { value: 2, label: '乙单位' },\r\n { value: 3, label: '丙单位' },\r\n { value: 4, label: '丁单位' },\r\n { value: 5, label: '戊单位' }\r\n ]);\r\n }, 300)\r\n},[])"
},
"name": "payer",
"required": true,
},
"account": {
"title": "收款账户",
"x-decorator": "FormItem",
"x-component": "Select",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"x-reactions": {
"run": "$effect(() => {\n setTimeout(() => {\n $self.setOptions([\n { value: 1, label: '6211 12123' },\n { value: 2, label: '6222 14356' },\n { value: 3, label: '6324 32322' },\n { value: 4, label: '6789 23232' },\n { value: 5, label: '8756 23230' }\n ])\n }, 300)\n}, [])\n"
},
"name": "account",
"required": true,
},
"level": {
"title": "报销人职级",
"x-decorator": "FormItem",
"x-component": "Select",
"x-validator": [],
"x-component-props": {
},
"x-decorator-props": {},
"x-reactions": {
"run": "$effect(() => {\n setTimeout(() => {\n $self.setOptions([\n { value: 1, label: \"总经理\" },\n { value: 2, label: \"总监\" },\n { value: 3, label: \"主管\" },\n { value: 4, label: \"高级专员\" },\n { value: 5, label: \"专员\" },\n ])\n }, 300)\n}, [])\n$props({\n onChange(value){\n let standard = '';\n if (value === 1) {\n standard = `住宿费: 1000元/天\\n补贴: 1000元/天`;\n } else if (value === 2) {\n standard = `住宿费: 800元/天\\n补贴: 800元/天`;\n } else if (value === 3) {\n standard = `住宿费: 600元/天\\n补贴: 600元/天`;\n } else if (value === 4) {\n standard = `住宿费: 400元/天\\n补贴: 400元/天`;\n } else {\n standard = `住宿费: 200元/天\\n补贴: 200元/天`;\n }\n $form.setFieldsValue({standard: standard});\n }\n})\n"
},
"name": "level",
"required": true,
},
"standard": {
"type": "string",
"title": "标准",
"x-decorator": "FormItem",
"x-component": "Input.TextArea",
"x-validator": [],
"x-component-props": { "readOnly": true },
"x-decorator-props": {},
"name": "standard",
},
"phoneNumber": {
"type": "string",
"title": "电话",
"x-decorator": "FormItem",
"x-component": "Input",
"x-validator": 'phone',
"x-component-props": {},
"x-decorator-props": {},
"name": "phoneNumber",
"required": true,
},
"amount": {
"type": "string",
"title": "报销金额",
"x-decorator": "FormItem",
"x-component": "Input",
"x-validator": [],
"x-component-props": {},
"x-decorator-props": {},
"name": "amount",
"required": true,
},
"button": {
"type": "void",
"x-component": "Button",
"x-component-props": { "title": "提交", "type": "primary" },
"x-reactions": {
"run": "$props({\n onClick() {console.log('xxx'); $form.validateFields().then(values => {\n alert(JSON.stringify(values))\n }).catch(err=>{})\n },\n})\n"
},
"name": "button",
}
}
},
"gridcolumn2": {
"type": "void",
"x-component": "FormGrid.GridColumn",
"x-validator": [],
"x-component-props": {}
}
}
}
}
}
}
}
}
}
}
one to attention, how to run the reaction codes, we use the function constructor Function
(new Function('$form', '$self', '$effect', '$props', fieldSchema['x-reactions'].run)).call(null, form, field, useEffect);
3.How to create the JSON Structure, we need to develop a Form drag and drop system to create the JSON
designable-antd.formilyjs.org/