react 表单数据形式配置化设计

目录
  • 前言
  • 实现
    • 一般实现
    • 配置化的实现
    • 实现思路
  • 具体实现
    • 使用示例
  • 总结

前言

在日常的中后台系统开发中,表单是和我们打交道非常多的名词。但是在一般的表单实现中、我们会做着很多重复的工作,不停在写 FormItem...,以及为组件加上“请输入/请选择”等无脑的 placeholder 文本和“请输入xx/请选择xx”等必填提示。其次表单一般都存在编辑页和详情页,而为了代码更好的维护性通常会将编辑和详情用一套代码实现。此时我们的代码里就会出现“isEdit ? 表单组件 : 纯文本”这样无脑且重复率高的代码。秉承着更少代码更多产出的原则,我设计了一套配置化逻辑来提升这一开发体验。

实现

一般实现

// 一般实现
import React from 'react';
import { Form, Input, Select } from 'antd';

const Demo = (props) => {
  const { form: { getFieldDecorator }, obj = {}, isEdit } = props;
  return (
    <>
      <FormItem label="姓名" >
        {isEdit ? obj.name || '-' :
          getFieldDecorator('name', {
            initialValue: obj.name,
          })(
            <Input placeholder="请输入" />
          )
        }
      </FormItem>
      <FormItem label="性别" >
        {isEdit ? obj.sex || '-' :
          getFieldDecorator('sex', {
            initialValue: obj.sex,
            rules: [{ required: true, message: '请选择性别' }],
          })(
            <Select placeholder="请选择" >
              <Option key="male" value="male">男</Option>
              <Option key="female" value="female">女</Option>
            </Select>
          )
        }
      </FormItem>
       <FormItem label="手机号" >
        {isEdit ? obj.phone || '-' :
          getFieldDecorator('phone', {
            initialValue: obj.phone,
            rules: [{ required: true, message: '请输入手机号' }],
          })(
            <Input placeholder="请输入" />
          )
        }
      </FormItem>
    <>
  )
}

配置化的实现

// 配置化的实现
import React from 'react';
import { renderDataForm } from 'src/util/renderDataForm';

const Demo = (props) => {
  const { form, obj = {}, isEdit } = props;
  const conf = [{
    label: '姓名', // 表单的label
    field: 'name', // 表单字段名
    initialValue: obj.name, // 表单默认值
    required: false, // 是否必填、默认必填
  }, {
    label: '性别',
    field: 'sex',
    initialValue: obj.sex,
    formItemType: 'Select', // 表单类型默认 Input
    options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }], // 下拉选项
  }, {
    label: '手机号',
    field: 'phone',
    initialValue: obj.phone,
  }];
  const dataForm = isEdit ? 'form' : 'text';
  // 传入form,表单配置,想要的数据形式
  return renderDataForm(form, conf, dataForm));
}

实现思路

如上图所示,无论是在详情页中显示文本亦或是编辑页中的表单组件包裹的数据,其实本身所对应的都是同一个数据,只是展示形式不一样而已。在这里我们暂时将数据的形式定为表单组件形式与文本形式。其实在实际的使用中,由于数据的收集形式不同,会出现第三种数据形式。它就是表单文本形式,一种以文本展示但数据可被表单自动收集的形式,我把它定义为 FormText(如下所示)。至此,针对实现数据详情与编辑形式的方案有了这样两种,表单与文本组合或表单与表单文本组合的实现。本次我选择表单与文本组合的方案。

/**
 * 用于 Form 表单内部受控展示文本
 */
export default class FormText extends Component {
  render() {
    const { value, formatMethod = a => a, defaultText = '-', ...resetProps } = this.props;
    return <span {...resetProps}>{formatMethod(value) || defaultText}</span>;
  }
}
// 使用
<FormItem label="姓名">
  {getFieldDecorator('name', {
      initialValue: 'egg',
  })(<FormText />)}
</FormItem>

具体实现

1、形式选择(表单组件 or 文本)

const renderDataForm = (form, conf = {}, dataForm = 'form') => {
  // customRenderText 自定义文本形式
  const { customRenderText } = conf;
  return (
    <FormItem label={conf.label} {...conf.formItemProps} >
      {dataForm === 'form' ? renderFormItem(form, conf) :
        customRenderText ? customRenderText(conf) : renderText(conf) }
    </FormItem>
  );
};

2、表单组件选择

export const renderFormItem = (form, rest) => {
  const { getFieldDecorator } = form;
  const { label = '', field = '', formItemType = 'input', initialValue, required = true, rules = [], ...itemRest } = rest;
  return (getFieldDecorator(field, {
    initialValue,
    rules: [
      // 必填提示
      { required, message: renderMessage(formItemType, label) },
      ...rules,
    ],
    ...(formItemType === 'upload' ? { // Upload组件的通用配置
      getValueFromEvent: (e) => {
        if (Array.isArray(e)) {
          return e;
        }
        return e && e.fileList;
      },
      valuePropName: 'fileList' } : {}),
  })(
    renderItem(formItemType, itemRest)
  ));
};

// 选择表单组件
const renderItem = (formItemType, itemRest) => {
  const { options = [], CustomFormItem } = itemRest;
  const obj = { Input, TextArea, InputNumber, Upload, Select, RadioGroup, CheckboxGroup, DatePicker };
  // 自定义的表单组件
  if (formItemType === 'CustomFormItem') {
    return <CustomFormItem {...itemRest} />;
  }
  // 不存在对应组件时返回默认的 Input 组件
  if (!obj[formItemType]) {
    return <Input placeholder="请输入" {...itemRest} />;
  }
  const Comp = obj[formItemType];
  // 双标签组件处理
  if (['Select', 'Upload'].includes(formItemType)) {
    return formItemType === 'Upload' ? (
      <Upload
        {...itemRest}
      >
        <Button><Icon type="upload" />上传</Button>
      </Upload>
    ) : (
      <Comp {...getDefaultCompProps(itemRest)} {...itemRest} >
        {options.map(el => (
          <Option key={el.value} value={el.value}>{el.label || el.name}</Option>))}
      </Comp>
    );
  }
  // 单标签组件
  return <Comp {...getDefaultCompProps(itemRest)} {...itemRest} />;
};

// 获取组件属性
const getDefaultCompProps = (conf = {}) => {
  const { formItemType } = conf;
  const props = {};
  props.placeholder = renderMessage(formItemType);
  if (formItemType === 'InputNumber') {
    // zeroOmit 小数点后多余的零是否省略,limitDecimal 限制最长的小数位数
    const { zeroOmit = true, limitDecimal = 6 } = conf;
    const limitDecimalsF = (value) => {
      const reg = new RegExp(`^(-)*(\\d+)\\.(\\d{${limitDecimal}}).*$`);
      return `${value}`.replace(reg, '$1$2.$3');
    };
    if (zeroOmit) {
      props.formatter = limitDecimalsF;
      props.parse = limitDecimalsF;
    }
  }
  if (formItemType === 'Input') {
    props.maxLength = 100; // 输入框的默认最大输入字符长度
  }
  if (formItemType === 'TextArea') {
    props.maxLength = 500; // 文本框的默认最大输入字符长度
  }
  return props;
};

3、映射文本

export const renderText = (rest) => {
  const { formItemType = 'Input', initialValue, selectOptions = [], selectMode = '', options = [] } = rest;
  switch (formItemType) {
  case 'RadioGroup':
    return (options.find(item => item.value === initialValue) || {}).label || '-';
  case 'DatePick':
    const { format = 'YYYY-MM-DD HH:mm:ss' } = rest;
    // 日期组件组件值格式化为对应的 文本
    return initialValue !== undefined ? moment(initialValue).format(format) : '-';
  // ...code
  default:
    return bizStringFormat(initialValue); // 无 值 时 默认 ‘-'
  }
}

4、通用校验规则整理

export const postCode = /^[0-9]{6}$/;
export const phone = /^1\d{10}$/;
// ...其他正则

// form rules
export const postCodeRule = {
  pattern: postCode,
  message: '请输入6位数字',
};
export const phoneRule = {
  pattern: phone,
  message: '请输入11位号码',
};
// ...其他表单校验规则

使用示例

const Demo = (props) => {
  const { form } = props;
  // 数据
  const obj = {
    email: '123@egg.com',
    addr: '派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星',
    sort: 'up',
    birthday: '1999-01-23',
    sex: 'male',
    file: [{ fileId: '123', name: '信用承诺书', size: 1024 }],
  };
  // 因为数据的形式默认为表单,所以 dataForm: 'form' 可不配置
  const formConf = [{
    label: '邮箱',
    field: 'email',
    initialValue: obj.email,
    rules: [emailRule], // emailRule 为邮箱校验规则
  }, {
    label: '地址',
    field: 'addr',
    initialValue: obj.addr,
    formItemType: 'TextArea',
  }, {
    label: '排序',
    field: 'sort',
    initialValue: obj.sort,
    formItemType: 'Select',
    options: [{ value: 'up', label: '升序' }, { value: 'down', label: '降序' }],
  }, {
    label: '生日',
    field: 'birthday',
    initialValue: obj.birthday,
    formItemType: 'DatePicker',
    format: 'YYYY-MM-DD', // 日期组件的格式配置字段
  }, {
    label: '性别',
    field: 'sex',
    initialValue: obj.sex,
    formItemType: 'RadioGroup',
    options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }],
  }, {
    label: '信用承诺书',
    field: 'file',
    initialValue: obj.file,
    formItemType: 'Upload',
  }];
  const dataForm = isEdit ? 'form' : 'text';
  // 将配置遍历传入renderDataForm
  // 当然你也可以封装成组建,直接向组建传入 form、formConf,减少遍历的重复书写和整洁
  return formConf.map(item => renderDataForm(form, item, dataForm));

最终呈现如下:

1.编辑

 2. 触发校验

3.详情

总结

虽然,在目前的前端领域,关于页面配置、可视化等更加复杂的能力,已有更丰富和更全面的实现。比如我们前端团队的无相应用早已实现整个表单页的配置化能力。而本文展示的表单块的代码配置化能力接入较为轻量、内容上更容易理解。

到此这篇关于react 表单数据形式配置化设计的文章就介绍到这了,更多相关react 表单数据内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • react ant Design手动设置表单的值操作

    1.设置表单的值 this.props.form.setFieldsValue({ name:"张三", }); 2.清空表单的值 this.props.form.resetFields(); 3.获取某一输入框的值 this.props.form.getFieldValue('newPassword'); 4.获取整个表单的值 this.props.form.getFieldsValue(); 多看官方文档就知道这些东西了 补充知识:react使用antd表单赋值,用于修改弹框 1.

  • React实现复杂搜索表单的展开收起功能

    给时间时间,让过去过去. 上节我们写过了[搜索]表单,以及查询.重置功能.本节对于需要展开收起效果的查询表单 进行概述,主要涉及前端样式知识. 样式效果如下: 思路:在Search组件中定义两个组件renderAdvancedForm,renderSimpleForm,其中renderSimpleForm中只有五个查询选项,而在renderAdvancedForm包含所有的搜索选项.点击'展开''收起'按钮调用onClick={toggleForm}切换form显示样式即可. 1. 写rende

  • React如何利用Antd的Form组件实现表单功能详解

    一.构造组件 1.表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等. 这里先引用了封装的表单域 <Form.Item /> 2.使用Form.create处理后的表单具有自动收集数据并校验的功能,但如果不需要这个功能,或者默认的行为无法满足业务需求,可以选择不使用Form.create并自行处理数据 经过Form.create()包装过的组件会自带this.props.form属性,this.props.form提供了很多API来处理数据,如getFieldDe

  • react使用antd的上传组件实现文件表单一起提交功能(完整代码)

    最近在刚刚开始使用react做项目,非常不熟练,非常小白.小白同学可以阅读了,因为我会写的非常简单,直白. 项目中需要实现表单中带附件提交,上传文件不单独保存调接口. import { Form, Button, Upload } from 'antd'; import { UploadOutlined } from '@ant-design/icons'; const normFile = (e) => { console.log('Upload event:', e); if (Array.

  • react antd实现动态增减表单

    之前写动态表单遇到过坑,就是用index下标做key会导致bug,而且很严重! 今天有空写下文章记录下:怎么处理和逻辑 我用的是antd3的版本,3和4的表单有点不一样,不过差别应该不大. 需求: 1.选择类型切换展示固定的模板 2.通过新增字段可以动态增减表单里面的每一行 3.控制每一行的字段是否需要必填 4.编辑时候回填参数 效果图: 部分关键代码: import React, { Component } from 'react'; import styles from './index.l

  • 基于React实现表单数据的添加和删除详解

    前言 最近在学习React,做了一个简单的Demo,用以自勉及和有需要的朋友们参考学习. 实现功能 在输入框中输入数据后,点击保存按钮,数据将会逐一显示在输入框下方,点击保存后显示的任何一条数据,该数据即可被删除. 实现思路 在开始实现之前,我们需要理清我们的思路,这样才能更好地去完成预定功能. 点击保存按钮时,输入框中的数据读取,可通过onChange绑定事件,获得输入框数据:e.target.value 自定义一个事件,输入数据后,点击保存按钮时,数据的存储操作交由该事件完成 当不断点击保存

  • react 表单数据形式配置化设计

    目录 前言 实现 一般实现 配置化的实现 实现思路 具体实现 使用示例 总结 前言 在日常的中后台系统开发中,表单是和我们打交道非常多的名词.但是在一般的表单实现中.我们会做着很多重复的工作,不停在写 FormItem...,以及为组件加上“请输入/请选择”等无脑的 placeholder 文本和“请输入xx/请选择xx”等必填提示.其次表单一般都存在编辑页和详情页,而为了代码更好的维护性通常会将编辑和详情用一套代码实现.此时我们的代码里就会出现“isEdit ? 表单组件 : 纯文本”这样无脑

  • 基于react hooks,zarm组件库配置开发h5表单页面的实例代码

    最近使用React Hooks结合zarm组件库,基于js对象配置方式开发了大量的h5表单页面.大家都知道h5表单功能无非就是表单数据的收集,验证,提交,回显编辑,通常排列方式也是自上向下一行一列的方式显示 , 所以一开始就考虑封装一个配置化的页面生成方案,目前已经有多个项目基于此方式配置开发上线,思路和实现分享一下. 使用场景 任意包含表单的h5页面(使用zarm库,或自行适配自己的库) 目标 代码实现简单和简洁 基于配置 新手上手快,无学习成本 老手易扩展和维护 写之前参考了市面上的一些方案

  • 前端vue2 element ui高效配置化省时又省力

    目录 前言 一.场景介绍 1. 业务场景 2. 配置化想法萌生 二.设计 & 实现 1. 设计协议 2. 实现渲染器 3. render函数 & 配置数据 4.丰富组件能力,实现业务 三.配置静态化 前言 这篇文章是笔者曾经全盘负责了接近一年的广告投放系统沉淀下来的开发经验,大家各取所需,不喜勿喷-当然啦,有自己的见解.更好的建议的大牛朋友们,我们评论区见-hhh,大多提点高见,让笔者继续学习继续进步! 本文从 场景介绍 . 设计&实现 . 性能优化 三个部分进行讲解.笔者当时的技

  • antd form表单数据回显操作

    index页面有一个表格,有一个新增按钮,点击新增按钮或表格编辑弹出表单模块,如果是编辑,会回显对应的表格中的数据 //index页面 import React from 'react' import { Table, Button, message, Input, Select, Modal, } from 'antd'; const Option = Select.Option; import AddOrEdit from './AddOrEdit ' class List extends

  • react中事件处理与柯里化的实现

    目录 1. 事件处理 阻止默认行为 合成事件 2. 柯里化 柯里化的目的 一个简单的例子 1. 事件处理 React 中元素也可接受.处理事件,但是在语法上有一点不同. 在React 中所有事件的命名采用的是小驼峰,而非原生 DOM 的纯小写,所有事件需要我们传入一个函数,而非字符串. 例如: const Button = () => { const handleClick = () => { console.log('click') } return <button onClick={

  • jQuery ajax中使用serialize()方法提交表单数据示例

    jQuery ajax中数据以键值对(Key/Value)的形式发送到服务器,使用ajax提交表单数据时可以使用jQuery ajax的serialize() 方法表单序列化为键值对(key1=value1&key2=value2-)后提交.serialize() 方法使用标准的 URL-encoded 编码表示文本字符串.下面是使用serialize()序列化表单的实例: 复制代码 代码如下: $.ajax({   type: "POST",   url: ajaxCallU

  • Javaweb获取表单数据的多种方式

    Javaweb获取表单数据的几种方式 一.通过键值对的形式获取表单数据 getParameter(String name):通过key,返回一个value. getParameterValues(String name):通过key返回一个string数组(多个值) getParameterNames():返回form表单中的所有key值. 下面介绍通过键值对获取form表单数据的数据的方法: @WebServlet({ "/FormServlet", "/form"

  • mysql表名忽略大小写配置方法详解

    linux下mysql默认是要区分表名大小写的.mysql是否区分大小写设置是由参数lower_case_table_names决定的,其中: 1)lower_case_table_names = 0  区分大小写(即对大小写不敏感),默认是这种设置.这样设置后,在mysql里创建的表名带不带大写字母都没有影响,都可以正常读出和被引用. 2)lower_case_table_names = 1  不区分大小写(即对大小写敏感).这样设置后,表名在硬盘上以小写保存,MySQL将所有表名转换为小写存

  • Spring学习笔记2之表单数据验证、文件上传实例代码

    在上篇文章给大家介绍了Spring学习笔记1之IOC详解尽量使用注解以及java代码,接下来本文重点给大家介绍Spring学习笔记2之表单数据验证.文件上传实例代码,具体内容,请参考本文吧! 一.表单数据验证 用户注册时,需要填写账号.密码.邮箱以及手机号,均为必填项,并且需要符合一定的格式.比如账号需要32位以内,邮箱必须符合邮箱格式,手机号必须为11位号码等.可以采用在注册时验证信息,或者专门写一个工具类用来验证:来看下在SpringMVC中如何通过简单的注释实现表单数据验证. 在javax

  • Ajax 提交表单数据到入库的全盘操作流程分享

    *******php项目中当我们要对数据库进行写入操作时,有时会因为代码没有做防sql注入工作,导致各种不可预知的错误******* 1,index.htm 这是一个很简单的注册页面l 我这是以ajax形式提交数据 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt

随机推荐