前言
在使用typescript时,所有可以直接输入任意字符串的地方,都可能是被改造的目标。
而element-plus表单中,就有有这么一个。el-form-item 的 prop属性。
如下方代码,prop="name"、prop="detail.job"都可能被输入任意值,导致与rules定义的key不相符而验证功能失效。
<template>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="m-auto my-10">
<el-form-item label="name" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Job" prop="detail.job">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"> Submit </el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue"
import type { FormInstance, FormRules } from "element-plus"
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive({
name: "",
detail: {
job: "",
},
})
const rules = reactive<FormRules<typeof ruleForm>>({
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
"detail.job": [{ required: true, message: "请输入工作", trigger: "blur" }],
})
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
console.log("submit!")
} else {
console.log("error submit!")
}
})
}
</script>
那么怎么改造呢?
看见旁边的v-model就不以输入任意值,因为它直接引用了ruleForm。
那么同理,prop如何引用rules呢?
很容易想到,把rules的key转化为一个新的key映射的object,然后prop再引用这个新object.
1.尝试定义转化函数
function obj2keyObj<T extends object>(
data: T
): { [Key in keyof T]: Key } {
const obj: any = {}
Object.keys(data).forEach((key) => (obj[key] = key))
return obj
}
const rulesKeys=obj2keyObj(rules)
但发现有问题,怎么是可空的,并且没有设置rule的detail,也出现在结果里了。
2.尝试解决可空问题,修改rules定义方式
const rules = reactive({
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
"detail.job": [{ required: true, message: "请输入工作", trigger: "blur" }],
} satisfies FormRules<typeof ruleForm>)
function obj2keyObj<T extends object>(
data: T
): { [Key in keyof T]: Key } {
const obj: any = {}
Object.keys(data).forEach((key) => (obj[key] = key))
return obj
}
const rulesKeys=obj2keyObj(rules)
3.最后在模板里绑定就好
<template>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="m-auto my-10">
<el-form-item label="name" :prop="rulesKeys.name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Job" :prop="rulesKeys['detail.job']">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"> Submit </el-button>
</el-form-item>
</el-form>
</template>
总结
将需要输入rules key的prop,从直接输入字符串,改为引用一个key值object ,而此object由rules派生而来,所有保证了类型安全。
期待一个star
我的github: github.com/leafio
完整代码
<template>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="m-auto my-10">
<el-form-item label="name" :prop="rulesKeys.name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Job" :prop="rulesKeys['detail.job']">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"> Submit </el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue"
import type { FormInstance, FormRules } from "element-plus"
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive({
name: "",
detail: {
job: "",
},
})
const rules = reactive({
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
"detail.job": [{ required: true, message: "请输入工作", trigger: "blur" }],
} satisfies FormRules<typeof ruleForm>)
function obj2keyObj<T extends object>(
data: T
): { [Key in keyof T]: Key } {
const obj: any = {}
Object.keys(data).forEach((key) => (obj[key] = key))
return obj
}
const rulesKeys = obj2keyObj(rules)
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
console.log("submit!")
} else {
console.log("error submit!")
}
})
}
</script>