如何封装一个带表单验证的 Vue的表单插件(二):功能开发

接下来,我们开始完整的功能开发

目录结构

目录结构

整体目录结构如上图所示

  • src/components 项目的业务组件/页面
  • src/base form表单组件
  • src/mixins 混入相关的内容放在这个文件夹(dispatch、capture方法)

form表单之 input组件

首先我们先看看input.vue文件的内容:

<template>
  <input :type="type" :placeholder="placeholder" :value="currentValue" @input="inputChange">
</template>
<script>
import Emitter from '@/mixins/emitter'
export default {
  mixins: [Emitter],
  name: 'w-input',
  props: {
    value: {
      type: String,
      default: ''
    },
    placeholder: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      default: 'text'
    }
  },
  data () {
    return {
      currentValue: this.value
    }
  },
  methods: {
   inputBlur () {
      this.dispatch('form-item', 'on-input-blur', this.currentValue)
    },
    inputChange (e) {
      this.currentValue = e.target.value
      this.$emit('input', this.currentValue)
      // 如果单需要在输入事件input的时候就开始校验 增加下面语句
      this.dispatch('form-item', 'on-input-input', this.currentValue)
    }
  }
}

我们的稍作分析:

  • props我们接受三个属性 value(输入框的值)、 type(input的type值,可配password、email等)、placeholder(占位内容)
  • data里面定义一个currentValue,默认值为props中传入的value,之所以要定义这个值,是因为不能直接修改props中的值,所以输入框的value我们用currentValue填充
  • methods里面有两个事件 输入框的input事件(实时更新数据,添加表单校验的触发事件)和blur事件(添加表单校验的触发事件)
    this.$emit('input', this.currentValue) 这句代码是自定义组件使用v-model双向数据绑定的语法,不了解的小伙伴可以查看Vue的官方文档 自定义组件上面使用v-model 或者我自己写的 v-model详解

form表单之 form-item组件

接下来我们查看 form-item组件代码:

<template>
  <div class="form-item">
    <label :class="isRequired ? 'is_required' : ''">{{label}}:</label>
    <slot></slot>
    <p v-if="validateState === 'error'" class="error-text">{{validateMessage}}</p>
  </div>
</template>
<script>
import AsyncValidator from 'async-validator'
import Emitter from '@/mixins/emitter'
export default {
  mixins: [Emitter],
  name: 'form-item',
  props: {
    label: {
      type: String,
      default: ''
    },
    prop: {
      type: String,
      default: ''
    }
  },
  inject: ['form'],
  data () {
    return {
      currentValue: '',
      isRequired: false,
      validateState: '',
      ValidateMessage: ''
    }
  },
  mounted () {
    if (this.prop) {
      this.dispatch('w-form', 'on-form-item-add', this)
      this.currentValue = this.fieldValue
      this.isRequired = this.form.rules[this.prop].some(item => item.required)
      this.setInputListen()
    }
  },
  computed: {
    fieldValue () {
      return this.form.modes[this.prop]
    }
  },
  destroyed () {
    this.dispatch('w-form', 'on-form-item-delete', this)
  },
  methods: {
    /**
     * 添加监听input触发事件
     */
    setInputListen () {
    // 当前我们先监听blur事件,input事件可以自行添加
      this.$on('on-input-blur', this.listenBlur)
    },
    /**
     * input失去焦点回调
     */
    listenBlur (val) {
      this.validate('blur')
    },
    getRules () {
      const rules = this.form.rules[this.prop] || []
      return [].concat(rules)
    },
    getFilterRules (trigger) {
      let rules = this.getRules()
      // return rules.filter(rule => this.prop && rule.trigger === trigger)
      return rules.filter(
        // 这种比较方式的好处: trigger为空 或者匹配成功的情况都为 true
        rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1
      )
    },
    validate (trigger, callback = function () {}) {
      const rules = this.getFilterRules(trigger)
      if (!rules || !rules.length) {
        return true
      }

      this.validateState = 'validating'
      let descriptor = {}
      descriptor[this.prop] = rules
      const validator = new AsyncValidator(descriptor)
      let model = {}
      model[this.prop] = this.fieldValue
      validator.validate(model, { firstFields: true }, errors => {
        this.validateState = !errors ? 'success' : 'error'
        this.validateMessage = errors ? errors[0].message : ''

        callback(this.validateMessage)
      })
    },
    resetField () {
      this.validateState = ''
      this.validateMessage = ''
      this.form.modes[this.prop] = this.currentValue
    }
  }
}
</script>

form-item组件是表单验证的核心组件,功能相对来说比较复杂,我们慢慢看:

  1. props定义了 label(输入框前面的文字定义) prop(用于匹配rules规则和输入框值的属性名)。
  2. inject 接受form组件的实例(provide/inject),因为form-item需要使用用户在外面定义的校验规则和表单的值。
  3. data中定义了四个属性:- currentValue 当前表单元素的value值,在data中缓存;- isRequired 是否必填,用于控制必填的样式(可有可无);- validateState 表单的验证状态 验证失败为‘error’;- ValidateMessage 表单验证的错误信息,验证通过信息为空。
  4. 因为此表单验证支持一键全部校验,所以我们要将当前的form-item实例缓存到form组件中(mounted生命周期),以便于统一操作,然后再组件卸载的时候(destroyed)将组件卸载,所以我们使用 dispatch方法将其分别暴露给form监听。
  5. 当form-item加载成功之后,我们通过prop判断当前form-tem是否需要验证;为true时 继续往下执行,监听从input.vue中的事件this.$on('on-input-blur', this.listenBlur)。回调中再次调用表单验证方法 validate();
  6. validate方法接受两个参数,trigger(过滤符合校验条件的校验规则) callback(回调函数)。通过两个方法getRules() 、getFilterRules()方法获取当前form-item组件当前事件校验的rules。设置validateState校验状态为 validating ;然后调用async-validator的方法开始验证。如果errors存在,则将validateState赋值 error,validateMessage为报错的文字提示。

form表单之 form组件

form-item组件的功能需要结合form.vue才能更好的理解:

<template>
  <form>
    <slot></slot>
  </form>
</template>
<script>
export default {
  name: 'w-form',
  props: {
    rules: {
      type: Object,
      default: () => {}
    },
    modes: {
      type: Object,
      default: () => {}
    }
  },
  data () {
    return {
      fields: []
    }
  },
  provide () {
    return {
      form: this
    }
  },
  created () {
    this.$on('on-form-item-add', node => {
      if (node.prop) {
        this.fields.push(node)
      }
    })
    this.$on('on-form-item-delete', node => {
      this.fields.splice(this.fields.indexOf(node), 1)
    })
  },
  methods: {
    resetFields () {
      for (let field of this.fields) {
        field.resetField()
      }
    },
    validateAll (callback) {
      return new Promise(resolve => {
        let valid = true
        let count = 0
        this.fields.forEach(field => {
          field.validate('', errors => {
            console.log(errors)
            if (errors) {
              valid = false
            }
            if (++count === this.fields.length) {
              // 全部完成
              resolve(valid)
              if (typeof callback === 'function') {
                callback(valid)
              }
            }
          })
        })
      })
    }
  }
}
</script>

form.vue主要就是传递用户设置的校验规则和表单双向数据绑定的值并 提供一键校验所有表单元素功能的组件;内容分析:

  • props中的两个属性分别为 整个表单的校验规则集合rules 和整个表单值的对象集合modes。
  • 因为Vue组件渲染机制的原因,form-item中的mounted会优先于父级的mounted渲染,所以在form-item 的mounted中暴露给form的实例只能在created中监听~~
  • methods中定义resetFields() 一键重置(一般form表单中不一定有一键重置检验信息该功能,在此不详细描述);validateAll()方法就是一键全部校验;会返回一个Promise实例,方法内部做了两种处理,兼容callback回调的方式和then()的方式处理返回值。

到此,我们整个form基础组件也就开发完了,我们现在找个demo引用实践一下

<template>
  <div class="form-box">
    <w-form :rules="validateRules" :modes="validateModes" ref="form">
      <form-item label="姓名" prop="name">
        <w-input name="name" v-model="validateModes.name"/>
      </form-item>
      <form-item label="邮箱" prop="email">
        <w-input name="email" v-model="validateModes.email"/>
      </form-item>
      <button type="button" class="submit_btn" @click="handleSubmit">表单验证</button>
    </w-form>
  </div>
</template>
<script>
import WForm from '@/base/form/form'
import FormItem from '@/base/form-item/form-item'
import WInput from '@/base/input/input'
import Emitter from '@/mixins/emitter'
export default {
  mixins: [Emitter],
  name: 'index',
  components: {
    WForm,
    FormItem,
    WInput
  },
  data () {
    return {
      validateRules: {
        name: [
          {
            required: true,
            message: '用户名不能为空',
            trigger: 'blur'
          }
        ],
        email: [
          {
            required: true,
            message: '邮箱不能为空',
            trigger: 'blur'
          },
          {
            type: 'email',
            message: '邮箱格式不正确',
            trigger: 'blur'
          }
        ]
      },
      validateModes: {
        name: '',
        email: ''
      }
    }
  },
  mounted () {
    this.capture('w-input', 'test', 'test')
  },
  methods: {
    handleSubmit () {
      // this.$refs.form.validateAll(valid => {
      //   // console.log(valid)
      //   if (valid) {
      //     alert('验证通过')
      //   }
      // })

      this.$refs.form.validateAll().then(valid => {
        if (valid) {
          alert('验证通过')
        }
      })
    }
  }
}
</script>

整个demo很简单,两个输入框一个按钮,检验触发事件是blur(form-item.vue暂时只响应blur)
data中validateRules就是 async-validator需要的验证规则格式。
当我们对每个输入框失去焦点,响应的输入框校验就触发了,当我们点击按钮,就触发了全部校验。
在这里插入图片描述

总结

这个form表单组件以及封装完成,描述的可能比较跳跃,我简单的画了一个思维导图便于对比理解~

在这里插入图片描述
这个表单组件只包含了输入框,还有其他的表单元素并没有加入,另外也不支持自定义校验,我们在下一章节继续分析。

备注

源码地址: https://github.com/wangyangsea/wy-validate

参考文章

Vue.js 组件精讲

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值