前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实现一个简单的表单校验器

实现一个简单的表单校验器

作者头像
IMWeb前端团队
发布2019-12-03 17:01:06
9820
发布2019-12-03 17:01:06
举报
文章被收录于专栏:IMWeb前端团队

本文作者:IMWeb chenxd1996 原文出处:IMWeb社区 未经同意,禁止转载

实现一个简单的表单校验器

1. 问题提出:

最近笔者在用React+antd做管理后台系统需求的时候,碰到了一个问题,就是在同一个antd的FormItem下面有多个子数据,那么在表单校验的时候某个数据一旦出错,整个FormItem下面的表单组件都会标红,无法准确标出出错的字段。

如图所示,这里的表格数据,其实都是同一个数据字段的子字段。可以看到,即使只有第一个input框校验出错,也会出现一个大红框,出错信息也是显示在整个表格下方,很难看到具体出错的位置。

我们的目标效果应该是这样的:

2. 解决方法:

Form表单下面是不能嵌套Form表单的,所以笔者试着自己写了一个简单的表单校验器。虽然有点简陋,但感觉也还有点意思,与大家分享一下。

首先能想到的是模仿getFieldDecorator,提供一个函数getField,调用getField(option)(formComponent)得到一个包装过的Component,在原来表单组件上加入错误信息显示。

例如:

代码语言:javascript
复制
getField({
    field: 'name', // field相当于是该字段的id,支持类似'userInfo.name', 'users[0].userInfo.name'
    validator: (value, values, callback) {
        // value为该字段的值
        // values为该字段的父字段的值。如果value是userInfo.name的值, 那么values就是userInfo
        // callback()时候为校验成功,callback('some error msg') 为检验失败
    }
})

好了,上面就是实现目标了,接下来开始一步步实现。

首先,肯定是要有一个容器用来存放校验器的,getField这个方法就是为了存放校验器,这个容器还要暴露出一个validate方法,这个方法一旦被调用,所有的校验器就都被调用,如果出错就会显示错误信息。

这个容器可以用class来实现,其大概内容应该如下:

代码语言:javascript
复制
class MyValidation {
  toValidate = {}; // 用来存放校验器的,key是field,value是validator

  constructor(context) {
    // context是表单所在Component,用来更新视图用的
    this.context = context
  }
  add(field, validator) { // 用来添加校验器的
  }

  @autobind
  getField(options = {}) {
   // 调用add函数保存校验器,并返回一个包装过element
   // 包装element也叫高阶组件,目的为了在原有组件下面显示出错信息
  }

  @autobind
  validate(values) {
    // 用来触发校验,values是表单数据      
  }

  @autobind
  clearFields() {
    // 用来清除校验信息
  }
}

看到这里,聪明的你也许已经看穿了一切了,如果有兴趣,可以照着这个思路,自己把细节实现一下。

接下来,我们将继续将探究一下每个函数的实现细节。

add

这个函数非常简单,如下:

代码语言:javascript
复制
   add(field, validator) { // 用来添加校验器的
       this.toValidate[field] = {
         validator,
       };    
   }

getField

这个函数略有些复杂,需要对React高阶组件有一定的了解

代码语言:javascript
复制
   getField(options = {}) {
      // 调用add函数保存校验器,并返回一个包装过element
      // 包装element也叫高阶组件,目的为了在原有组件下面显示出错信息
       const { field, validator } = options;
       this.add(field, validator); // 保存校验器
       return (Cmp) => { // Cmp就是将被包装的表单组件,例如<Input />
         return function (props) { // 函数式组件,因为不需要state
           Cmp.props = { ...Cmp.props, ...props }; // 将props传给Cmp
           const msg = errorRecord[field]; // errorRecord是全局变量,存放错误信息,看一下是否有错误信息
           return (
             <div className={msg ? 'has-error' : null}>
               {Cmp}
               {
                 <div className="ant-form-explain" style={errorMsgStyle}>
                   <span>{msg}</span>
                 </div>
                 }
             </div>
           );
         };
       };
   }

这里需要说明的是,errorRecord是个全局变量,这里不是通过高阶组件的setState来更新视图的,后面会讲到校验后如何触发视图更新。这里我没有自己写的样式,是直接用的antd表单校验的样式。

validate

代码语言:javascript
复制
   validate(values) {
       // 用来触发校验,values是表单数据
       // context是表单所在Component,用来更新视图用的
       const fields = Object.keys(this.toValidate);
       let isValid = true;
       fields.forEach((field) => {
         const { validator } = this.toValidate[field];
         const value = _.get(values, field); // 这里的_是lodash库,用它的get方法,获取对象的值,非常强大
         const callbackCreator = (field) => {
            return (msg) => { // 这里用了一个闭包,生成field的对应callback
              errorRecord[field] = msg;
            };
         };
         if (validator) {
           validator(value, values, callbackCreator(field));
         }
         const msg = errorRecord[field];
         if (msg) { // 如果有msg,说明出错了
           isValid = false;
         }
       });
       this.context.forceUpdate(); // 这里要更新下视图
       return isValid;     
   }

clearFields

代码语言:javascript
复制
   clearFields() {
      // 用来清除校验信息
      errorRecord = {};
      this.context.forceUpdate();
   }

大工告成,接下来看看如何使用吧,使用方法大概如下:

代码语言:javascript
复制
const validatation = new myValidation(this);
const { getField } = validation;

function () {

}

class TestCmp extends React.Component {
    getFields = () => {
        const Wrapper = getField({
            field: 'name',
            validator: (value, values, callback) => {
                if (!value || value === '') {
                    callback('名字不能为空');
                    return;
                }
                callback();
            }
        })(<Input />);
        return <Wrapper />
    }
    submit = () => {
        const isValid = validation.validate(this.props.form.getFieldsValue());
        if (!isValid) {
            return;        
        }
        // submit form data
    }
    render() {
        return (
            <Form>
                <FormItem>
                {
                this.getFields();
                }
                </FormItem>
            </Form>
            <Button onClick={this.submit}>
        )
    }
}

3. 总结

有时候简单的代码写多了感觉会比较平淡,遇到一些稍有难度问题,反而能让你学到更多的东西。笔者学习React不足两个月,可能在很多方面还是理解得不够到位,如有纰漏,欢迎读者批评指正,谢谢!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-07-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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