首页 > 编程 > JavaScript > 正文

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

2019-11-19 14:33:17
字体:
来源:转载
供稿:网友

前端开发中,表单的校验一个很常见的功能,一些 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' } ]}// 根据校验规则构造一个 validatorconst 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 组件结合起来呢。

实现思路

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

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 源码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表