首先我们先看看我们第一步要实现的表单都有那些功能吧,先看一个小小的demo
下面我们就一步一步来实现它:
基本的文件结构就是这样的,另外我们还需要一个emmiter文件 具体的代码是:
function broadcast(componentName, eventName, params) {
this.$children.forEach(child => {
const name = child.$options.name;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
methods: {
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;
let name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};
input文件里面我们就实现一个最简单的input组件
<template>
<input type="text" @blur="handleInputBlur" @input="handleInput" :value="currentValue">
</template>
<script>
import Emitter from '@/mixins/emitter'
export default {
name: 'Input',
components: {
},
props: {
value: '',
},
mixins: [ Emitter ],
data() {
return{
currentValue: ''
}
},
computed: {
},
watch: {
value(val){
this.currentValue = val
}
},
mounted() {
},
methods: {
// 这里我们只是用到了input里面的blue和input方法
handleInputBlur(e){
// 这里是v-model的语法糖
this.$emit('input', this.currentValue)
//这里我们用mixins里面的dispath方法 来触发父组件方法
this.dispatch('pFormItem', 'on-input-blur', this.currentValue)
},
handleInput(e){
let value = e.target.value
this.$emit('input', value)
//这里我们用mixins里面的dispath方法 来触发父组件方法
this.dispatch('pFormItem', 'on-input-change', value)
}
}
}
</script>
</style>
下面再来看看form-item中都有那些内容吧
<template>
<div class="form-item">
<span :class="[isRequired ? 'form-item-require' : '', 'fll']" :style="{width: form.labelWidth}">{{label}}</span>
<!-- 默认的插槽 -->
<slot class="fll"></slot>
<!-- 错误提示信息 -->
<div v-if="validateState === 'error'" class="form-item-error-message" :style="{marginLeft: form.labelWidth}">{{validateMessage}}</div>
</div>
</template>
<script>
import AsyncValidator from 'async-validator';
import Emitter from '@/mixins/emitter'
export default {
name: 'pFormItem',
components: {
},
props: {
prop: String,
label: String
},
inject: ['form'], //获取到父组件中注入的当前实例
mixins: [ Emitter ],
data() {
return{
isRequired: false, //是否是必填项
initialValue: '',
validateState: '', //校验状态
validateMessage: '' //校验信息
}
},
computed: {
fieldValue(){
// 获取表单的值
return this.form.model[this.prop]
}
},
mounted() {
if(this.prop){
// 校验的时候我们主要是通过prop来进行判断的 有prop则进行校验
// 设置初始值,以便在重置时恢复默认值
this.initialValue = this.fieldValue
this.setRules()
}
},
methods: {
setRules(){
let rules = this.getRules()
rules.every(rule => {
// 过滤required
this.isRequired = rule.required
})
// 传入prop需要验证
if(this.prop){
// 如果需要验证 则把当前form-item实例缓存到form中
this.dispatch('pForm', 'on-form-item-add', this)
}
// 挂载的两个blur和change事件
this.$on('on-input-blur', this.onFieldBlur)
this.$on('on-input-change', this.onFieldChange)
},
onFieldBlur(){
this.validate('blur')
},
onFieldChange(){
this.validate('change')
},
// 获取当前rules
getRules(){
let formRules = this.form.rules
formRules = formRules ? formRules[this.prop] : []
return [].concat(formRules || [])
},
getFilterRule(trigger){
const rules = this.getRules()
return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1)
},
validate(trigger, callback = function(){}){
// 这里主要是通过rules来判断是否需要进行校验
let rules = this.getFilterRule(trigger)
if(!rules || rules.length === 0){
return true
}
this.validateState = 'validating'
let descriptor = {}
let source = {}
descriptor[this.prop] = this.form.rules[this.prop]
source[this.prop] = this.fieldValue
// 这里主要是使用async-validator来进行校验的
// descriptor指的是当前校验规则
const validator = new AsyncValidator(descriptor)
validator.validate(source, { firstFields: true }, error => {
// 通过error来对校验结果进行处理
this.validateState = error ? 'error' : 'success'
this.validateMessage = error ? error[0].message : ''
callback(this.validateMessage)
})
},
// 重置当前表单
resetField(){
this.form.model[this.prop] = ''
}
}
}
</script>
这里我们只在form-item进行了表单的验证和重置当前表单两个操作
form组件
<template>
<div class="form">
<slot></slot>
</div>
</template>
<script>
import { resolve, reject } from 'q';
export default {
name: 'pForm',
components: {
},
props: {
// 数据
model: {
type: Object
},
// rules校验规则
rules: {
type: Object
},
// label的width
labelWidth: {
type: String
}
},
provide(){
return {
form: this //在父组件注入当前实例
}
},
data() {
return{
fields: [] //缓存form-item的数组
}
},
computed: {
},
created() {
this.$on('on-form-item-add', (fields) => {
//把form-item放到fields中
if(fields) this.fields.push(fields)
})
},
methods: {
// 重置当前表单 这里是循环form-item并调用里面的resetField方法
resetFields(){
this.fields.forEach(field => {
field.resetField()
});
},
// 整个表单的验证
validate(callback){
// 支持promise
return new Promise((resolve, reject) => {
let valid = true
let count = 0
// 这里也是循环form-item实例使用里面的validate方法
this.fields.forEach(field => {
field.validate('', errors => {
if(errors){
valid = false
}
// 这里是判断循环是否结束
if(++count === this.fields.length){
resolve(valid)
// form表单的验证结果即支持promise也支持callback
if(typeof callback === 'function'){
callback(valid)
}
}
})
})
})
}
}
}
</script>
到此我们的基本的form表单验证基本上完成了。
下面我们说一下具体的实现思路:我们首先在form中provide当前实例,然后在form-item中获取实例中的数据,通过input的blur和change事件来触发校验的方法,并且我们组件加载的时候把需要验证的实例缓存到form中,方便我们以后调用form-item中的方法, 基本的思路就是这样的。
如果感觉有用就start一下吧 😄😄
使用中有什么问题,或者不足之处可以随时联系我!!