一、引言

本文为主题即为 Form 表单在数栈的利用,旨在心愿能通过一些在数栈曾经利用的实例和笔者整顿的小tips来帮忙大家更深刻的了解Form表单校验、以及联动校验的认知和做法。

本文的重点为 Form 表单的校验及在数栈中的利用,偏差于利用总结与心得分享。家喻户晓,咱们生在一个最好的时代,antd 曾经帮咱们把绝大多数性能封装好了,即开即用, API 详尽,但即便如此,antd 开发人员仍然在以后根底上一遍又一遍地做优化和摸索,所以,笔者心愿通过本文不仅能带给大家业务上的小技巧,还心愿能带给大家一些思路上的启发。

对于 Form 表单的其余内容,在 Form 表单在数栈的利用(下) 会和大家见面。

二、什么是 Form

置信大家在日常开发中曾经对 Form 表单的应用已十分精通了,但十分值得一提的是,可能大家对“Form的定义是什么?什么时候咱们会抉择用 form ?”这个问题却往往答复不好。

定义:

  • 具备 数据收集、校验 和 提交 性能的表单,蕴含复选框、单选框、输入框、下拉抉择框等元素;

用法:

  • 当咱们用于创立一个实体或收集信息、或须要对输出的数据类型进行校验时,可用Form表单。

三、表单域校验(FormItem)

首先来看看 antd 提供的两种根底表单域校验形式

/*  *** 「申明式」表单校验 ****/<Form.Item  {...formItemLayout}  label="明码"  hasFeedback  validateStatus={validateStatus}  help={help}>    {getFieldDecorator('input', {    })(<Input onChange={(e) => { this.handleClickOne(e)}} placeholder="请输出值"/>)    }</Form.Item>
/*  *** 「自定义式」表单校验 ****/<Form.Item  {...formItemLayout}  label="明码"  hasFeedback>    {getFieldDecorator('input_controlled', {      validateTrigger: 'onBlur',      rules: [{          required: true,          message: '请输出明码!'      }, {          min: 5,          message: '明码长度至多大于5!'      }, {          validator: this.checkInputValue      }]  })(      <Input placeholder="请输出银行卡明码" />  )}</Form.Item>checkInputValue = (rule, value, callback) => {    if (!value) return callback('请输出明码!')    if (value.length <= 2) {        callback('长度至多大于2')    }    if (value !== '222') {        callback('明码不对')    }    callback()}

请思考: 在上述自定义校验中可优化的点?

是否不依照官网 callBack(new Error('sting')) 而间接写 callBack('string') 不正规呢?实则不然:在 rc-field-form 其实明确指出,参数 error 的类型实际上就是 sting,当然了,如果写成 new Error 或者看起来更优雅,但间接写 string 也没错;

rules 里既然定义了自定义的 validator ,那是否能够简化成只写一个呢? rules 外面定义的多条规定,是否执行失败了一条就不再接着执行了呢?会不会呈现 rules 的提醒语反复的问题呢?
首先,在 rules 里定义的 rule 会逐条执行;代码简化也是显然能够,但可能是出于习惯问题,笔者看了一些我的项目的业务代码其实格调都和上述代码无异,经革新后代码如下图:

<Form.Item  {...formItemLayout}  label="明码"  hasFeedback  required>    {getFieldDecorator('input_controlled', {      validateTrigger: 'onBlur',      rules: [{          validator: this.checkInputValue      }]  })(      <Input placeholder="请输出银行卡明码" />  )}</Form.Item>checkInputValue = (rule, value, callback) => {    if (!value) return callback(new Error('明码不可为空!'))    if (value.length <= 5) {        return callback('长度至多大于2')    }    if (value !== '222') {        return callback('明码不对')    }    .........    // 接口校验等等    callback()}

不难看出,经革新后咱们能一眼看出所需的校验规定,有利于前期排查,在 rules 过多的时候也不会高低来回看。

四、表单值校验(ValidateFields)

说完了 FormItem,当初转头来看看 ValidateFields,两个例子demo演示

下图是antd对于validataFields的用法介绍,温习一下,不做赘述

1. 校验表单值所有字段

这是在数栈用的比拟高频的,个别在提交表单的数据时,先对以后所有表单域进行校验,只有全副通过校验能力进行下一步操作。(调接口、联动等操作)

2. 校验指定表单域

特定工夫点对于指定表单域的正确性校验,如果指定表单域通过校验方可进行下一步操作。这里会用到 validateFields 的第一个参数 fileNames,数组里是指定表单域的绑定名。

form.validateFields(['xxx'],(err,values) => {    if(err) return  // 进行values相干操作  ...})

3. 减少 options 校验以及 options 在数栈的实用场景

在操作的时候对域值正确性进行校验,可依据需要减少校验规定。API 回顾如下:

示例场景1: (标签引擎我的项目)

场景形容:表单在第一次自定义校验时失败报错,经排查为在校验的时刻账号权限有余,此时咱们给予该用户应该具备的权限(此时弹窗未敞开),再次点击确定发现并无成果。
问题剖析:从 antd 的应用角度来讲,有域值谬误时不该当再持续进行后续操作,但存在这种极限状况,问题是因为自定义校验后果产生,而此自定义校验是存在时效性的,所以此时咱们应该让自定义校验具备准确性,应用 options 参数,设置 force 为 true,让每一次“提交”(校验值)操作的之前都必须重走一遍所有校验逻辑。(参考演示demo)

handleOk = () => {this.props.form.validateFields({ force: true }, (err, values) => {    if (err) return    // this.props.handleOk(values)});

};

4. 多表单的联结校验

场景形容:不同业务中都会有很多相似状况会呈现——在一个页面里具备多个 form 表单,那么此时 form 表单的校验应该如何解决呢?(下图为标签业务中的一个历史性能,代码暂略)

问题剖析:因为我的项目历史起因,咱们抛开设计问题暂不表,间接探讨校验计划。首先是存在多层 form 嵌套的问题,也就是说同一个页面里可能还蕴含或嵌入多个 form 表单,相似问题的外围就在于如何在一个页面中拿到以后容器的 form 实例和嵌套的 form 示例。

解决方案:通过 ref 或 应用 rc-form 提供的 wrappedComponentRef 传送门

/* * 外围代码思维如下*/class CustomizedForm extends React.Component { ... }// use wrappedComponentRefconst CustomForm =  Form.create()(CustomizedForm);<CustomForm wrappedComponentRef={(form) => this.form = form} />此时的 this.form 就是 CustomizedForm 的实例

上局部业务代码 (参考笔者演示 Demo 代码详情链接地址:demo地址)

class RowLevelConf extends React.Component<IProps, IState> {  constructor(props) {        super(props);    }    state: IState = {...}    basicForm: any;    levelForm: any;        ... ...         // 校验逻辑     create = () => {        const { form } = this.props;        // 调用规定组件本身的 validate        this.levelForm.validateFields((err, values) => {            if(err) return            /**             *** ... 业务解决逻辑 ...            **/             form.validateFields(async (err, values) => {                if (err) return                /**                 *** ... 业务解决逻辑 ...                **/            })        })    }      ... ...   render() {    ... ...    return (    ... ...    <Form>        <LevelInfo           isCreate={isCreate}           form={form}           ruleData={ruleData}           onRef={(formRef) => this.levelForm = formRef}         />        <CommTitle           titleName='关联用户'           className='comm-title__margin'         />        <BasicInfoForm           isCreate={isCreate}           ruleData={ruleData}           form={form}           onRef={(formRef) => this.basicForm = formRef}         />    </Form>     ... ...     )  }  }

通过咱们自定义的 onRef 办法,在自组件层将 ref 传递即可

<Component    ref={(ref) => { this.props.onRef(ref) }}    ... other params .../>

思考:察看咱们的校验代码,是否存在优化计划呢?

// 校验逻辑 create = () => {    const { form } = this.props;    // 调用规定组件本身的 validate    this.levelForm.validateFields((err, values) => {        if(err) return        /**         *** ... 业务解决逻辑 ...        **/         form.validateFields(async (err, values) => {            if (err) return            /**             *** ... 业务解决逻辑 ...            **/        })    })}

尽管问题是解决了,的确从上到下执行了 form 的校验,但认真看代码其实是存在先后顺序的,相当于是先对 levelForm 进行了校验,胜利后再对下方的 form 进行校验,那么请问该如何实现让他们同时进行校验,以实现代码和校验交互上的优化呢?

笔者思路 tips:尽管 validateFields 并不会返回 promise,但会在 callback 办法内调用到放回的 errors 和 values,因而,咱们能够给它进行 promise 封装化。

5. table 与 form 的碰撞 (组件联动校验)

业务场景:数栈中其实存在各种与 form 联动的案例,笔者取 数据资产 (data-assets-front) 我的项目为例,在 table 中动静插入单条数据并实现可自定义校验内容:

失常思路是将 dataSource 中的每一项看作成一个 form 或 formItem,咱们将其抽离为一个相似最小构造,参考地址: 「form in table」

而后来剖析其校验形式:

通常思路可能会有两个:

1.用 antd table 新增的 components 属性来自定义列表元素,以笼罩默认的 table 元素,再在自定义列表元素中应用 form ;

2.将 table 的每一行元素都看作一个独立的表单域(formItem),再通过 form.validateFields 进行校验操作;(详请见 demo 演示)

两种办法实现形式可能不同,但归根结底的校验外围都是一样的,笔者这里用思路二进行合成:

const tableTitle = [    {        title: "ip地址",        dataIndex: "ip",        key: "ip",        width: 205,        render: (ip, record, index) => (            <Form.Item required>                {getFieldDecorator(`mapping[${index}].ip`, {                    initialValue: ip || "",                    validateFirst: true,                    validateTrigger: ["onChange", "onFocus"],                    rules: [                        {                            validator: this.syslogIPValidator                        }                    ]                })(                    <Input                        size="small"                        style={{ width: "170px" }}                        autoComplete="off"                        placeholder={'placeholder'}                    />                )}            </Form.Item>        )    },]... ...this.props.form.validateFields((err, values) => {    // to do sth})

五、思考与总结

本文列举了一些 antd form 3.x 在数栈的一些典型利用,梳理了一些小 tips 和 小 demo ,心愿对大家有所帮忙,至此,form 的校验形式大体曾经讲完,当然,如果有补充或者疑难欢送随时提出。

对于 antd 4.x 的 form 校验,这里也做了一个简略的总结,有趣味的同学能够移步 antd form 4.x 进行探索:

首先对于「申明式」校验有个扭转:

<Form.Item  {...formItemLayout}  name="username"  label="Name"  rules={[    {      required: true,      message: 'Please input your name',    },  ]}>  <Input placeholder="Please input your name" /></Form.Item>

其次是「自定义」校验,针对自定义校验,4.x做的细节改变其实很大,先看 API :

新增了 warningOnly,是不是很赞

validator 变成了 promise,是不是和下面的思考是一样的?

mockSubmit = () => {    form.validateFields().then((values)=>{      // to do here     }).catch((errInfo)=>{        // 如果有未通过校验的表单域,会走catch,外面能够打印所有校验失败的信息      console.log('失败')    console.log(errInfo)    })}