来看看 element-ui 中 form 表单 的验证
前言
element-ui 这个 ui 库许多前端开发都用过, 它的口号的是 网站快速成型工具 上周在使用的过程中,
form
组件配置了所需的校验配置项,但是validate
校验一直失败,今天来深入了解一下
首先来看,form
在 element-ui
中的用法,更多用法👉 element-ui#form
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
>
<el-form-item label="Password" prop="pass">
<el-input v-model="ruleForm.pass" type="password" autocomplete="off" />
</el-form-item>
// 提交按钮
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)">
Create
</el-button>
</el-form-item>
</el-form>
<script lang="ts" setup>
const ruleFormRef = ref<FormInstance>()
// 数据源
const ruleForm = reactive({
name: 'Hello',
})
// 定义校验规则
const rules = reactive<FormRules>({
pass: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
]
})
// 执行校验
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)
return false
}
})
}
</script>
在上述代码中 :
el-form
中接收配置 model
,rules
属性
el-form-item
接收 prop
属性,这个属性必须存在于 rules
中
el-input
中使用 v-model
连接 model
中的某一个属性
在校验的时候,使用的 el-form
中的 validate
方法
起步
在执行上述代码 validate
的这个地方 打一个 debugger
, 来大致过一下流程
- 首先来到
form.vue
文件,在form.vue
文件中执行validate
方法,接收一个callback
,然后去执行了validateField(undefined, callback)
(🔥 注意: 第一个参数是 undefined,第二个才是callback)
form.vue
- 来到
validateField
函数。其中的第一个参数modelProps
(默认值[]
)(刚才的 undefined 被重置为[]
), 第二个参数是我们熟悉的callback
函数
函数内部 执行 doValidateField
方法,把我们刚才的 modelProps
作为入参传入,返回值 result
作为 callback
的实参传入, 🔥 也就是说,这个 result
决定 validate
是否校验成功
这个就是大致的整体流程,validate
--> validateField
--> doValidateField
核心就是在 doValidateField
里面, doValidateField
的返回结果决定着校验的结果
🔥 el-form 辅助校验
1. doValidateField 起点
依然是接收一个参数props
,对于我们的简单例子来说,是一个空数组
内部的 obtainValidateFields
方法接收了 props
obtainValidateFields
返回一个 fields 数组
对 fields
进行判断
- 如果是空,直接返回 true
- 如果不为空,则进行校验,把错误信息收集到
validationErrors
对象上,如果没有错误,也是返回true
,否则就是返回Promise.reject
内部方法obtainValidateFields
返回的 fields
是一个核心方法,接着看它做了什么
2. obtainValidateFields
可以看到,这个方法拿着 props
走进 filterFields
方法中,但是出现了一个新的变量 fields
,这个是从哪里来的呢?🧐
不得不提一下 fileds
,因为这个属性很重要,我们稍微偏移下轨道🚆
- fileds 的身世
el-form
const fields: FormItemContext[] = []
const addField: FormContext['addField'] = (field) => {
fields.push(field)
}
ok, 在 el-form
中定义了 addField
方法,但是它自己却没有用到,看到这里,想必有经验的同学已经猜到了,这个方式是为了收集子组件(也可能不是直接子组件) el-form-item
中的属性
果不其然,在 form.vue
文件最后,使用了 provide
方法把 addField
提供了出去👮♀️ ( 这个地方也解释了formEl.resetFields()
的来源 )
👇 是 provide 提供的部分方法
provide(formContextKey,reactive({
resetFields,
clearValidate,
validateField,
addField,
}))
既然知道了el-form-item
要使用,那么来到el-form-item
文件,(为了方便理解,只保留了重要属性)
el-form-item
const formContext = inject(formContextKey, undefined)
const context: FormItemContext = reactive({
// ....
resetField,
clearValidate,
validate,
})
onMounted(() => {
if (props.prop) {
formContext?.addField(context)
}
})
addField
把 context
属性在 onMounted
的时候全部给拿走了
ok,这个就是 fields
的前世今生,fields
就是一个一个 el-form-item
中的属性方法组成的数组集合,明白了这个,我们回到正轨🚆
在上文的 ### obtainValidateFields
方法中,用到了一个工具函数 filterFields
filterFields
这个不在 form.vue
文件中,在同级目录 utils
文件
uitls.ts
由于props
是一个 []
,那么 normalized
也是一个空数组,所以,filterFields
在这种情况下返回的就是fields
数组
👇图 为 fields
数组
🚀 校验
绕了一大圈,又回到了这里来,是不是已经忘了这个方法,没关系,我们来回看一下,(element-puls
调用链是挺长的)
obtainValidateFields
结束了,返回一个 fields
, 进行遍历 fields
,使用 try catch
包裹捕获错误
这个是校验的代码,执行 filed(即form-item)
中的 validate
方法
每一个 form-item
单独执行自己的校验,然后结果由 el-form
捕获
ok, 来看看 form-item
的 validate
方法
🔥🔥 el-form-item 核心校验
el-form-item
validate
const validate: FormItemContext['validate'] = async (trigger, callback) => {
const rules = getFilteredRule(trigger)
console.log("🚀~ rules:", rules);
const hasCallback = isFunction(callback)
//...
return doValidate(rules)
.then(() => {
callback?.(true)
return true as const
})
.catch((err: FormValidateFailure) => {
const { fields } = err
callback?.(false, fields)
return hasCallback ? false : Promise.reject(fields)
})
}
validate
在 el-from
中只接收了 一个 空字符串''
,那 trigger
就是一个 空字符, callback
被赋值为 undefined
这个 validate
方法在 el-input
中也需要用到,那个时候 的 trigger
要换成 blur
或者 change
事件了
内部方法getFilteredRule
拿着这个 trigger
返回了一个过滤后的 rules
, 最后使用 map
把 trigger
给去掉了
👇为过滤后的 rules
结果, 基本上没有变化,把 trigger:blur
去掉了
上文用到的 getFilteredRule
方法,过滤 rules
, 不做过多描述
doValidate
拿着过滤后的 rules
去做 valide
(不得不说,函数名字起的真好👍)
压力来到了doValidate
方法,它接收过滤后的 rules
,doValidate
是一个 promise
方法,不仅需要在 then
方法中执行 callback(true)
,还需要在 catch
抛出错误
👨🚀 doValidate
重要! 重要! 重要!
是核心的校验方法,为了便于理解, rules 和 modelName 都以注释的形式放上去了,
**使用AsyncValidator
进行校验 rules
和modelName
**
AsyncValidator 是何许人也?
放一段 npm#AsyncValidator 官网上的代码,看完就明白了,el-form
的校验全靠它了
对照着我上文贴的 代码
,Shema
替换成 AsyncValidator
,description
替换成
[modelName]: rules
偏离一下轨道🚆,讲解一下 fieldValue
的来源
记得formContext
的来源吗?
formContext
来自于 el-form
中的 provide
值,model
就是定义在 el-form
的那个大对象,props.prop
是定义在 el-form-item
上的具体属性
当知道了 fieldValue.value
之后,我们 回到正轨 🚆
当 校验成功之后,会走进validator
中的 then 回调,执行 onValidationSucceeded
// 错误消息
const validateMessage = ref('')
// 验证状态
const validateState = ref<FormItemValidateState>('')
const setValidationState = (state: FormItemValidateState) => {
validateState.value = state
}
const onValidationSucceeded = () => {
setValidationState('success')
}
const onValidationFailed = (error: FormValidateFailure) => {
const { errors, fields } = error
if (!errors || !fields) {
console.error(error)
}
validateMessage.value = errors
? errors?.[0]?.message ?? `${props.prop} is required`
: ''
}
onValidationSucceeded 和 onValidationFailed 中 使用 方法setValidationState
设置了不同的校验状态
在 onValidationFailed
中设置了 validateMessage
错误消息
在页面上使用
传递了一个具名的
error 插槽
,同时把validateMessage
给抛出来
以上就是当你按下 开始校验按钮
之后的全部过程
有朋友要问了,你说的是 点击校验按钮
才能校验,那有时候blur / change
也可以执行校验,是为什么呢?
别急,还记得 elf-form-item
中的 validate
方法吗?,也就是 el-form
执行el-form-item
的校验方法,当你按下 校验按钮
的时候, trigger
为 空,但是 input
中的 trigger
就不一样了
input.vue
通过 inject
获取 el-form-item
实例,执行 validate
方法,这样就可以校验啦
总结
el-form
的校验简单来说,是通过 el-form
身上的 model
对象,匹配 el-form-item
的 prop
属性
即 fieldValue
,使用 async-validator
中的实例 validate
拿着 el-form
中传递的 rules
进行 校验
const modelName = prop;
const fieldValue = model[prop]
const validator = new AsyncValidator({
[modelName]: rules,
})
也可以通过 el-input
中执行 el-form-item
的 validate
方法来执行校验
如果有错误,在 el-form-item
中显示
以上就是 el-form
中的关于表单验证总结
转载自:https://juejin.cn/post/7221001498753695805