Form:管理数据模型-model,校验规则-rules,全局校验方法validate
FormItem:显示标签-label,执行校验-prop,显示校验结果
Input:绑定数据模型 v-model,通知FormItem执行校验
1、实现自定义的双向绑定
// KInput.vue中 <div> <!-- 自定义组件要实现v-model,必须实现 :value,@input -->
<!-- $attr存储的是props之外的部分,放在此处后,就可以不需要设置type了 --> <input v-bind="$arrt" :value="value" @input="onInput"> </div>
inheritAttrs: false, // 避免顶层容器继承属性 methods: { onInput(e){ // 通知父组件数值变化 this.$emit('input', e.target.value) } }
form文件夹下的index.vue组件
<div> <KInput v-model="model.username"></KInput> <KInput v-model="model.password" type="password"></KInput> {{model}} </div> import KInput from './KInput.vue' data () { return { model: { username: 'tom', password: '' } } }, components: { KInput }
v-model相当于一个语法糖
<KInput v-model="model.username"></KInput>
等同于
<KInput :value="model.username" @input="xxxx"></KInput>
FormItem
任务1:给Input预留插槽 - slot
任务2:能够展示label 和 校验信息
任务3:能够进行校验
index.vue 文件
<div> <KForm :model="model" :rules="rules" ref="loginForm"> <KFormItem label="用户名" prop="username"> <KInput v-model="model.username"></KInput> </KFormItem> <KFormItem label="密码" prop="password"> <KInput v-model="model.password" type="password"></KInput> </KFormItem> <KFormItem> <button @click="onLogin">登录</button> </KFormItem> </KForm> {{model}} </div> import KInput from './KInput.vue' import KFormItem from './KFormItem.vue' import KForm from './KForm.vue' export default { data () { return { model: { username: 'tom', password: '' }, rules: { username: [{required: true, message: '用户名必填'}], password: [{required: true, message: '密码必填'}] } } }, components: { KInput, KFormItem, KForm }, methods: { onLogin() { this.$refs.loginForm.validate((isValied) => { if(isValied){ alert("登录!!") }else{ alert("有错!!") } }) } } }
KForm.vue 文件
<div> <slot></slot> </div> export default { provide(){ return{ form: this // 传递Form实例给后代 } }, props:{ model: { type: Object, required: true }, rules: { type: Object } }, data () { return { } }, methods: { validate(callback){ // map的结果是若干Promise数组 const tasks = this.$children .filter(item => item.prop) .map(item => item.validate()) // 所有任务必须全部成功才算校验通过 Promise.all(tasks) .then(() => callback(true)) .catch(() => callback(false)) } } }
KFormItem.vue 文件
<div> <label v-if="label"> {{label}} </label> <!-- 存放input --> <slot></slot> <!-- 校验信息 --> <p v-if="errorMessage"> {{errorMessage}} </p> </div> import Schema from 'async-validator' export default { inject: ['form'], props: { label: { type: String, default: '' }, prop: String }, data () { return { errorMessage: '' } }, mounted () { // 监听校验事件,并执行监听 this.$on('validate', () => { this.validate() }) }, methods: { validate(){ // 执行组件校验 // 1、获取校验规则 const rules = this.form.rules[this.prop] // 2、获取数据 const value = this.form.model[this.prop] // 3、执行校验 // 使用async-validator库(element-ui也使用的这个校验库) // npm i async-validator -S const desc = { [this.prop]: rules } const schema = new Schema(desc) // 参数1是值, 参数2是校验错误对象数组 // 返回的Promise boolean return schema.validate({[this.prop]: value}, errors => { if(errors){ // 有错 this.errorMessage = errors[0].message }else{ // 如果没错,就清除错误信息 this.errorMessage = '' } }) } } }
KInput.vue 文件
<div> <!-- 自定义组件要实现v-model,必须实现 :value,@input --> <!-- $attrs存储的是props之外的部分 --> <!-- {type: 'password'} -- v-bind="$attrs"相当于将其展开 --> <input :value="value" @input="onInput" v-bind="$attrs"> </div> export default { inheritAttrs: false, props:{ value: { type: String, defaule: '' } }, data () { return { } }, components: { }, created () { }, methods: { onInput(e){ // 通知父组件数值变化 this.$emit('input', e.target.value) // 通知FormItem校验 this.$parent.$emit('validate') } } }