前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Form 表单在数栈的应用(上): 校验篇

Form 表单在数栈的应用(上): 校验篇

作者头像
袋鼠云数栈
发布2022-04-13 21:10:59
1.3K0
发布2022-04-13 21:10:59
举报
文章被收录于专栏:数栈技术分享

一、引言

本文为主题即为 Form 表单在数栈的应用,旨在希望能通过一些在数栈已经应用的实例和笔者整理的小tips来帮助大家更深入的理解Form表单校验、以及联动校验的认知和做法。

本文的重点为 Form 表单的校验及在数栈中的应用,偏向于应用总结与心得分享。众所周知,我们生在一个最好的时代,antd 已经帮我们把绝大多数功能封装好了,即开即用, API 详尽,但即便如此,antd 开发人员依然在当前基础上一遍又一遍地做优化和探索,所以,笔者希望通过本文不仅能带给大家业务上的小技巧,还希望能带给大家一些思路上的启发。

关于 Form 表单的其他内容,在 Form 表单在数栈的应用(下) 会和大家见面。

二、什么是 Form

相信大家在日常开发中已经对 Form 表单的使用已非常精通了,但非常值得一提的是,可能大家对“Form的定义是什么?什么时候我们会选择用 form ?”这个问题却往往回答不好。

定义:

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

用法:

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

三、表单域校验(FormItem)

首先来看看 antd 提供的两种基础表单域校验方式

代码语言:javascript
复制
/* 
 *** 「声明式」表单校验 ***
*/

<Form.Item
  {...formItemLayout}
  label="密码"
  hasFeedback
  validateStatus={validateStatus}
  help={help}
>
	{getFieldDecorator('input', {
	})(<Input onChange={(e) => { this.handleClickOne(e)}} placeholder="请输入值"/>)
	}
</Form.Item>
代码语言:javascript
复制
/* 
 *** 「自定义式」表单校验 ***
*/

<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 会逐条执行;代码简化也是显然可以,但可能是出于习惯问题,笔者看了一些项目的业务代码其实风格都和上述代码无异,经改造后代码如下图:

代码语言:javascript
复制
<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,数组里是指定表单域的绑定名。

代码语言:javascript
复制
form.validateFields(['xxx'],(err,values) => {
	if(err) return
  // 进行values相关操作
  ...
})

3. 增加 options 校验以及 options 在数栈的适用场景

在操作的时候对域值正确性进行校验,可根据需求增加校验规则。API 回顾如下:

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

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

代码语言:javascript
复制
	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 传送门

代码语言:javascript
复制
/*
 * 核心代码思想如下
*/

class CustomizedForm extends React.Component { ... }
// use wrappedComponentRef
const CustomForm =  Form.create()(CustomizedForm);
<CustomForm wrappedComponentRef={(form) => this.form = form} />

此时的 this.form 就是 CustomizedForm 的实例

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

代码语言:javascript
复制
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 传递即可

代码语言:javascript
复制
<Component
    ref={(ref) => { this.props.onRef(ref) }}
    ... other params ...
/>

思考:观察我们的校验代码,是否存在优化方案呢?

代码语言:javascript
复制
// 校验逻辑 
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 进行校验,那么请问该如何实现让他们同时进行校验,以完成代码和校验交互上的优化呢?

代码语言:javascript
复制
笔者思路 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 演示)

两种方法实现方式可能不同,但归根结底的校验核心都是一样的,笔者这里用思路二进行分解:

代码语言:javascript
复制
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 进行探究:

首先对于「声明式」校验有个改变:

代码语言:javascript
复制
<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,是不是和上面的思考是一样的?

代码语言:javascript
复制
mockSubmit = () => {
    form.validateFields().then((values)=>{
      // to do here 
    }).catch((errInfo)=>{  
      // 如果有未经过校验的表单域,会走catch,里面可以打印所有校验失败的信息
      console.log('失败')
    console.log(errInfo)
    })
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/01/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、引言
  • 二、什么是 Form
  • 三、表单域校验(FormItem)
  • 四、表单值校验(ValidateFields)
  • 五、思考与总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档