Version
Vben Admin V5
Description
字段插槽里的自定义组件如果既没有绑定 componentProps,也没有实现 modelValue(内部用若干控件拼出来的复合组件、第三方组件),表单项拿不到它的值,schema 上的 rules 也就无从校验。
更常见的踩坑是:组件自持一份内部状态、不接收表单下发的值。点「重置」或 setValues 时,表单里的值清了,界面上的选中态还在。antdv 适配器默认把字符串 component 映射成 v-model:value,插槽组件如果用的是标准 modelValue,还要显式写 modelPropName: 'modelValue',否则 props 对不上。
Vant 的 useCustomFieldValue 把这件事收成一套约定:值仍归表单所有,组件只负责 emit 出去;composable 只把取值函数交给外层字段做校验,不自己再存一份。VbenForm 现在没有对等能力。
Proposed Solution
在 @vben-core/form-ui 增加 useCustomFieldValue,由 form-field provide 字段上下文,组件 inject 后注册取值函数:
<script lang="ts" setup>
import { useCustomFieldValue } from '@vben/common-ui';
const modelValue = defineModel<string[]>({ default: () => [] });
const { disabled, error } = useCustomFieldValue(() => modelValue.value);
</script>
<Form>
<template #tags="slotProps">
<TagPicker v-bind="slotProps.componentProps" />
</template>
</Form>
建议约定:
- 值仍归表单所有:组件继续用
modelValue 接收表单下发的值(插槽里就是 v-bind="slotProps.componentProps"),setValues、重置才能顺着 props 流回来
- 取值函数变化时写回字段、清校验,并按
validateOn 触发校验;写回值与表单当前值一致时(重置 / setValues 经组件流回来)不重复写回、也不触发校验,避免一点重置就冒出必填错误
- 同一个表单项只接受一个取值函数,重复注册忽略并告警
- 只有完全自持内部状态、不接受外部值的组件,才需要用返回的
value 自行同步
- schema 里
component 写成字符串、插槽组件用标准 modelValue 时,需要显式 modelPropName: 'modelValue'
已经在 fork 上做了一版,方便对照:
Alternatives Considered
- 每个自定义组件自己
watch(formApi) / 调 setFieldValue:能用,但每个复合控件都要写一套,口径容易漂。
- 强制所有字段插槽必须
v-bind="slotProps.componentProps":对单控件够用,复合组件 / 第三方组件的值仍然落不到表单上,rules 还是校验不了。
- 直接照抄 Vant:Vant 的 composable 只注册 getter、不写回表单。VbenForm 的字段模型需要把值写进 form store 才能走 schema
rules,所以写回是必要的;但写回时要跳过「值和表单已经一致」的那一趟,否则重置会立刻触发校验。
Additional Context
实现贴现有能力:form-field provide 字段上下文,取值函数 watch 后走现有 setFieldValue / validateField。不引入新的 schema 字段类型,也不改变提交数据结构。插槽 componentProps.modelValue 在表单值带索引签名、字段类型解析成 unknown 时放宽为 any,否则 v-bind 喂不进声明了具体 model 类型的组件。
Validations
Version
Vben Admin V5
Description
字段插槽里的自定义组件如果既没有绑定
componentProps,也没有实现modelValue(内部用若干控件拼出来的复合组件、第三方组件),表单项拿不到它的值,schema 上的rules也就无从校验。更常见的踩坑是:组件自持一份内部状态、不接收表单下发的值。点「重置」或
setValues时,表单里的值清了,界面上的选中态还在。antdv 适配器默认把字符串component映射成v-model:value,插槽组件如果用的是标准modelValue,还要显式写modelPropName: 'modelValue',否则 props 对不上。Vant 的
useCustomFieldValue把这件事收成一套约定:值仍归表单所有,组件只负责 emit 出去;composable 只把取值函数交给外层字段做校验,不自己再存一份。VbenForm 现在没有对等能力。Proposed Solution
在
@vben-core/form-ui增加useCustomFieldValue,由form-fieldprovide 字段上下文,组件 inject 后注册取值函数:建议约定:
modelValue接收表单下发的值(插槽里就是v-bind="slotProps.componentProps"),setValues、重置才能顺着 props 流回来validateOn触发校验;写回值与表单当前值一致时(重置 /setValues经组件流回来)不重复写回、也不触发校验,避免一点重置就冒出必填错误value自行同步component写成字符串、插槽组件用标准modelValue时,需要显式modelPropName: 'modelValue'已经在 fork 上做了一版,方便对照:
/examples/form/custom的「自定义取值(slot)」Alternatives Considered
watch(formApi)/ 调setFieldValue:能用,但每个复合控件都要写一套,口径容易漂。v-bind="slotProps.componentProps":对单控件够用,复合组件 / 第三方组件的值仍然落不到表单上,rules还是校验不了。rules,所以写回是必要的;但写回时要跳过「值和表单已经一致」的那一趟,否则重置会立刻触发校验。Additional Context
实现贴现有能力:
form-fieldprovide 字段上下文,取值函数 watch 后走现有setFieldValue/validateField。不引入新的 schema 字段类型,也不改变提交数据结构。插槽componentProps.modelValue在表单值带索引签名、字段类型解析成unknown时放宽为any,否则v-bind喂不进声明了具体 model 类型的组件。Validations