使用async-validator编写Form组件的方法


Posted in Javascript onJanuary 10, 2018

前端开发中,表单的校验一个很常见的功能,一些 ui 库例如ant.design 与Element ui 都实现了有校验功能的 Form 组件。async-validator 是一个可以对数据进行异步校验的库,ant.design 与 Element ui 的 Form 组件都使用了 async-validator。本文就简单介绍一下 async-validator 的基本用法以及使用该库实现一个简单的有校验功能的 Form 组件。

1. async-validator 的基本用法

async-validator 的功能是校验数据是否合法,并且根据校验规则给出提示信息。

下面演示一下 async-validator 的最基本用法。

import AsyncValidator from 'async-validator'
// 校验规则
const descriptor = {
 username: [
 {
  required: true,
  message: '请填写用户名'
 },
 {
  min: 3,
  max: 10,
  message: '用户名长度为3-10'
 }
 ]
}
// 根据校验规则构造一个 validator
const validator = new AsyncValidator(descriptor)
const data = {
 username: 'username'
}
validator.validate(model, (errors, fields) => {
 console.log(errors)
})

当数据不符合校验规则时,在 validator.validate 的回调函数中,就可以得到相应的错误信息。

当 async-validator 中常见的校验规则无法满足需求时,我们可以编写自定义的校验函数来校验数据。一个简单的校验函数如下。

function validateData (rule, value, callback) {
 let err
 if (value === 'xxxx') {
  err = '不符合规范'
 }
 callback(err)
}
const descriptor = {
 complex: [
  {
  validator: validateData
  }
 ]
}
const validator = new AsyncValidator(descriptor)

async-validator 支持对数据异步校验,所以在编写自定义校验函数时,不管校验是否通过,校验函数中的 callback 都要调用。

2. 编写 Form 组件与 FormItem 组件

现在知道了 async-validator 的使用方法,如何将这个库跟要编写的 Form 组件结合起来呢。

实现思路

用一张图描述一下实现思路。

使用async-validator编写Form组件的方法

Form 组件

Form 组件应该是一个容器,里面包含不定数量的 FormItem 或者其他元素。可以使用 Vue 内置的slot 组件来代表 Form 里面的内容。

Form 组件还需要知道包含了多少个需要校验的 FormItem 组件。一般情况下,父子组件的通信 是通过在子组件上绑定事件实现的,但是这里使用 slot,无法监听到子组件的事件。这里可以在 Form 组件上通过$on 监听事件,FormItem 挂载或者销毁前触发 Form 组件的自定义事件即可。

按照这个思路,我们先编写 Form 组件。

<template>
 <form class="v-form">
  <slot></slot>
 </form> 
</template>
<script>
import AsyncValidator from 'async-validator'
export default {
 name: 'v-form',
 componentName: 'VForm', // 通过 $options.componentName 来找 form 组件
 data () {
  return {
   fields: [], // field: {prop, el},保存 FormItem 的信息。
   formError: {}
  }
 },
 computed: {
  formRules () {
   const descriptor = {}
   this.fields.forEach(({prop}) => {
    if (!Array.isArray(this.rules[prop])) {
     console.warn(`prop 为 ${prop} 的 FormItem 校验规则不存在或者其值不是数组`)
     descriptor[prop] = [{ required: true }]
     return
    }
    descriptor[prop] = this.rules[prop]
   })
   return descriptor
  },
  formValues () {
   return this.fields.reduce((data, {prop}) => {
    data[prop] = this.model[prop]
    return data
   }, {})
  }
 },
 methods: {
  validate (callback) {
   const validator = new AsyncValidator(this.formRules)
   validator.validate(this.formValues, (errors) => {
    let formError = {}
    if (errors && errors.length) {
     errors.forEach(({message, field}) => {
      formError[field] = message
     })
    } else {
     formError = {}
    }
    this.formError = formError
    // 让错误信息的顺序与表单组件的顺序相同
    const errInfo = []
    this.fields.forEach(({prop, el}, index) => {
     if (formError[prop]) {
      errInfo.push(formError[prop])
     }
    })
    callback(errInfo)
   })
  }
 },
 props: {
  model: Object,
  rules: Object
 },
 created () {
  this.$on('form.addField', (field) => {
   if (field) {
    this.fields = [...this.fields, field]
   }
  })
  this.$on('form.removeField', (field) => {
   if (field) {
    this.fields = this.fields.filter(({prop}) => prop !== field.prop)
   }
  })
 }
}
</script>

FormItem 组件

FormItem 组件就简单很多,首先要向上找到包含它的 Form 组件。接下来就可以根据 formError 计算出对应的错误信息。

<template>
 <div class="form-item">
  <label :for="prop" class="form-item-label" v-if="label">
   {{ label }}
  </label>
  <div class="form-item-content">
   <slot></slot>
  </div>
 </div>
</template>
<script>
export default {
 name: 'form-item',
 computed: {
  form () {
   let parent = this.$parent
   while (parent.$options.componentName !== 'VForm') {
    parent = parent.$parent
   }
   return parent
  },
  fieldError () {
   if (!this.prop) {
    return ''
   }
   const formError = this.form.formError
   return formError[this.prop] || ''
  }
 },
 props: {
  prop: String,
  label: String
 }
}
</script>

FormItem 在 mounted 与 beforeDestroy 钩子中还需要触发 Form 组件的一些自定义事件。

<script>
export default {
 // ...
 methods: {
  dispatchEvent (eventName, params) {
   if (typeof this.form !== 'object' && !this.form.$emit) {
    console.error('FormItem必须在Form组件内')
    return
   }
   this.form.$emit(eventName, params)
  }
 },
 mounted () {
  if (this.prop) {
   this.dispatchEvent('form.addField', {
    prop: this.prop,
    el: this.$el
   })
  }
 },
 beforeDestroy () {
  if (this.prop) {
   this.dispatchEvent('form.removeField', {
    prop: this.prop
   })
  }
 }
}
</script>

最后新建一个 index.js 导出编写好的组件。

import VForm from './Form.vue'
import FormItem from './FormItem.vue'

export {
 VForm,
 FormItem
}

3. 使用方式

表单的校验函数是在 Form 组件中。通过$ref 可以访问到 Form 组件,调用 validate 函数,从而获取到相应的校验信息。

使用方法如下:

<template>
 <v-form :model="formData" :rules="rules" ref="form">
  <form-item label="手机号" prop="tel">
   <input type="tel" maxlength="11" v-model.trim="formData.tel"/>
  </form-item>
  <button @click="handleSubmit">保存</button>
 </v-form>
</template>
<script>
 import { VForm, FormItem } from './common/Form'
 export default {
  data () {
   return {
    formData: {
     tel: ''
    },
    rules: {
     tel: [
      {required: true, message: '您的手机号码未输入'},
      {pattern: /^1[34578]\d{9}$/, message: '您的手机号码输入错误'}
     ]
    }
   }
  },
  methods: {
   handleSubmit () {
    this.$refs.form.validate(errs => {
     console.log(errs)
    })
   }
  },
  components: {
   VForm,
   FormItem
  }
 }
</script>

完整的代码点击这里。

4. 总结

本文简单介绍了一下 async-validator 的用法,并实现了一个有校验功能的 Form 组件。这里的实现的 Form 表单存在着很多的不足:(1) 仅仅是在提交表单时才去校验。(2) FormItem 组件也应该根据校验的结果调整 ui,给出相应的提示。所以,Form 组件更适合在交互较少的移动端使用。

大家可以根据这个实现思路,根据应用场景,编写定制化更高的 Form 组件。

参考资料

async-validator
Element ui 的 Form组件
Element ui 的 Form 源码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
分享一个asp.net pager分页控件
Jan 04 Javascript
js onkeypress与onkeydown 事件区别详细说明
Dec 13 Javascript
关于删除时的提示处理(确定删除吗)
Nov 03 Javascript
jQuery实现防止提交按钮被双击的方法
Mar 24 Javascript
原生js和jquery分别实现横向导航菜单效果
May 13 Javascript
jQuery获取单击节点对象的方法
Jun 02 Javascript
BootStrap智能表单实战系列(四)表单布局介绍
Jun 13 Javascript
jQuery简单实现遍历单选框的方法
Mar 06 Javascript
jQuery实现打开网页自动弹出遮罩层或点击弹出遮罩层功能示例
Oct 19 jQuery
react build 后打包发布总结
Aug 24 Javascript
vant(ZanUi)结合async-validator实现表单验证的方法
Dec 06 Javascript
微信小程序开发数据缓存基础知识辨析及运用实例详解
Nov 06 Javascript
基于casperjs和resemble.js实现一个像素对比服务详解
Jan 10 #Javascript
JavaScript实现快速排序的方法分析
Jan 10 #Javascript
jQuery第一次运行页面默认触发点击事件的实例
Jan 10 #jQuery
js推箱子小游戏步骤代码解析
Jan 10 #Javascript
vue select二级联动第二级默认选中第一个option值的实例
Jan 10 #Javascript
AngularJS使用ui-route实现多层嵌套路由的示例
Jan 10 #Javascript
Vue+jquery实现表格指定列的文字收缩的示例代码
Jan 09 #jQuery
You might like
用PHP实现图象锐化代码
2007/06/14 PHP
PHP迅雷、快车、旋风下载专用链转换代码
2010/06/15 PHP
php防攻击代码升级版
2010/12/29 PHP
基于PHP5魔术常量与魔术方法的详解
2013/06/13 PHP
Web程序工作原理详解
2014/12/25 PHP
详解WordPress中提醒安装插件以及隐藏插件的功能实现
2015/12/25 PHP
niceTitle 基于jquery的超链接提示插件
2010/05/31 Javascript
js获取元素外链样式的方法
2015/01/27 Javascript
JavaScript类型系统之基本数据类型与包装类型
2016/01/06 Javascript
JS转换HTML转义符的方法
2016/08/24 Javascript
学习vue.js条件渲染
2016/12/03 Javascript
利用express启动一个server服务的方法
2017/09/17 Javascript
分析JS中this引发的bug
2017/12/12 Javascript
vue项目实现github在线预览功能
2018/06/20 Javascript
vue导出html、word和pdf的实现代码
2018/07/31 Javascript
angular2 组件之间通过service互相传递的实例
2018/09/30 Javascript
Vuex实现数据增加和删除功能
2019/11/11 Javascript
vue实现短信验证码登录功能(流程详解)
2019/12/10 Javascript
在vue项目中利用popstate处理页面返回的操作介绍
2020/08/06 Javascript
JavaScript Array.flat()函数用法解析
2020/09/02 Javascript
微信小程序实现多张图片上传功能
2020/11/18 Javascript
Python多进程原理与用法分析
2018/08/21 Python
python 实现将文件或文件夹用相对路径打包为 tar.gz 文件的方法
2019/06/10 Python
python3使用腾讯企业邮箱发送邮件的实例
2019/06/28 Python
使用Python的Turtle库绘制森林的实例
2019/12/18 Python
使用pygame编写Flappy bird小游戏
2020/03/14 Python
Pandas中DataFrame交换列顺序的方法实现
2020/12/14 Python
奥地利网上现代灯具和灯饰店:Lampenwelt.at
2018/01/29 全球购物
HelloFresh奥地利:立即订购烹饪盒
2019/02/22 全球购物
法国最大的在线眼镜店:EasyLunettes
2019/08/26 全球购物
2013年员工自我评价范文
2013/12/27 职场文书
大学军训感想
2014/02/12 职场文书
消防志愿者活动方案
2014/08/23 职场文书
花木兰观后感
2015/06/10 职场文书
nginx如何将http访问的网站改成https访问
2021/03/31 Servers
如何使用Python提取Chrome浏览器保存的密码
2021/06/09 Python