随着 Web 应用的不断发展,业务表单作为 Web 应用的重要组成部分之一,在 Web 应用中的作用越来越被重视。然而,开发表单页面过程中,总是需要不断地去实现各种各样的表单、表单组件,导致代码重复,效率低下。因此,我们需要一种可配置的通用业务表单,我们可以在需要的时候对其进行配置,快速生成业务表单页面,提高开发效率。
在本文中,我们将介绍如何基于 React Redux 打造可配置的通用业务表单,这个表单可以用于各种业务场景,满足各种需求。接下来,我们将逐一介绍这个表单的实现过程。
设计数据结构
对于一个可配置的通用业务表单,我们需要对其数据结构进行设计。下面是我们设计的数据结构:
{ // 表单属性 formProps: { // 表单名称 title: '', // 提交按钮文本 submitText: '', // 表单提交接口地址 apiUrl: '', // 表单提交方法 method: 'POST', // 表单提交成功回调 successCallback: () => {}, // 表单提交失败回调 failCallback: () => {}, }, // 表单字段 fields: [ { // 字段名称 name: '', // 字段类型 type: '', // 字段默认值 defaultValue: '', // 字段校验规则 rules: [], // 字段属性 props: {}, // 字段选项 options: [], // 子表单,用于嵌套表单 subForm: {}, }, ... ], }
在数据结构设计中,我们用 formProps
存储表单属性,用 fields
存储表单字段。其中,fields
是一个数组,每个元素表示一个表单字段。name
表示字段名称,type
表示字段类型,defaultValue
表示字段默认值,rules
表示字段校验规则,props
表示字段属性,options
表示字段选项,subForm
用于嵌套表单。
实现表单组件
在数据结构设计完成后,我们需要实现表单组件。这里我们使用 Antd
组件库来实现表单组件。下面是完整的表单组件代码:
import React from "react"; import { Form, Input, Button, Select } from "antd"; import { connect } from "react-redux"; import axios from "axios"; const FormItem = Form.Item; const Option = Select.Option; class DynamicForm extends React.Component { constructor(props) { super(props); this.state = { loading: true }; } componentDidMount() { const { fetchData } = this.props; fetchData && fetchData().then(() => this.setState({ loading: false })); } validateField = (value, rules) => { let errorMsg = ""; for (let i = 0; i < rules.length; i++) { const rule = rules[i]; if (rule.required && !value) { errorMsg = "该字段为必填项"; } else if (rule.maxLength && value && value.length > rule.maxLength) { errorMsg = `该字段最多输入${rule.maxLength}个字符`; } if (!!errorMsg) { break; } } return errorMsg; }; validateForm = values => { const { fields } = this.props; let errorFields = []; fields.forEach(field => { const { name, rules } = field; const value = values[name]; const errorMsg = this.validateField(value, rules); if (!!errorMsg) { errorFields.push({ name, errorMsg }); } }); return errorFields; }; handleSubmit = e => { e.preventDefault(); const { form, onSubmit, successCallback, failCallback } = this.props; form.validateFields((err, values) => { if (!!err) { console.log("err", err); return; } const errorFields = this.validateForm(values); if (errorFields.length > 0) { console.log("errorFields", errorFields); return; } onSubmit && onSubmit(values).then(() => { successCallback && successCallback(values); }).catch(() => { failCallback && failCallback(values); }); }); }; renderField = (field, getFieldDecorator) => { const { name, type, defaultValue, rules, props = {}, options = [], subForm = {}, } = field; let fieldNode = null; switch (type) { case "input": const { prefix, suffix, addonBefore, addonAfter, ...otherProps } = props; fieldNode = ( <Input placeholder="" defaultValue={defaultValue} prefix={prefix} suffix={suffix} addonBefore={addonBefore} addonAfter={addonAfter} {...otherProps} /> ); break; case "select": fieldNode = ( <Select defaultValue={defaultValue}> {options.map(option => ( <Option value={option.value} key={option.value}> {option.label} </Option> ))} </Select> ); break; case "subForm": const { formProps, fields } = subForm; fieldNode = <DynamicForm formProps={formProps} fields={fields} />; break; default: break; } if (!fieldNode) { return null; } const { label, hidden = false } = props; const validateRules = rules.map(rule => ({ required: !!rule.required, message: rule.message })); return ( <FormItem key={name} label={label} style={{ display: hidden ? "none" : "" }}> {getFieldDecorator(name, { rules: validateRules, initialValue: defaultValue, })(fieldNode)} </FormItem> ); }; render() { const { loading } = this.state; const { formProps, fields = [], form } = this.props; const { getFieldDecorator } = form; const { title, submitText, apiUrl, method } = formProps; return ( <Form onSubmit={this.handleSubmit}> <h2>{title}</h2> {fields.map(field => this.renderField(field, getFieldDecorator))} <FormItem> <Button type="primary" htmlType="submit" loading={loading}> {submitText || "提交"} </Button> </FormItem> </Form> ); } } const mapStateToProps = () => ({}); const mapDispatchToProps = (dispatch, props) => ({ onSubmit: values => { const { apiUrl, method = "POST" } = props.formProps || {}; return axios({ method, url: apiUrl, data: values, }); }, }); const WrappedDynamicForm = Form.create()(connect(mapStateToProps, mapDispatchToProps)(DynamicForm)); export default WrappedDynamicForm;
使用动态表单
使用动态表单,只需要在组件的 render
函数中传入数据即可生成表单。下面是一个使用示例:
import React from "react"; import WrappedDynamicForm from "./components/dynamic-form"; const data = { formProps: { title: "添加用户", submitText: "提交", method: "POST", apiUrl: "/api/user", successCallback: () => { alert("添加用户成功"); }, failCallback: () => { alert("添加用户失败"); }, }, fields: [ { name: "username", type: "input", props: { label: "用户名", required: true, maxLength: 20, }, rules: [ { required: true, message: "用户名不能为空" }, { maxLength: 20, message: "用户名长度不能超过20个字符" }, ], }, { name: "password", type: "input", props: { label: "密码", required: true, }, rules: [{ required: true, message: "密码不能为空" }], }, { name: "sex", type: "select", props: { label: "性别", required: true, }, options: [ { label: "男", value: "男" }, { label: "女", value: "女" }, ], rules: [{ required: true, message: "性别不能为空" }], }, ], }; class App extends React.Component { render() { return ( <div> <WrappedDynamicForm {...data} /> </div> ); } } export default App;
在使用这个表单组件时,只需要将数据传入组件即可快速生成表单。数据结构清晰,易于扩展和配置,因此可以应用于多种业务场景中。
总结
在本文中,通过对数据结构和代码实现的介绍,我们学习了如何基于 React Redux 打造可配置的通用业务表单,这个表单可以用于各种业务场景,满足各种需求。通过这样的表单组件,我们可以快速构建出一个业务功能完备、可扩展的表单页。希望这篇文章能够对各位前端开发者在实际工作中有所启发,让你的工作更加高效。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a25f7cadd4f0e0ffa8287a