接上面的一片文章,我们封装一个类似于elementui的表单控件,实现的效果:
正常显示的
验证为空时的报错提示
验证格式不正确的报错
下面继续沿用上一篇文章的封装思路二来实现这个效果
form.vue
// emailRules验证规则
// title控件名
// type 类型
// v-model="modelValue" 在form组件获取validate-input 组件的值
<form>
{{ modelValue }}
<validate-input
:rules="emailRules"
:title="'Email Address'"
:prop="'email'"
:type="'email'"
></validate-input>
</form>
js部分
import ValidateInput from "../components/ValidateInput.vue";
const reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
export default defineComponent({
name: "Home",
components: {ValidateInput },
setup() {
const emailRef = reactive({
val: "",
error: false,
message: "",
});
const emailRules = [
{
type: "required",
message: "电子邮箱地址不能为空",
},
{
type: "email",
message: "请输入正确的电子邮箱地址",
},
];
const modelValue = ref("");
return {
emailRef,
emailValidate,
emailRules,
modelValue,
};
},
});
ValidateInput.vue
<template>
<div class="form-group">
<!-- {{ inputRef.val }} -->
<label :for="prop">{{ title }}</label>
<input
:type="type"
class="form-control"
:id="prop"
:class="{ 'is-invalid': inputRef.error }"
v-model="inputRef.val"
@blur="validateInput"
/>
<div class="form-text invalid-feedback" v-if="inputRef.error">
{{ inputRef.message }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, reactive } from "vue";
//邮箱验证的正则
const reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
interface RuleProp {
type: "required" | "email";
message: string;
}
export type RulesProp = RuleProp[];
export default defineComponent({
props: {
rules: {
//是prop的类型书写方式
type: Array as PropType<RulesProp>,
},
title: {
type: String,
},
prop: {
type: String,
},
type: {
type: String,
},
modelValue: {
type: String,
},
},
setup(context, props) {
const inputRef = reactive({
val: context.modelValue,
error: false,
message: "",
});
const validateInput = () => {
if (context.rules) {
const allPassed = context.rules.every((item) => {
let passed = true;
inputRef.message = item.message;
switch (item.type) {
case "required":
passed = inputRef.val.trim() != "";
break;
case "email":
passed = reg.test(inputRef.val);
break;
default:
break;
}
return passed;
});
inputRef.error = !allPassed;
}
};
return { inputRef, validateInput };
},
});
</script>
至此,整个表单的验证就完成了,但是…我们此时在form组件里面无法获取到组件输入的值,于是我们还要在组件上加v-model的功能
<validate-input
:rules="emailRules"
:title="'Email Address'"
:prop="'email'"
:type="'email'"
v-model="modelValue"
></validate-input>
然后在script中,进行赋值暴露出
...
const modelValue = ref("");
return {...,modelValue} //暴露出来
...
然后在validateInput中接收
//之前的model要拆成value和input事件,就是为了emit我们的输入框的值
<input
:type="type"
class="form-control"
:id="prop"
:class="{ 'is-invalid': inputRef.error }"
@blur="validateInput"
:value="inputRef.val"
@input="updateValue" //输入事件
/>
props:{
...
modelValue,
...
}
setup(context, props) {
//context现在指的是上下文环境,个人理解就是相当于vue2.0的this,
//setup拿不到this,用context进行获取父组件传送的数据
const inputRef = reactive({
val: context.modelValue, //此处赋值为我们接收到v-model的值
error: false,
message: "",
});
const validateInput = () => {
if (context.rules) {
const allPassed = context.rules.every((item) => {
let passed = true;
inputRef.message = item.message;
switch (item.type) {
case "required":
passed = inputRef.val.trim() != "";
break;
case "email":
passed = reg.test(inputRef.val);
break;
default:
break;
}
return passed;
});
inputRef.error = !allPassed;
}
};
const updateValue = (e: KeyboardEvent) => {
//这里的难点是e.target的类型断言吧,搞不懂的话会一直会有错误提示,重点理解下
const targetValue = (e.target as HTMLInputElement).value;
inputRef.val = targetValue;
//此处就是核心了,触发update,更改父组件传送的v-model数据实现在父组件获取子组件输入的数据
props.emit("update:modelValue", targetValue);
};
return { inputRef, validateInput, updateValue };
},
至此,组件的封装就差不多啦,还需要细节完善,大概的思路就是这样吧!打完收工~~~