最近一直在忙着做项目,在这个过程中也遇到了很多问题,之前虽然也有做笔记总结,但从未发过文章,这是第一次尝试,既为分享,也为记录,写得不好请各位多多指正。

言归正传,相信大家经常都会遇到要处理表单验证的环节,而我在最近的项目中也遇到需要做表单验证的业务,在此做一下小菜鸟的分享。

先上效果图:

表单校验前的准备

首先可以先参考Element Plus官网表单组件的校验表单基本格式

地址:Form 表单 | Element Plus

准备过程总结为如下三步:

  1. 从element-plus中引入类型FormInstance和FormRules,并把表单对象的节点设置为FormInstance类型,表单内对所有参数约束的总规则(rules)设置为FormRules类型
  2. 定义包含表单内各参数的响应式数据对象ruleForm,并把ruleForm和rules在表单<el-form>标签的属性中进行单向数据绑定。此外需为表单参数对应的item设置相应的prop和v-model绑定对应ruleForm的参数
  3. 定义好提交表单的验证函数,官网例子中为submitForm函数和resetForm函数
<template>
  <div id="validator-form">
<!-- 以此为例 为表单对象设置ref以便后续获取该节点对象 
    并为表单对象添加ruleForm和rules的单向数据绑定 此外给姓名参数的item添加name的prop
    以及v-model数据双向绑定ruleForm对应名的参数-->
    <el-form
      ref="ruleFormRef"  
      :model="ruleForm"
      :rules="rules"
      label-width="520px"
      class="demo-ruleForm"
      :size="formSize"
      status-icon
    >
        <el-form-item label="姓名" prop="name">
        <el-input v-model="ruleForm.name" placeholder="请输入名字"/>
      </el-form-item>
...
 </el-form>
  </div>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
// 引入类型
import type { FormInstance, FormRules } from "element-plus";
const formSize = ref("default");
// 获取表单对象并设置为FormInstance类型
const ruleFormRef = ref<FormInstance>();
// 定义包含表单内各参数的响应式数据对象 用于保存表单参数
const ruleForm = reactive({
  name: "",
  sex: "",
  height:"",
  weight:"",
  phone: "",
  time: "",
  email: "",
  birthday:""
});
// 定义包含所有参数规则的rules常量并设置为FormRules类型
const rules = reactive<FormRules>({...})
...
// 表单校验函数
const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
        // 校验成功
      console.log("submit!");
    } else {
        // 校验失败
      console.log("error submit!", fields);
    }
  });
};
//清除校验效果并且清空表单参数的函数
const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
};
</script>

该案例个人代码如下:

<template>
  <div id="validator-form">
    <!-- 以此为例 为表单对象设置ref以便后续获取该节点对象 -->
    <el-form
      ref="ruleFormRef"  
      :model="ruleForm"
      :rules="rules"
      label-width="520px"
      class="demo-ruleForm"
      :size="formSize"
      status-icon
    >
      <el-form-item label="姓名" prop="name">
        <el-input v-model="ruleForm.name" placeholder="请输入名字"/>
      </el-form-item>
      <el-form-item label="性别" prop="sex">
        <el-select v-model="ruleForm.sex" placeholder="请选择性别">
          <el-option label="男" value="male" />
          <el-option label="女" value="female" />
        </el-select>
      </el-form-item>
      <el-form-item label="身高(cm)" prop="height">
        <el-input v-model.number="ruleForm.height" placeholder="请输入身高"/>
      </el-form-item>
      <el-form-item label="体重(kg)" prop="weight">
        <el-input v-model="ruleForm.weight" placeholder="请输入体重"/>
      </el-form-item>
      <el-form-item label="手机" prop="phone">
        <el-input v-model.number="ruleForm.phone" placeholder="请输入手机号码"/>
      </el-form-item>
      <el-form-item label="邮箱" prop="email">
        <el-input v-model="ruleForm.email" placeholder="请输入邮箱"/>
      </el-form-item>
      <el-form-item label="出生日期" required>
        <el-form-item prop="birthday">
          <el-date-picker
            v-model="ruleForm.birthday"
            type="date"
            label="Pick a date"
            placeholder="请选择出生日期"
            style="width: 100%"
          />
        </el-form-item>
    </el-form-item>


      <el-form-item>
        <el-button type="primary" @click="submitForm(ruleFormRef)"
          >提交</el-button
        >
        <el-button @click="resetForm(ruleFormRef)">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script lang="ts" setup>
import { reactive, ref } from "vue";
// 引入类型
import type { FormInstance, FormRules } from "element-plus";

const formSize = ref("default");
// 获取表单对象并设置为FormInstance类型
const ruleFormRef = ref<FormInstance>();
// 包含表单内各参数的响应式数据对象 用于保存表单参数
const ruleForm = reactive({
  name: "",
  sex: "",
  height:"",
  weight:"",
  phone: "",
  time: "",
  email: "",
  birthday:""
});
// 定义包含所有参数规则的rules常量并设置为FormRules类型
const rules = reactive<FormRules>({
  name: [
    { required: true,type:'string', message: "姓名不能为空", trigger: "blur" },
    { pattern:/[\u4e00-\u9fa5]/,min: 2, max: 15,transform(value){return value.trim()}, message: "姓名格式错误", trigger: "blur" },
  ],
  sex: [
    { required: true, message: "性别不能为空", trigger: "change" },
  ],
  height: [
    { required: true, message: "身高不能为空", trigger: "blur" },
    { min: 1, max: 300,type:"number", message: "身高范围为1~300", trigger: "blur" },
  ],
  weight: [
    { required: true, message: "体重不能为空", trigger: "blur" },
    { min: 2, max: 15, message: "体重范围为1~300", trigger: "blur" },
  ],
  phone: [
    { required: true, message: "手机不能为空", trigger: "blur" },
    { pattern:/^1[3|4|5|8|9]{1}[0-9]{9}$/,min: 2, max: 15, message: "手机格式错误", trigger: "blur" },
  ],
  email: [
    { required: true, message: "邮箱不能为空", trigger: "blur" },
    { pattern:/^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,min: 2, max: 15, message: "邮箱格式错误", trigger: "blur" },
  ],
  birthday: [
    { required: true, message: "出生日期不能为空", trigger: "change" },
  ],
});

const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log("submit!");
    } else {
      console.log("error submit!", fields);
    }
  });
};

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
};
</script>
<style lang="less">
#validator-form {
  margin-top: 200px;
  width: 1500px;
  height: 900px;
  button{
    margin-top: 50px;
  }
  div{
    label{
      margin-top: 20px;
    }
    div{
      margin-top: 5px;
    }
  }
}
</style>

 

 表单校验的重点:定义规则

1.input输入框原生自带的type规则约束

例如type="password",原生自动的type类型可以参考这个:<input>:输入(表单输入)元素 - HTML(超文本标记语言) | MDN

2.根据不同参数指定对应的自定义规则

举一个简单的例子:

const rules = reactive<FormRules>({
  name: [
    { required: true,type:'string', message: "姓名不能为空", trigger: "blur" },
    { pattern:/[\u4e00-\u9fa5]/,min: 2, max: 15, message: "姓名格式错误", trigger: "blur" },
  ],

    // 也可以写出这种形式 需要定义validatename函数
    //name: [{ validator: validateName, trigger: 'blur' }],
    ...
})

/*
const validateName = (rule: any, value: any, callback: any) => {
  if (value === '') {
    callback(new Error('Please input the name'))
  } else {
    if (ruleForm.name !== '') {
      if (!ruleFormRef.value) return
      ruleFormRef.value.validateField('checkPass', () => null)
    }
    callback()
  }
}  */

required:是否为必填项,如不设置,则会根据校验规则确认。

type的类型与原生input的type类型不同,它限制对应参数的类型,其可选项有如下:

string必须是类型string
number 必须是类型number
boolean必须是类型boolean
method必须是类型function
regexp必须是RegExp创建新的时不产生异常的实例或字符串RegExp
integer必须是类型number和整数
float必须是类型number和浮点数
array必须是由 确定的数组Array.isArray
object必须是 typeobject而不是Array.isArray
enum值必须存在于enum
date值必须是有效的Date
url必须是类型url
hex必须是类型hex
email必须是类型email
any可以是任何类型

但是仅仅靠type的限制是远远无法满足业务需求的,因此

pattern的存在就显得尤为重要,它代表着rule 属性指示值必须匹配才能通过验证的正则表达式。

通过正则表达式我们可以实现对手机号码、邮箱等各项数据的合法性进行最基本的正确性校验。

3.几个小小的注意点

  • 要验证字段的确切长度,请指定len属性。如果该len属性与minmax范围属性结合使用,len则优先。

  • 使用min和max属性定义范围时,对于string和array类型是针对其length, 对应number类型则要求它的值不能小于min或大于max

  • 此外如果需要验证深层对象属性,还可以通过将嵌套规则分配给规则的属性来验证属于object或类型的规则。

另一种的表单验证:async-validator

其基本用法是:定义描述符,将其分配给模式并将要验证的对象和回调函数传递给validate模式。

与上述的方法在规则限定上大体相识,使用前记得先install async-validator,其他啥也别说,先上例子:

import Schema from 'async-validator';
const descriptor = {
  name: {
    type: 'string',
    required: true,
    validator: (rule, value) => value === 'muji',
  },
  age: {
    type: 'number',
    asyncValidator: (rule, value) => {
      return new Promise((resolve, reject) => {
        if (value < 18) {
          reject('too young');  // reject with error message
        } else {
          resolve();
        }
      });
    },
  },
};
const validator = new Schema(descriptor);
validator.validate({ name: 'muji' }, (errors, fields) => {
  if (errors) {
    // validation failed, errors is an array of all errors
    // fields is an object keyed by field name with an array of
    // errors per field
    return handleErrors(errors, fields);
  }
  // validation passed
});

// PROMISE USAGE
validator.validate({ name: 'muji', age: 16 }).then(() => {
  // validation passed or without error message
}).catch(({ errors, fields }) => {
  return handleErrors(errors, fields);

除了可以像常规方法以对象的形式那般使用type、pattern正则表达式限制,它还可以以函数的形式使用,代码如下:

import Schema from 'async-validator';
const descriptor = {
  name(rule, value, callback, source, options) {
    const errors = [];
    if (!/^[a-z0-9]+$/.test(value)) {
      errors.push(new Error(
        util.format('%s must be lowercase alphanumeric characters', rule.field),
      ));
    }
    return errors;
  },
};
const validator = new Schema(descriptor);
validator.validate({ name: 'Firstname' }, (errors, fields) => {
  if (errors) {
    return handleErrors(errors, fields);
  }
  // validation passed
});

如果业务十分复杂,有多重验证的话,使用这个方法会更合适,通过官方文档给出的example可以清晰看到该方法可以使规则成为对象数组,代码如下:

const descriptor = {
  email: [
    { type: 'string', required: true, pattern: Schema.pattern.email },
    { 
      validator(rule, value, callback, source, options) {
        const errors = [];
        // test if email address already exists in a database
        // and add a validation error to the errors array if it does
        return errors;
      },
    },
  ],
};

官方文档地址:GitHub - yiminghe/async-validator: validate form asynchronous

个人总结:

本次项目中做表单验证的过程,我发现官方文档永远是咋们最可靠的帮手,看懂官方文档真的基本可以解决遇到的绝大部分问题。

最后,奉上我最近比较喜欢的一句话,从不胜利的人很少失败,从不攀登的人很少跌倒。希望大家都不畏失败,不怕跌倒。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐