前端 表单校验的使用和实现

首先来看一下element给出的案例

<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
  <el-form-item label="密码" prop="pass">
    <el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input>
  </el-form-item>
  <el-form-item label="确认密码" prop="checkPass">
    <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input>
  </el-form-item>
  <el-form-item label="年龄" prop="age">
    <el-input v-model.number="ruleForm.age"></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>
<script>
  export default {
    data() {
      var checkAge = (rule, value, callback) => {
        if (!value) {
          return callback(new Error('年龄不能为空'));
        }
        setTimeout(() => {
          if (!Number.isInteger(value)) {
            callback(new Error('请输入数字值'));
          } else {
            if (value < 18) {
              callback(new Error('必须年满18岁'));
            } else {
              callback();
            }
          }
        }, 1000);
      };
      var validatePass = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入密码'));
        } else {
          if (this.ruleForm.checkPass !== '') {
            this.$refs.ruleForm.validateField('checkPass');
          }
          callback();
        }
      };
      var validatePass2 = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请再次输入密码'));
        } else if (value !== this.ruleForm.pass) {
          callback(new Error('两次输入密码不一致!'));
        } else {
          callback();
        }
      };
      return {
        ruleForm: {
          pass: '',
          checkPass: '',
          age: ''
        },
        rules: {
          pass: [
            { validator: validatePass, trigger: 'blur' }
          ],
          checkPass: [
            { validator: validatePass2, trigger: 'blur' }
          ],
          age: [
            { validator: checkAge, 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  el-form-item  el-input

       校验规则在form组件中,具体的值绑定至input,通过操作form节点的.validate方法来实现校验,具体的数据和绑定看下面代码

        

<template>
  <div>
    <WWQBlock class="form">
      <WWQForm ref="formItems" :model="formData" :rules="ruleValidate">
        <WWQFromItem label="姓名 :" prop="value1">
          <WWQInput
            placeholder="请输入内容..."
            inputType="text"
            v-model="formData.value1"
          >
          </WWQInput>
        </WWQFromItem>
        <WWQFromItem label="密码 :" prop="value2">
          <WWQInput
            placeholder="请输入内容"
            inputType="password"
            v-model="formData.value2"
          >
          </WWQInput>
        </WWQFromItem>
        <WWQFromItem label="爱好 :" prop="value3">
          <WWQSelect v-model="formData.value3" :data="selectData"></WWQSelect>
        </WWQFromItem>
        <div></div>
      </WWQForm>
      <WWQBlock>
        <WWQButton
          type="success"
          size="small"
          @click="changeButton"
          radius="10px"
          >提交
        </WWQButton>
      </WWQBlock>
    </WWQBlock>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      formData: {
        value1: "",
        value2: "",
        value3: "",
  },
  ruleValidate: {
        value1: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
        value2: [
          { required: true, message: "放心输入自己的隐私吧!", trigger: "blur" },
          { required: true, min: 0, max: 8, message: "超出限制了...",trigger: "change"     },
    ],
        value3: [
          { required: true, message: "总得有个特长什么的吧?", trigger: "blur" },
    ],
  },
        }
  },
  methods: {
    changeButton() {
      console.log(this.formData);
      this.$refs.formItems
        .validate() // 验证方法
        .then((res) => {
          console.log(res);
        });
    },


  },
};
</script>

可以看到 el-form  el-form-item  el-input 这三个组件时互相嵌套关系,并非简单的父子组件,想要查看只能通过插槽,但是还有一种更灵活的办法,

        那就是 provide 广播 和  inject 派发

首先需要定义一个事件总线,以供全局使用


function broadcast(componentName, eventName, params) {
  // 循环子节点找到名称一样的子节点 否则 递归 当前子节点
  this.$children.map((child) => {
    if (componentName === child.$options.name) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat(params));
    }
  });
}
export default {
  methods: {
    /**
     * 派发 (向上查找) (一个)
     * @param componentName // 需要找的组件的名称
     * @param eventName // 事件名称
     * @param params // 需要传递的参数
     */
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root; //$parent 找到最近的父节点 $root 根节点
      let name = parent.$options.name; // 获取当前组件实例的name
      // 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点
      // 循环出当前名称的一样的组件实例
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.name;
        }
      }
      // 有节点表示当前找到了name一样的实例
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    /**
     * 广播 (向下查找) (广播多个)
     * @param componentName // 需要找的组件的名称
     * @param eventName // 事件名称
     * @param params // 需要传递的参数
     */
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    },
  },
};

在父组件我们这样做

  provide() {
    // 这里相当于把整个组件实例广播出去
    return {
      form: this,
    };
  },

子组件中通过inject获取

 inject: ["form"],

这样一来三个组件可以互相通信,并且彼此更改组件之间的值,

        form组件在接受到rule和model之后干了什么?

<template>
    <form action="">
      <slot></slot>
    </form>
</template>

<script>
export default {
  name: "WWQForm",
  provide() {
    return {
      form: this,
    };
  },
  data() {
    return {
      fields: [], // 储存当前的 form-item的实例
    };
  },
  created() {
    let that = this;
    this.$on("on-form-item-add", (item) => {
      if (item) {
        that.fields.push(item);
      }
    });
    this.$on("on-form-item-remove", (item) => {
      if (item.prop) {
        that.fields.splice(that.fields.indexOf(item), 1);
      }
    });
  },
  methods: {
    resetFields() {
      this.fields.forEach((field) => {
        field.resetField();
      });
    },
    validate(callback) {
      return new Promise((resolve) => {
        let valid = true; // 默认是通过
        let count = 0; // 来匹配当前是否是全部检查完
        this.fields.forEach((field) => {
          // 每个实例都会有 validation 的校验的方法
          field.validation("", (error) => {
            if (error) {
              valid = false;
            }
            // 通过当前检查完所有的form-item的时候才会调用
            if (++count === this.fields.length) {
              resolve(valid); // 方法使用then
              if (typeof callback === "function") {
                callback(valid); // 直接调用注入的回调方法
              } }
          });
        });
      }); }, },};

可以看到该组件广播了两个事件,并且定义了验证方法,遍历逐一校验表单项,

        form-item做了啥?

<template>
  <div class="wwq-form-item">
    <label class="wwq-label" v-if="label" for="">
      {{ label }}
    </label>
    <slot></slot>
    <Transition name="tip">
      <span class="wwq-form-item-message" v-if="validateState === 'error'">
        {{ validateMessage }}
      </span>
    </Transition>
  </div>
</template>

<script>
import Emitter from "../../emitter";
import schema from "async-validator";
export default {
  name: "WWQFromItem",
  mixins: [Emitter],
  inject: ["form"],
  props: {
    label: String,
    prop: String,
  },
  computed: {
    fieldValue() {
      return this.form.model[this.prop];
    },
  },
  data() {
    return {
      initialValue: "", // 储存默认值
      isRequired: false, // 当前的是否有问题
      validateState: "", // 是否校验成功
      validateMessage: "", // 校验失败文案
    };
  },
  methods: {
    setRules() {
      let that = this;
      let rules = this.getRules(); //拿到父组件过滤后当前需要使用的规则
      if (rules.length) {
        this.isRequired = rules.some((rule) => {
          return rule.required;
        });
      }
      this.$on("on-form-blur", that.onFieldBlur);
      this.$on("on-form-change", that.onFieldChange);
    },
    getRules() {
      let that = this;
      let rules = that.form.rules;
      rules = rules ? rules[that.prop] : [];
      return [].concat(rules || []); //这种写法可以让规则肯定是一个数组的形式
    },
    onFieldBlur() {
      this.validation("blur");
    },
    onFieldChange() {
      this.validation("change");
    },
    getFilteredRule(trigger) {
      let rules = this.getRules();
      // !res.trigger 没有调用方式的时候默认就校验的
      // filter 过滤出当前需要的规则
      return rules.filter(
        (res) => !res.trigger || res.trigger.indexOf(trigger) !== -1
      );
    },
    validation(trigger, callback = function () {}) {
      let rules = this.getFilteredRule(trigger);
      // 判断当前是否有规则
      if (!rules || rules.length === 0) {
        return;
      }
      this.validateState = "validating";
      var validator = new schema({ [this.prop]: rules });
      // firstFields: true 只会校验一个
      validator.validate(
        { [this.prop]: this.fieldValue },
        { firstFields: true },
        (errors) => {
          this.validateState = !errors ? "success" : "error";
          this.validateMessage = errors ? errors[0].message : "";
          callback(this.validateMessage);
        }
      );
    },
    resetField() {
      this.form.model[this.prop] = this.initialValue;
    },
  },
  // 组件渲染时,将实例缓存在 Form 中
  mounted() {
    // 如果没有传入 prop,则无需校验,也就无需缓存
    if (this.prop) {
      this.dispatch("WWQForm", "on-form-item-add", this);
      // 设置初始值,以便在重置时恢复默认值
      this.initialValue = this.fieldValue;
      // 添加表单校验
      this.setRules();
    }
  },
  // 组件销毁前,将实例从 Form 的缓存中移除
  beforeDestroy() {
    this.dispatch("WWQForm", "on-form-item-remove", this);
  },
};
</script>

通过自身接受的字段从父级获取表单绑定的value,并广播交互事件

input组件做的事

<template>
  <div class="wwq-input">
    <input
      :type="inputType"
      :value="defaultValue"
      @input="handleInput"
      @blur="handleBlur"
      :placeholder="placeholder"
    />
  </div>
</template>

<script>
import Emitter from "../../emitter";
export default {
  name: "WWQInput",
  mixins: [Emitter],
  data() {
    return {
      defaultValue: this.inputVal,
    };
  },
  props: {
    inputType: {
      type: String,
      default: "text",
    },
    value: String,
    placeholder: String,
  },
  watch: {
    value(val) {
      this.defaultValue = val;
    },
  },
  methods: {
    handleInput(event) {
      this.defaultValue = event.target.value;
      this.$emit("input", event.target.value);
      // 将当前的值发送到 aiFormItem 进行校验
      this.dispatch("WWQFromItem", "on-form-change", event.target.value);
    },

    handleBlur(event) {
      // vue 原生的方法 return 出去
      this.$emit("blur", event.target.value);
      // 将当前的值发送到 aiFormItem 进行校验
      this.dispatch("WWQFromItem", "on-form-blur", event.target.value);
    },
  },
};
</script>

表单交互事件派发给 form-item 组件,改变绑定value的同时触发校验

校验失败的话 form-item 会弹出警告信息。

注意:

        绑定至父级节点form的rule一定要符合规范~

  ruleValidate: {
    value1: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
    value2: [
      { required: true, message: "放心输入自己的隐私吧!", trigger: "blur" },
      { required: true, min: 0, max: 8, message: "超出限制了...",trigger: "change" },
    ],
    value3: [
      { required: true, message: "总得有个特长什么的吧?", trigger: "blur" },
    ],
  },

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值