test-form.vue
<template>
<el-form
:model="ruleForm"
:rules="rules"
ref="ruleForm"
>
<el-form-item label="用户名" prop="username">
<el-input type="username" v-model="ruleForm.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
import elForm from "../components/el-form.vue";
import elFormItem from "../components/el-form-item.vue";
import elInput from "../components/el-input.vue";
export default {
components: {
"el-form": elForm,
"el-input": elInput,
"el-form-item": elFormItem
},
data() {
return {
ruleForm: {
pass: "",
username: ""
},
rules: {
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" },
{ min: 3, max: 5, message: "长度在 3 到 5 个字符", trigger: "blur" }
],
pass: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{ min: 3, max: 5, message: "长度在 3 到 5 个字符", trigger: "blur" }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
alert("submit!");
} else {
console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
};
</script>
el-form.vue
- slot 中插入组件
- provide 出自己,供子孙组件获取当前组件
- 接收 校验规则rules,表单数据model
- 提供表单校验函数。对所有子组件进行校验
<template>
<form @submit.prevent>
<slot></slot>
</form>
</template>
<script>
export default {
name: "elForm",
provide() {
return { elForm: this };
},
props: {
rules: {
type: Object,
default: () => ({}) // 保证数据不被共享和data一样
},
model: {
type: Object,
default: () => ({}) // 保证数据不被共享和data一样
}
},
methods: {
resetFields() {
// .async v-model 找到输入框清理
},
async validate(cb) {
let children = this.$broadcast("elFormItem"); // 获取所有子元素
try {
await Promise.all(children.map(child => child.validate()));
cb(true);
} catch {
cb(false);
}
}
},
mounted() {
console.log(this);
}
};
</script>
el-form-item.vue
- 注入 elForm,拿到当前el-form-item组件传入的v-model,
this.elForm.model[this.prop]
, 校验规则this.elForm.rules[this.prop]
- $on 监听当子组件 el-input 值变化时进行值校验
<template>
<div>
<label v-if="label">{{label}}</label>
<slot></slot>
<span v-if="errorMessage">{{errorMessage}}</span>
</div>
</template>
<script>
import Schema from "async-validator";
export default {
name: "elFormItem",
inject: ["elForm"],
data() {
return { errorMessage: null };
},
props: {
label: {
type: String
},
prop: {
type: String
}
},
methods: {
validate() {
if (this.prop) {
let value = this.elForm.model[this.prop];
let ruleValue = this.elForm.rules[this.prop];
let schema = new Schema({
[this.prop]: ruleValue // username:rule
});
return schema.validate({ [this.prop]: value }, (err) => {
if (err) {
this.errorMessage = err[0].message;
} else {
this.errorMessage = null;
}
});
}
}
},
mounted() {
this.$on("validate", () => {
this.validate();
});
}
};
</script>
el-input.vue
- 实时监听当前input框的值,并 emit 当前值给父组件,v-model 语法糖
- 实时监听值变化,进行校验
<template>
<input type="text" :value="value" @input="handleInput" />
</template>
<script>
export default {
name: "elInput",
props: {
value: {
type: String
}
},
methods: {
handleInput(e) {
this.$emit("input", e.target.value);
this.$dispatch("elFormItem", "validate");
}
}
};
</script>
- $broadcast 函数:向下寻找子组件,存储componentName的子组件,或者让子组件 emit事件
- $dispatch 函数:向上寻找指定父组件,并向父组件抛出事件
Vue.prototype.$broadcast = function (componentName, eventName) {
let children = this.$children;
let arr = [];
function findFormItem(children) {
children.forEach(child => {
if (child.$options.name === componentName) {
if (eventName) {
arr.push(child.$emit('eventName'))
} else {
arr.push(child)
}
}
if (child.$children) {
findFormItem(child.$children);
}
});
}
findFormItem(children);
return arr;
}
Vue.prototype.$dispatch = function (componentName, eventName) {
let parent = this.$parent;
while (parent) {
let name = parent.$options.name;
if (name == componentName) {
break;
} else {
parent = parent.$parent;
}
}
if (parent) {
if (eventName) {
return parent.$emit(eventName)
}
return parent
}
}