使用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 相关文章推荐
可以用来调试JavaScript错误的解决方案
Aug 07 Javascript
filters.revealTrans.Transition使用方法小结
Aug 19 Javascript
javascript判断是手机还是电脑访问网页的简单实例分享
Jun 03 Javascript
jQuery带箭头提示框tooltips插件集锦
Nov 17 Javascript
利用vue写todolist单页应用
Dec 15 Javascript
JS中使用gulp实现压缩文件及浏览器热加载功能
Jul 12 Javascript
微信小程序的日期选择器的实例详解
Sep 29 Javascript
JavaScript原型对象、构造函数和实例对象功能与用法详解
Aug 04 Javascript
TypeScript之调用栈的实现
Dec 31 Javascript
Element Rate 评分的使用方法
Jul 27 Javascript
Vue 同步异步存值取值实现案例
Aug 05 Javascript
Vue实现小购物车功能
Dec 21 Vue.js
基于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制作静态网站的模板框架(三)
2006/10/09 PHP
mysq GBKl乱码
2006/11/28 PHP
PHP array_flip() 删除重复数组元素专用函数
2010/05/16 PHP
仿Aspnetpager的一个PHP分页类代码 附源码下载
2012/10/08 PHP
JQuery select标签操作代码段
2010/05/16 Javascript
JS 控制小数位数的实现代码
2011/08/02 Javascript
JavaScript使用yield模拟多线程的方法
2015/03/19 Javascript
jquery实现的3D旋转木马特效代码分享
2015/08/25 Javascript
js显示当前日期时间和星期几
2015/10/22 Javascript
AngularJS单选框及多选框实现双向动态绑定
2016/01/13 Javascript
浅析JavaScript 箭头函数 generator Date JSON
2016/05/23 Javascript
前端js弹出框组件使用方法
2020/08/24 Javascript
JS日程管理插件FullCalendar简单实例
2017/02/07 Javascript
vue 请求后台数据的实例代码
2017/06/22 Javascript
使用 jQuery 实现表单验证功能
2017/07/05 jQuery
layer.open 按钮的点击事件关闭方法
2018/08/17 Javascript
node.js的Express服务器基本使用教程
2019/01/09 Javascript
Vuepress 搭建带评论功能的静态博客的实现
2019/02/17 Javascript
nodejs微信开发之自动回复的实现
2019/03/17 NodeJs
node.js 基于cheerio的爬虫工具的实现(需要登录权限的爬虫工具)
2019/04/10 Javascript
小程序数据通信方法大全(推荐)
2019/04/15 Javascript
node.js实现上传文件功能
2019/07/15 Javascript
Nodejs监控事件循环异常示例详解
2019/09/22 NodeJs
vuex实现像调用模板方法一样调用Mutations方法
2019/11/06 Javascript
javascript 函数的暂停和恢复实例详解
2020/04/25 Javascript
Flask框架的学习指南之制作简单blog系统
2016/11/20 Python
Python中BeautifuSoup库的用法使用详解
2019/11/15 Python
Python中的四种交换数值的方法解析
2019/11/18 Python
检测浏览器对HTML5和CSS3支持度的方法
2015/06/25 HTML / CSS
JACK & JONES瑞典官方网站:杰克琼斯欧式风格男装
2017/12/23 全球购物
离婚财产处理协议书
2014/09/30 职场文书
2014年幼儿园工作总结
2014/11/10 职场文书
挂职锻炼工作总结2015
2015/05/28 职场文书
老干部局2015年度工作总结
2015/10/22 职场文书
《所见》教学反思
2016/02/23 职场文书
Meta增速拉垮,元宇宙难当重任
2022/04/29 数码科技