老师,关于min和max的校验,希望能斧正下

来源:5-7 ValidateForm 组件需求分析

希望林柚一健康快乐成长

2022-03-25

export type ValidationKey = 'email' | 'phone' | 'required' | 'range'
interface RuleProp {
  type: ValidationKey;
  message?: string;
  min?: { message: string, length:number};
  max?: { message: string, length:number};
}
interface ResObj {
  isValid: boolean;
  message: string
}
type Validation = Record<ValidationKey, (param: string, rule: Array<RuleProp>) => ResObj>
// Record是ts中高级类型,可以理解为,定义了一系列的对象,对象的属性值为validtaionKey的值,而属性值则是第二个参数决定的,它可以是对象,联合类型,枚举,函数等等,在这个案例中,他是一个函数,
// 函数有一个参数值是字符串类型,该函数会返回一个布尔值
// 邮箱的正则
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
const phoneReg = /^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[0-35-9]\d{2}|4(?:0\d|1[0-2]|9\d))|9[0-35-9]\d{2}|6[2567]\d{2}|4(?:(?:10|4[01])\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/
// 这个地方他导出的就是一个对象,属性为可以,属性值为方法体
export const Validations:Validation = {
  email (str, rule) {
    const result = { isValid: true, message: '' }
    for (let i = 0; i < rule.length; i++) {
      if (rule[i].type === 'email') {
        result.isValid = emailReg.test(str)
        result.message = rule[i].message as string
        break
      }
    }
    return result
  },
  phone (str, rule) {
    const result = { isValid: true, message: '' }
    for (let i = 0; i < rule.length; i++) {
      if (rule[i].type === 'required') {
        result.isValid = phoneReg.test(str)
        result.message = rule[i].message as string
        break
      }
    }
    return result
  },
  required (str, rule) {
    const result = { isValid: true, message: '' }
    for (let i = 0; i < rule.length; i++) {
      if (rule[i].type === 'required') {
        result.isValid = !!str.trim()
        result.message = rule[i].message as string
        break
      }
    }
    return result
  },
  range (str: string, rule) {
    const newstr = str.trim()
    const result = { isValid: true, message: '' }
    for (let i = 0; i < rule.length; i++) {
      if (rule[i].type === 'range') {
        if (rule[i]?.min) {
          const nummin = rule[i]?.min?.length as number
          if (newstr.length < nummin) {
            result.isValid = false
            result.message = rule[i]?.min?.message as string
            break
          }
        }
        if (rule[i]?.max) {
          const nummax = rule[i]?.max?.length as number
          if (newstr.length > nummax) {
            result.isValid = false
            result.message = rule[i]?.max?.message as string
            break
          }
        }
      }
    }
    return result
  }
}

老师,上面是我将校验规则单独抽离的ts文件,增加了range校验规则

在ValidateInput.vue中,如下定义

<template>
  <div class="validata-input-container pb-3">
    <input
      v-bind="$attrs"
      :class="{'is-invalid': inputRef.error}"
      class="form-control"
      id="exampleInputEmail1"
      :value="inputRef.val"
      @input="updateValue"
      @blur="validateInput">
      <span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, PropType, onMounted, renderSlot } from 'vue'
import { ValidationKey, Validations } from '../hooks/validation'
import { emitter } from './validateForm.vue'
interface RuleProp {
  // ValidationKey是一种类型,可以导入导出使用,那么这里的type就限定了3中,只能是邮箱,手机和必填
  type: ValidationKey;
  message?: string;
  min?: { message: string, length:number};
  max?: { message: string, length:number};
}
// export type RulesProp = RuleProp[],这句话的意思是type是类型别名,这句话的意思就是,创建一个类型,这个类型是个数组类型,每个数组成员是RuleProp类型的数据
// export 只是一个关键字,在ts中,类型可以导出和导入,代码实现也可以导出和到入
export type RulesProp = RuleProp[]
export default defineComponent({
  props: {
    rules: Array as PropType<RulesProp>,
    modelValue: String
  },
  inheritAttrs: false,
  setup (props, context) {
    // 需要判断用户有没有传入默认的初始值,如果传入了需要显示在页面上
    const inputRef = reactive({
      val: props.modelValue || '',
      error: false,
      message: ''
    })
    const updateValue = (e: KeyboardEvent) => {
      // 修改组件的值,实现双向数据绑定
      const targetValue = (e.target as HTMLInputElement).value
      inputRef.val = targetValue
      context.emit('update:modelValue', targetValue)
    }
    const validateInput = () => {
      // 首先判读是否有rules规则
      if (props.rules) {
        // 判断表单所有的是否都通过校验
        for (const rule of props.rules) {
          const resultObj = Validations[rule.type](inputRef.val, props.rules)
          if (!resultObj.isValid) {
            inputRef.error = true
            inputRef.message = resultObj.message
            return false
          }
        }
        inputRef.error = false
      }
    }
    onMounted(() => {
      emitter.emit('form-item-created', inputRef.val)
    })
    return {
      inputRef,
      updateValue,
      validateInput
    }
  }
})
</script>

我传入的规则校验是

    // 密码的校验规则
    const passwordRules: RulesProp = [
      { type: 'required', message: '密码不能为空' },
      { type: 'range', min: { message: '你的密码至少包括六位,不能含有空格', length: 6 }, max: { message: '你的密码至多包括10位,不能含有空格', length: 10 } }
    ]

但是这样会有一个问题,当密码的校验规则既有min也有max的时候,按照我的写法,他永远只会提示,当前密码长度不能小于6个,当超过10个的时候也是提示这个,因为我在动态判断inputRef.message的时候,用的是三元表达式,这样肯定不对的,我需要一个标识,来判断当前到底是min没有通过,还是max没有通过
我的想法是,range方法返回一个对象类似于{ type :‘min’, flag: true},然后在失焦方法触发的时候,去判断这个类型是啥,然后动态设置inputRef.message的信息,但是会遇到问题,希望老师提供一个解决思路

第二个问题,定义rules类型的时候,因为message变成了一个可选参数,inputRef.message = rule.message ? rule.message : ‘’,这里如果直接赋值会报错,rules上不存在message属性,所有这里我使用了三元表达式,不知道这样写合不合乎规范

写回答

2回答

张轩

2022-03-29

同学你好

我发现你遇到的大多数都是 ts 问题,而且你学习的很认真,给你点个赞。这里面你遇到的大多数都是类型问题。我这里简单作答一下。

第一:这个文件不应该放到 hooks 当中,它不是一个 hook

关于 ts,这里有个重要的概念是 type guard,类型保护,它会自动缩小范围,你很多问题都是这样出现的。可以使用if 判断将联合类型中的一部分类型排除出去。https://www.typescripttutorial.net/typescript-tutorial/typescript-type-guards/ 

给你完整的改了一遍,测试完毕,你自己酌情看看,解释了你的疑问,可以好好看下。提交在:

https://gitee.com/wang_shuangqin/those-who-know-also/commit/60fc6b1fd57661db6482ff4dc8a665597b50d7cf



0
1
希望林柚一健康快乐成长
很感谢老师,看了之后,获益匪浅
2022-03-29
共1条回复

张轩

2022-03-26

同学你好

写的非常认真,很好,给你点个赞,关于你的问题:我认为可以简单这样修改一下,没有测试,只是提供一个思路

  range (str: string, min?:minMax, max?: minMax) {
    // 初始化一个最终结果
    const result = { isValid: true, message: '' }
    const newstr = str.trim()
    if (min) {
      // 只可能通过一条条件,或大或小
      if (newstr.length < min.length) {
        result.isValid = false
        result.message = min.message
      }
    }
    if (max) {
      if (newstr.length > max.length) {
        result.isValid = false
        result.message = max.message        
      }
    }
    // 返回这个结果,用 isValid 判断是否通过,用 message 显示错误信息
    return result
  }



0
3
希望林柚一健康快乐成长
回复
张轩
https://gitee.com/wang_shuangqin/those-who-know-also/invite_link?invite=3fa1dd81fbc2a1346989a4339f36cbbf0b3bbb1a57c4802ab7993663152babc55e6dd44c087a1f425f318cd36bbddc3a,老师,您加入就能看哈,谢谢老师
2022-03-28
共3条回复

Vue3 + TS 仿知乎专栏企业级项目

带你完成前后端分离复杂项目,率先掌握 vue3 造轮子技能

3142 学习 · 2313 问题

查看课程