完整源代码请移步GitHub: https://github.com/webxing/element_form
要仿照的elementUI的效果:
<el-form :model="ruleForm" :rules="rules" ref="loginForm">
<el-form-item label="用户名" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pwd">
<el-input v-model="ruleForm.pwd"></el-input>
</el-form-item>
</el-form>
实现思路
Step1:实现一个支持 v-model 的自定义输入组件
要实现的结果如下:保证 l-input 可以使用 v-model 来进行数据的双向绑定
<l-input v-model="inputValue" :type="type"></l-input>
v-model
:
- 原理:
<l-input :value="inputValue" @input="inputValue = $event"></l-input>
- 把 inputValue 传给组件 l-input 内部
- 当组件 l-input 的 input 事件被触发时自动将事件携带的参数赋值给 inputValue
$event
:
当在父级组件监听事件的时候,我们可以通过 $event 访问到被抛出的值
代码
l-input 组件实现代码如下:
<template>
<!-- LInput组件 -->
<div class='l-input'>
<input :type="type" :value="value" @input="handlerInput">
</div>
</template>
这个组件内的 <input>
必须:
- 将其 value 特性绑定到一个名叫 value 的 prop 上
- 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
export default {
props: {
value: {
type: String,
default: ''
},
type: {
type: String,
default: ''
}
},
methods: {
handlerInput(e) {
// 通知父组件值更新
this.$emit('input', e.target.value)
// 通知 formItem 进行校验
this.$parent.$emit('validate',e.target.value)
}
},
}
Step2:实现一个支持 负责label标签 以及 显示错误信息的组件
要实现的结果如下:保证 l-form-item 可以显示 label 以及可以对 prop 进行数据校验
<l-form-item label='用户名' prop='name'>
<l-input v-model="ruleForm.name" type="text"></l-input>
</l-form-item>
代码
<template>
<div>
<label class='label' v-if="label">{{label}} </label>
<slot></slot>
<span class='tips' v-if="validateStatus === 'error'">{{validateMsg}}</span>
</div>
</template>
import schema from 'async-validator'
export default {
inject: ["form"], // 注入form 获取到 form 的 model 以及 rules
props: {
label: {
type: String,
default: ''
},
prop: {
type: String,
default: ''
}
},
data() {
return {
validateStatus: '',
validateMsg: ''
}
},
created() {
// 校测到来此 LInput 的检测请求
this.$on('validate', this.validate)
},
mounted() {
// 挂载到form上时,派发一个添加事件
// 如果此组件传入了 props ,则需要通知父组件 以放入检测数组
if (this.prop) {
this.$parent.$emit("formItemAdd", this)
}
},
methods: {
validate() {
// 使用 async-validator 进行校验
return new Promise((resolve) => {
console.log('validate -- input 通知我校验数据了', this.form.model[this.prop])
// 校验规则
let descriptor = {
[this.prop]: this.form.rules[this.prop]
}
// 校验器
let validator = new schema(descriptor)
// 开始校验数据
validator.validate({[this.prop]: this.form.model[this.prop]}, (error) => {
if(error) {
this.validateStatus = 'error'
this.validateMsg = error[0].message
resolve(false)
} else {
this.validateStatus = ''
this.validateMsg = ''
resolve(true)
}
})
})
}
},
}
Step3:实现一个支持 负责label标签 以及 显示错误信息的组件
要实现的结果如下:传入 表单数据对象(ruleForm) 和 表单验证规则(rules)后可以实现表单的数据校验 提供 validate 方法来获取整个表单的验证结果
<l-form :model='ruleForm' :rules='rules' ref='loginForm'>
<l-form-item label='用户名' prop='name'>
<l-input v-model="ruleForm.name" type="text"></l-input>
</l-form-item>
<l-form-item label='密码' prop='pwd'>
<l-input v-model="ruleForm.pwd" type="password"></l-input>
</l-form-item>
<l-form-item>
<button @click='submitForm'>signIn</button>
</l-form-item>
</l-form>
// 传入的表单数据对象和表单验证规则
ruleForm: {
name: "",
pwd: ""
},
rules: {
name: [
{ required: true, message: "请输入名称" },
{ min: 6, max: 10, message: "请输入6~10位用户名" }
],
pwd: [{ required: true, message: "请输入密码" }]
}
// 调用组件的validate方法获取验证结果
this.$refs.loginForm.validate(valid => {
if (valid) {
alert("提交登录!")
} else {
console.log("校验失败")
return false
}
})
代码
<template>
<div>
<slot></slot>
</div>
</template>
export default {
provide() {
return {
form: this // 将表单实例传递给后代
}
},
props: {
model: {
type: Object,
required: true
},
rules: {
type: Object
}
},
data() {
return {
files: []
}
},
created() {
// 缓存需要校验的表单项
this.$on('formItemAdd', item => this.files.push(item))
console.log(this.files)
},
methods: {
// 将FormItem数组转换为 validate() 返回的promise数组
// 调用 LForm 组件的 validate 方法可以得到表单数据校验的结果
async validate(callback) {
let tasks = this.files.map(item => item.validate())
console.log(tasks)
// 获取所有结果统一处理
const results = await Promise.all(tasks)
let ret = true
results.forEach(valid => {
if (!valid) {
ret = false // 只要一个失败就失败
}
})
callback(ret)
}
},
}