美文网首页让前端飞Web前端之路前端开发
Ant Design表单二次封装(1)——基础表单

Ant Design表单二次封装(1)——基础表单

作者: Jkanon | 来源:发表于2019-07-07 12:09 被阅读9次

    前言

    表单是我们日常操作中接触最多的一个组件,antd-design默认提供的组件在表单验证,表单生成方面需要编写太多的重复代码,令人生厌,故在此将其进行二次封装,方便调用以提高开发效率。
    我们期望能够拥有以下特性:

    • 配置式表单,表单项可根据配置生成
    • 表单项支持数据填充
    • 表单内容验证
    • 表单支持收缩展开(如果没有字段是展开可见的,则不显示展开收缩菜单)
    • 在以上情况无法满足需求时,支持自定义注入表单内容

    组件调用示例1——配置式表单

    const formItems = [{
          label: 'id',
          name: 'id',
          itemRender: <Input type="hidden" />,
          hidden: true,
        },
        {
          label: '首页链接',
          name: 'homePage',
          // 验证规则,详见ant-design的表单组件
          rules: [
            {
              required: true,
              message: '首页链接不能为空',
            },
            {
              type: 'url',
              message: '请输入正确的链接地址!',
            },
          ],
          itemRender: <Input placeholder="首页链接" />,
        },
        {
          label: '名称',
          name: 'name',
          // 默认值
          defaultValue: '简书',
          rules: [
            {
              required: true,
              message: '站点名称不能为空',
            },
          ],
          itemRender: <Input placeholder="请输入站点全称" />,
        },
        {
          label: '超时时间'
          name: 'timeOut',
          itemRender: <Input placeholder="超时时间,单位s(秒)" />,
          // 标注toggleField默认隐藏
          toggleField: true,
        },
    }];
    
    
    const formValues = {
        homePage: 'test',
      timeOut: 3,
    };
    <BaseForm
              formItems={formItems}
              formValues={formValues}
              // onValuesChange={onValuesChange}
              // 表单验证通过后触发的提交操作
              // onSubmit={onSubmit}
              wrappedComponentRef={v => {
                this.form = v;
              }}
            />
    
    • 效果



    组件调用示例2——自定义注入表单内容

    class TestForm extends PureComponent {
      render() {
        const {
          form: { getFieldDecorator },
          record,
        } = this.props;
    
        return (
          <Fragment>
            {getFieldDecorator('id', {
              initialValue: record.id,
            })(<Input type="hidden" />)}
            <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="区域名称">
              {getFieldDecorator('name', {
                initialValue: record.name,
                rules: [
                  {
                    required: true,
                    message: '区域名称不能为空',
                  },
                ],
              })(<Input placeholder="区域名称" />)}
            </Form.Item>
          </Fragment>    
         );
      }
    }   
    
    <BaseForm 
              wrappedComponentRef={v => {
                this.form = v;
              }}
            >
          <TestForm /> 
    </BaseForm>
    
    • 最终效果


    组件定义

    • BaseForm.js
    import React, { Component, Fragment } from 'react';
    import { Form, Icon } from 'antd';
    
    import { renderFormItems, submitForm } from '../utils';
    
    @Form.create({
      // 表单项变化时调用,传入onValuesChange进行回调
      onValuesChange({ onValuesChange, ...restProps }, changedValues, allValues) {
        if (onValuesChange) onValuesChange(restProps, changedValues, allValues);
      },
    })
    class BaseForm extends Component {
      static defaultProps = {
        layout: 'horizontal',
        formValues: {},
      };
    
      constructor(props) {
        super(props);
        this.state = {
          /**
           * 表单折叠项可见性
           */
          toggleFieldVisibility: false,
        };
      }
    
      /**
       * 表单提交时触发
       *
       * @param e
       */
      onSubmit = e => {
        if (e) e.preventDefault();
        this.submit(e);
      };
    
      /**
       * 调用表单提交
       * @param e
       * @param extraOptions 其他组件传递进来的额外参数
       */
      submit = (e, extraOptions) => {
        const { form, formValues, onSubmit } = this.props;
        submitForm(form, formValues, onSubmit, extraOptions);
      };
    
      /**
       * 收缩、展开表单
       */
      toggleForm = () => {
        const { toggleFieldVisibility } = this.state;
        this.setState({
          toggleFieldVisibility: !toggleFieldVisibility,
        });
      };
    
      /**
       * 默认表单主体渲染器(根据配置生成)
       * @returns {*}
       */
      renderFormBody = () => {
        const {
          form: { getFieldDecorator },
          formItems,
          formValues,
        } = this.props;
        const { toggleFieldVisibility } = this.state;
    
        return (
          <Fragment>
            {renderFormItems(formItems, getFieldDecorator, formValues, toggleFieldVisibility)}
            {formItems.some(item => item.toggleField) && (
              <div style={{ textAlign: 'center' }} onClick={this.toggleForm}>
                <a style={{ marginLeft: 8 }}>
                  {(toggleFieldVisibility && '收起') || '更多'}
                  {(toggleFieldVisibility && <Icon type="up" />) || <Icon type="down" />}
                </a>
              </div>
            )}
          </Fragment>
        );
      };
    
      render() {
        const { children, layout, form, formValues } = this.props;
    
        return (
          <Form layout={layout} onSubmit={this.onSubmit}>
            {
              // 自定义表单内容,并且注入表单相关属性
              (children &&
              React.Children.map(children, child =>
                React.cloneElement(child, { form, record: formValues }),
              )) ||
              this.renderFormBody()}
          </Form>
        );
      }
    }
    
    export default BaseForm;
    
    
    • utils.js
    import React from 'react';
    import { Form } from 'antd';
    import { isFunction } from 'lodash';
    
    const defaultFormLayout = {
      labelCol: { span: 7 },
      wrapperCol: { span: 13 },
    };
    
    const fetchFieldDecorator = (item, getFieldDecorator) => {
      const { name, defaultValue, rules, itemRender, fieldDecoratorProps } = item;
      return getFieldDecorator(name, {
        initialValue: defaultValue,
        rules,
        ...fieldDecoratorProps,
      })(itemRender);
    };
    
    /**
     * 根据配置渲染单个表单项
     *
     * @param item
     * @param getFieldDecorator
     * @returns {*}
     */
    export const renderFormItem = (item, getFieldDecorator) => {
      const { name, label, formItemLayout, style, formItemProps } = item;
      return (
        <Form.Item key={name} label={label} {...formItemLayout} style={style} {...formItemProps}>
          {fetchFieldDecorator(item, getFieldDecorator)}
        </Form.Item>
      );
    };
    
    /**
     * 根据配置渲染所有的表单项
     * @param items
     * @param getFieldDecorator
     * @param formValues
     * @param toggleFieldVisibility
     * @param layout
     * @return
     */
    export const renderFormItems = (
      items,
      getFieldDecorator,
      formValues = {},
      toggleFieldVisibility,
      layout
    ) =>
      items.map(item => {
        const { style, defaultValue, hidden, ...restProps } = item;
        const display =
          ((hidden === true || (item.toggleField && toggleFieldVisibility === false)) && 'none') ||
          'block';
        let defaultVal = defaultValue;
        if (formValues[item.name] !== undefined) {
          defaultVal = formValues[item.name];
        }
        return renderFormItem(
          {
            formItemLayout: layout === 'vertical' ? null : defaultFormLayout,
            ...restProps,
            style: { ...style, display },
            defaultValue: defaultVal,
          },
          getFieldDecorator
        );
      });
    
    /**
     * submit form
     * @param form
     * @param formValues
     * @param callback
     * @param extraOptions
     */
    export const submitForm = (form, formValues, callback, extraOptions) => {
      if (form) {
        form.validateFieldsAndScroll((err, fieldsValue) => {
          if (!err && isFunction(callback)) {
            callback({ ...formValues, ...fieldsValue }, form, extraOptions);
          }
        });
      } else {
        // eslint-disable-next-line no-console
        console.warn('form is not defined');
      }
    };
    
    

    结语

    希望本文能给您以启发,欢迎留言交流讨论。

    相关文章

      网友评论

        本文标题:Ant Design表单二次封装(1)——基础表单

        本文链接:https://www.haomeiwen.com/subject/ifcbhctx.html