form组件旨在统一控制表单组件的输入值、获取值、校验值,提升开发效率。
核心逻辑:在父元素加载时订阅xx方法,在子元素的合适位置进行触发
el-form组件
- 用于包裹所有的表单元素,和传递校验规则;
- 通过slot容纳子组件
- 定义validate方法:找到所有的表单子元素,通过Promise.all遍历执行表单子元素的校验方法,校验完成后执行相应的回调
<template>
<form @submit.prevent>
<slot></slot>
</form>
</template>
--------------------------------
<script>
export default {
name: 'el-form',
provide() { // 通过provide和inject方式进行组件间通信
return {elForm: Object}
},
props: {
model: {
type: Object,
default: () => ({}),
},
rules: Object
},
methods: {
async validate(cb) {
let children = this.$children;
let arr = [];
function findFormItem(children) {
children.forEach(child => {
if (child.$options.name === 'el-form-item') {
arr.push(child);
}
if (child.$children) {
findFormItem(child.$children)
}
})
}
findFormItem(children)
try {
await Promise.all(arr.map(item => item.validate()))
cb(true)
}catch{
cb(false)
}
}
}
}
</script>
el-form-item组件
- 用于包裹表单子元素,传递表单子元素的label和属性名;
- 通过slot容纳子组件
- 在mounted时,订阅validate事件
- 定义validate方法:通过inject和provide从表单form中获取本表单元素的校验规则rule、表单key和value,通过async-validator插件进行校验
<template>
<div>
<label v-if='label'></label>
<slot></slot>{{errMsg}}
</div>
</template>
---------------------------------
<script>
import Schema from 'async-validator'
export default {
name: 'el-form-item',
inject: ['elForm'], // 通过provide和inject方式进行组件间通信
props: {
label: {
type: String,
default: ''
},
prop: String
},
data() {
return {
errMsg: ""
}
},
mounted() {
this.$on('validate', this.validate)
},
methods: {
validate() {
if(this.prop) { // 需要时进行校验
let rule = this.elForm.rules[this.prop];
let newVal = this.elForm.model[this.prop];
let descriptor = {
[this.prop]: rule,
}
let schema = new Schema(descriptor);// 通过描述信息创建骨架
schema.validate({[this.prop]: newVal}, (err) => {
if (err) {
this.errMsg = err[0].message;
} else {
this.errMsg = ''
}
})
}
}
},
}
</script>
el-input组件
- 输入时,通过this.$emit("input", e.target.value)同步输入的内容
- 输入时,找到name为el-form-item的组件,触发validate方法进行校验
<template>
<input type="text" :value='value' @input='handleInput'>
</template>
--------------------------------------
<script>
export default {
name: 'el-input',
props: {
value: {
type: String,
default: ''
},
},
methods: {
handleInput(e){
this.$emit("input", e.target.value);
let parent = this.$parent;
while(parent) {
let name = parent.$options.name
if (name === 'el-form-item') {
break
}else {
parent = parent.$parent
}
}
if(parent) {
parent.$emit('validate')
}
}
}
}
</script>
使用
<template>
<div>
{{ruleForm}}
<el-form
:model='ruleForm'
:rules='rules'
ref='ruleForm'
>
<el-form-item label='用户名' prop='username'>
<el-input v-model='ruleForm.username'></el-input>
</el-form-item>
<el-form-item label='密码' prop='password'>
<el-input v-model='ruleForm.password'></el-input>
</el-form-item>
<el-form-item label='密码' prop='password'>
<button @click='submitForm'>提交</button>
</el-form-item>
</el-form>
</div>
</template>
----------------------------------------
<script>
import elForm from './components/el-form'
import elFormItem from './components/el-form-item'
import elInput from './components/el-input'
export default {
components: {
'el-form': elForm,
'el-form-item': elFormItem,
'el-input': elInput,
},
data() {
return {
ruleForm: {
username: "hihi",
password: 'xx',
},
rules: {
username: [
{required: true, message: "请输入用户名"},
{min: 3, max: 5, message: "长度在3-5个字符"}
],
password: [
{required: true, message: "请输入用户名"}
]
},
}
},
methods: {
submitForm() {
this.$refs['ruleForm'].validate(valid => {
if (valid) {
console.log('提交')
}else{
console.log('error');
return false
}
})
}
}
}
</script>