表单验证在我们前端开发中是经常要用到的业务需求,在日常开发中会大量的使用!那么,我们每天都在使用的这种验证表单是如何封装实现的呢?今天我们就对这个组件的封装来进行研究学习。
首先看下elementui的效果
使用方法就不说了,可以仔细阅读使用文档
宇宙门: elementui官网
下面是我们今天要展示的自己封装的效果,是此次的重点!
相关的技术栈是vue3.0+ts+bootsrap(借用她的样式)
正常的情况下展示
直接点击提交的时候,值为空所以会进行校验
输入完符合要求的内容,在点击提交的时候错误提示会消失
注意点一:vue3.0不支持 $on, $emit 的发布订阅 api,需要我们安装 mitt 这个插件进行事件通信传输数据, $emit 仍然是现有 API 的一部分,因为它用于触发由父组件以声明方式附加的事件处理程序.
官方推荐使用第三方类库: mitt
cnpm i mitt -S / yarn add mitt
validateInput.vue
<template>
<div class="form-group">
<label :for="prop">{{ title }}</label>
<!-- {{ $attrs }} -->
<input
class="form-control"
:id="prop"
:class="{ 'is-invalid': inputRef.error }"
:value="inputRef.val"
@blur="validateInput"
@input="updateValue"
v-bind="$attrs"
/>
<div class="form-text invalid-feedback" v-if="inputRef.error">
{{ inputRef.message }}
</div>
</div>
</template>
传参分析:
- label for id 此处不用解释了吧,就是我们在点击文字title的时候自动聚焦到对应的输入框
- v-bind= " $attrs " 父组件传递数据到子组件,vue2/3都是支持的。注意的地方是如果属性是以props的方式直接传递到子组件的话,$attrs是不包含这个属性的!
<script lang="ts">
import { defineComponent, onMounted, PropType, reactive, ref } from "vue";
//这个emitter 是在 ValidateForm暴露出来的,我们引入进行事件的emit
import { emitter } from "./ValidateForm.vue";
const reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
interface RuleProp {
type: "required" | "email" | "password";
message: string;
}
//这是个type定义的校验规则
export type RulesProp = RuleProp[];
export default defineComponent({
inheritAttrs: false, //$attrs属性不在子组件的dom上展示就设置为false
props: {
rules: {
type: Array as PropType<RulesProp>,
},
title: {
type: String,
},
prop: {
type: String,
},
modelValue: {
type: String,
default: "",
},
},
setup(context, props) {
const emailVal = ref("");
const inputRef = reactive({
val: context.modelValue,
error: false,
message: "",
});
//校验每个输入框的方法,统一在这里进行判断true or false
const validateInput = () => {
if (context.rules) {
//setup里面不能使用this,可以用context获取上下文,与2.0this类似
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;
case "password":
console.log(inputRef.val, "inputRef.val");
passed = inputRef.val.length > 6;
default:
break;
}
return passed;
});
inputRef.error = !allPassed;
return allPassed;
}
return true;
};
const updateValue = (e: KeyboardEvent) => {
const targetValue = (e.target as HTMLInputElement).value;
inputRef.val = targetValue;
props.emit("update:modelValue", targetValue);
};
onMounted(() => {
//触发submit提交事件,把validateInput 方法传递给ValidateForm
emitter.emit("submit", validateInput);
});
return { inputRef, validateInput, updateValue };
},
mounted() {},
});
</script>
ValidateForm.vue
<template>
<div>
<form class="validate-form-container">
<slot></slot>
<div class="submit-area" @click.prevent="submitForm">
//具名插槽,设置了默认值,我们不设置自定义的按钮的时候就显示这个默认的按钮
<slot name="submit">
<button type="submit" class="btn btn-primary">
提交
</button>
</slot>
</div>
</form>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted } from "vue";
//引入官方推荐第三方的插件mitt
import mitt from "mitt";
//极乐门:[mitt](https://www.npmjs.com/package/mitt)
type Events = {
submit: ValidateFunc; //事件名: 类型
};
type ValidateFunc = () => boolean; // 返回值为boolean的校验
export const emitter = mitt<Events>();
export default defineComponent({
setup(context, props) {
//组件被多次复用,触发多次,我们要拿到所有的组件的验证结果
let funcArr: ValidateFunc[] = [];
//此处进行遍历,对validateInput 传递出来的每个validateInput方法执行
const submitForm = () => {
const result = funcArr.map((item) => item()).every((result) => result);
//然后触发调用ValidateForm组件上的自定义事件@form-submit="onFormSubmit",遍历校验的值传递出去
props.emit("form-submit", result);
};
//每个input都会触发一个validateInput,把它保存到一个数组里面
const callback = (func: ValidateFunc) => {
// console.log(func(), "test1");
funcArr.push(func);
};
emitter.on("submit", callback);
onUnmounted(() => {
//卸载的时候销毁这个监听,内存开销节省
emitter.off("submit", callback);
//事件数组值为空
funcArr = [];
});
return { submitForm };
},
emits: ["form-submit"],
});
</script>
上面的代码需要注意的是,文档对ts的特别说明,不想被类型验证搞死的要仔细阅读这里,不然红波浪会一直缠着你
Home.vue
<validate-form @form-submit="onFormSubmit">
<validate-input
:rules="emailRules"
:title="'Email Address'"
:prop="'email'"
type="email"
v-model="modelValue"
placeholder="请输入邮箱地址"
></validate-input>
<validate-input
:rules="pwRules"
:title="'Password'"
:prop="'text'"
type="password"
v-model="passwordValue"
placeholder="请输入密码"
></validate-input>
//具名插槽的简写方式1
<template #submit>
<button name="submit" type="submit" class="btn btn-danger">
提 交
</button>
</template>
</validate-form>
//方式2,1或者2按个人喜欢进行选取吧
<template v-slot:submit>
<button name="submit" type="submit" class="btn btn-danger">
提 交
</button>
</template>
</validate-form>
js部分
<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
import ValidateInput from "../components/ValidateInput.vue";
import ValidateForm from "../components/ValidateForm.vue";
import GlobalHeader, { UserProps } from "../components/GlobalHeader.vue";
import ColumnList, { ColumnProps } from "../components/ColumntList.vue";
const user: UserProps = {
isLogin: true,
name: "Gaofeng",
id: 0,
};
const reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
export default defineComponent({
name: "Home",
components: { ColumnList, GlobalHeader, ValidateInput, ValidateForm },
setup() {
const emailRef = reactive({
val: "",
error: false,
message: "",
});
const emailValidate = () => {
if (emailRef.val.trim() === "") {
emailRef.error = true;
emailRef.message = "can not be empty";
} else if (!reg.test(emailRef.val)) {
emailRef.error = true;
emailRef.message = "format is not correct";
} else {
emailRef.error = false;
emailRef.message = "";
}
};
const emailRules = [
{
type: "required",
message: "电子邮箱地址不能为空",
},
{
type: "email",
message: "请输入正确的电子邮箱地址",
},
];
const pwRules = [
{
type: "required",
message: "密码不能为空",
},
{
type: "password",
message: "密码长度不能小于6位",
},
];
const modelValue = ref("");
const passwordValue = ref("");
const inputRef = ref(null);
const onFormSubmit = (value: boolean) => {
//此处就是接受返回的验证值,是否通过验证
console.log(value, "dddd");
};
return {
lists,
user,
emailRef,
emailValidate,
emailRules,
modelValue,
inputRef,
pwRules,
onFormSubmit,
passwordValue,
};
},
});
</script>
true 通过
false 未通过
到此这个校验的表单组件就封装完成了,相比vue2.0的分装,主要ts的格式校验比较繁琐,耐心的去查文档吧!打完收工~~~