欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

antd快速开发(Form篇)

程序员文章站 2022-05-18 19:30:34
antd快速开发(Form篇) 前言 由于一直在做中台业务,后台项目特别多,但是后台项目的特点是:大量的列表和大量表单,重复开发会降低效率,所以我这边总结了一下使用 组件搭建 的快捷方法。希望能对大家有用。 传统Form搭建 首先传统搭建一个form表单,那么代码可能会是下面这样子 目前只有两个表单 ......

antd快速开发(form篇)

前言

由于一直在做中台业务,后台项目特别多,但是后台项目的特点是:大量的列表和大量表单,重复开发会降低效率,所以我这边总结了一下使用antd组件搭建form的快捷方法。希望能对大家有用。

传统form搭建

首先传统搭建一个form表单,那么代码可能会是下面这样子

import react from 'react';
import { form, input } from 'antd';

@form.create()
class mytestform extends react.component {
  render() {
    const { form: { getfielddecorator } } = this.props;
    return(
      <form>
        <form.item label='姓名'>
          {
            getfielddecorator('username', {
              rules: [
                {
                  required: true,
                  message: '这是必填项'
                }
              ]
            })(<input placeholder="placeholder" />)
          }
        </form.item>
        <form.item label='密码'>
          {
            getfielddecorator('password', {
              rules: [
                {
                  required: true,
                  message: '这是必填项'
                }
              ]
            })(<input placeholder="placeholder" />)
          }
        </form.item>
      </form>
    )
  }
}

export default mytestform;

目前只有两个表单项,看起来代码还挺清晰的,假如这个表单是很复杂的表单,有多个表单项,这块的代码会很长,维护和开发起来都是不方便,最重要的再来一个大的表单,你还是会需要写这么多的代码。这样就影响了开效率。

优化后的form

我们想要的是,尽量少写(不写)重复性的代码,让代码的复用性更高。我这块做了一些优化,主要的流程如图:

antd快速开发(Form篇)

主要的几个点:

  • 将最底层,最重要的部分抽离出来,也就是baseitem组件,baseitem具备的能力是form的能力,双向绑定的能力等,纯净的组件,不包含任何ui层面的东西。
  • ui层的东西也单独抽离出来,以方便以后ui层面的拓展。(例如想自己设计一套ui样式,就只用直接新开发一套ui层面的东西,而不用但心原子层组件的代码)。
  • 每个表单的信息抽离成配置文件,使页面维护起来更方便。

具体的代码如下

//baseitem.js(原子层)
const baseitem = (props) => {
  const { form: { getfielddecorator }, config } = props;
  const { name, children, ...argv } = config;
  return name ? getfielddecorator(name, { ...argv })(children) : (children);
}

export default baseitem;
//itemlayout.js(ui组件)
import react from 'react';
import { form } from 'antd';

//layout也可以用自己的ui组件
const layout = ({ config: { itemoptions }, children }) =>
  <form.item { ...itemoptions } >{ children }</form.item>

const hidden = (ishidden) => {
  const type = typeof(ishidden);
  return (type === 'function' && ishidden()) || type === undefined || ishidden; 
    //默认是显示
}

class itemlayout extends react.component {
  render() {
    const { children } = this.props;
    return(
      <>
        {
          react.children.map(
            children, (child, i) => {
              const { config: { ishidden, ...argv}} = child.props;
              return hidden(ishidden) ? null : //具有隐藏表单项能力
                  react.cloneelement(
                    <layout { ...argv }>{ child }</layout>,
                    {
                      ...children.props
                    }
                  )
            }
          )
        }
      </>
    )
  }
}

export default itemlayout;
//config.js(配置文件)
import react from 'react';
import { input } from 'antd';

export const formconfig = () => {
  return [
    {
      itemoptions : { //form.item的api配置
        label: '姓名'
        //...argv
      },
      name: 'username',
      initialvalue: '',
      rules: [],
      children: <input />
      //...argv
    },
    {
      itemoptions : {
        label: '密码'
      },
      name: 'password',
      initialvalue: '',
      rules: [],
      children: <input />,
      ishidden: true //隐藏此项 默认是显示
    },
    {
      itemoptions : {
        label: '密码'
      },
      name: 'password',
      initialvalue: '',
      rules: [],
      children: <input />,
      ishidden: () =>  false //通过方法来动态显示隐藏
    },
  ]
}

支持antd form 所有的api

配置文件为什么是使用函数的形式?因为可以通过函数的参数,实现配置文件和页面之间进行数据的传递。

在页面就这样使用,代码如下

import react from 'react';
import { form, input } from 'antd';
import { formconfig } from './config.js';
import baseitem from './baseitem';
import itemlayout from './itemlayout';

@form.create()
class mytestform extends react.component {
  render() {
    const { form } = this.props;
    return(
      <form>
        {
          formconfig().map((item, i) => 
             <itemlayout><baseitem key={i} config={item} form={form}/></itemlayout>)
        }
      </form>
    )
  }
}

export default mytestform;

相比传统搭建form是不是快捷了很多,而且页面代码层面更显得更清晰。

注意:

假如是想使用自定义的组件,(一个个性化的业务组件),简单点,我对input的封装

//自动trim的input
import { component } from 'react';
import { input } from 'antd';

class triminput extends component {
  handlechange = (e) => {
    e.target.value = e.target.value.trim();
    this.props.onchange(e.target.value); //input chang 后将值传递给props
  }

  render() {
    const { value, ...argv } = this.props;
    return(
      <input
        value={ value } //将props的填在input中
        { ...argv }
        onchange={this.handlechange}/>
    )
  }
}

export default triminput;

自定义的业务组件需要具备双向数据流的能力,最重要的一点是在更新的时候需要 调用this.props.onchange(data)

搜索form包装(searchform

假如觉得这还不够过瘾,那么一起来基于baseitem来再包装业务组件吧。相信每个后台都有搜索能力吧,那么我们就包装一个搜索的searchform

主要就是增加一个search功能并把form的值传递出去。

主要代码如下:

//searchform.js
import react from 'react';
import { form, button } from 'antd';

import baseitem from './baseitem';
import itemlayout from './itemlayout';

@form.create()
class searchform extends react.component {

  handlesearch = () => {
    const { form: { validatefields }, search } = this.props;
    validatefields((err, fieldsvalue) => {
      console.log(fieldsvalue);
      if(!err) {
        search && search(fieldsvalue);
      }
    })
  }

  render() {
    const { form, searchconfig, search, form: { resetfields } } = this.props;
    return(
      <>
        <form onsubmit={this.handlesearch}>
          {
            searchconfig().map((item, i) => 
               <itemlayout><baseitem key={i} config={item} form={form}/></itemlayout>)
          }
          {
            search && <div>
              <button htmltype="submit" type="primary" style={{marginright: '20px'}}>
                  搜索
              </button>
              <button onclick={() => resetfields()}>重置</button>
            </div>
          }
        </form>
      </>
    )
  }
}

export default searchform;

页面里面使用,表单项还是抽成配置文件使用:

//config.js 查询条件
export const searchconfig = () => {
  return [
    {
      itemoptions : {
        label: '条件一'
      },
      name: 'name1',
      initialvalue: '',
      rules: [],
      children: <input />
    },
    {
      itemoptions : {
        label: '条件二'
      },
      name: 'name2',
      initialvalue: '',
      rules: [],
      children: <input />
    }
  ]
}
import react from 'react';
import { searchconfig } from './config';
import searchform from './searchform';

class mytestform extends react.component {
  handlesearch = value => {
    console.log(value);//获取到的查询条件
  }
  render() {
    return(
      <searchform searchconfig={searchconfig} search={this.handlesearch} />
    )
  }
}

export default mytestform;

这样子写查询表单是不是很快呀,以后遇到查询就引用这个组件,然后抽一个配置文件,这样就ok了。

还有也会经常遇到这种情况,弹窗里面的form,这样子就需要给弹窗增加收集数据的能力。相当于我们把searchform的组件放在 modal 里面。 具体实现代码就不贴了。

antd form 需要注意的几个问题。

  1. initialvalue 这个属性只是设置表单的初始值,当需要动态更改表单的值的时候,使用 setfieldsvalue

  2. resetfields这个属性是重置一组输入控件的值与状态,(将值重置为initialvalue , 而不是清空数据,需要清空数据还是使用setfieldsvalue

antd form 新的改动

antd form 将在第4个版本使用 rc-field-form, 但是还没有发布,我是在4.0-prepare分支上看到。

那么两个底层组件 有什么区别呢?

首先rc-field-form 会尽量在api层面上保持一致,但是仍有地方做了改动。 主要是以下几点:

  • 当没有手动更新过表单的时候,将不会收集initialvalues 的值

    rc-form里面,如果用户没有操作过表单,将会从form的initialvalues 收集值。他们认为这是一个bug,但是好多用户是用了这个,所以他们不做修复。在rc-field-form中,将不会有这个bug。如果想改变组件的值,使用 setfieldsvalue 代替。

  • 嵌套的name使用数组代替字符串

    rc-form里面支持user.name,最终会被解释成为{user:{ name: '' } }

    rc-field-form将是['user', 'name'] 解释成为 {user: { name: '' }} 并且会把 user.name解释成为{ ['user.name']: ''}

  • 删除validatefieldsandscroll这个属性

    是因为使用了finddomnode,但是finddomnodestrictmode中被标记为警告。认为这是对表单组件的过度控制。

  • getfieldserror 将总是返回来数组

    rc-form 当没有错的时候,返回的是null,rc-field-form现在返回的是一个空数组

  • 删除了validatefields的callback函数

    是因为es8支持async/await,没有理由不使用它。我们使用的时候应该是

    async function() {
      try {
        const values = await form.validatefields();
        console.log(values);
      } catch (errorlist) {
        errorlist.foreach(({ name, errors }) => {
          // do something...
        });
      }
    }
  • setfields将不触发onfieldschangesetfieldsvalue不触发onvalueschange

    总结

写这篇文章主要是自己做后台最form的总结,还有是为大家提供一种思路,后台快速开发的方式。

后面还会更新其它antd 组件,主要是如何开发更适合业务场景的组件。

antd快速开发(Form篇)