vue-vben-admin
vue-vben-admin copied to clipboard
useFormItem.ts nextTick 方法导致dynamicRules第一次的值永远都不对
⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭
- [ x ] 已阅读 文档.
- [ x ] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复)
- [ x ] 已在 Issues 中搜索了相关的关键词
- [ x ] 不是 ant design vue 组件库的 Bug
描述 Bug
问题1: 使用 ApiSelect 、 ApiRadioGroup、原生的Select、 RadioGroup 等组件, rules的 trigger 设置为 'change', 每次改变选中的参数,会触发2次 validator。 1465
问题2: ApiSelect 或 ApiRadioGroup , 触发2次,因此表单校验(dynamicRules)就会执行2次,第一次执行dynamicRules函数,value和 model.xxx 的值为 上一次 选中的参数value (导致表单验证报错), 第二次执行dynamicRules函数,value和 model.xxx 的值是正确的。 (第二次执行dynamicRules函数传入的值正确,但并不会覆盖第一次的校验结果)
问题2已经在下方提供解决方案,但不知道为何2021年6月24日 作者为何原因加了 nextTick 方法, 我测试过:去掉nextTick后,问题2能解决。
伪代码:
field: 'resourceType',
component: 'ApiSelect',
// component: 'ApiRadioGroup',
componentProps: {
api: asyncFindDictList,
params: { type, excludes },
resultField: 'data',
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0;
},
},
dynamicRules: ({ model }) => {
return [
{
trigger: 'change', // trigger: 'change' 每次改变select的参数,会触发2次 validator
// trigger: 'blur', // trigger: 'blur' 每次改变select的参数,只会触发一次 validator
validator: async (_, value, ddd) => {
console.log(value); // useRuleFormItem方法中,使用 nextTick 会导致第一次的value值不对 ,第二次才对
console.log(model.resourceType );
}
}
]
}
复现 Bug
- 在表单使用 ApiTree 或 ApiRadioGroup
- 然后写dynamicRules校验表单
如何解决?
// 正确的写法
emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || []));
// 错误的写法
// nextTick(() => {
// emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || []));
// });
系统信息
- 操作系统: window 和 mac
- Node 版本: 16
- 包管理器 (npm/yarn/pnpm) 及其版本: 6.23
这么写还会出现别的问题
加这个是为了emitdata能接受到
可以看下pr1595,是不是和你描述的类似? 我去掉了useRuleFormItem,目前没有找到合适的方案,如果有请告知
这么写还会出现别的问题
还会出现什么问题呀?
可以看下pr1595,是不是和你描述的类似? 我去掉了useRuleFormItem,目前没有找到合适的方案,如果有请告知
按你pr的代码改动之后,确实能解决问题
emitdata
这么写还会出现别的问题
还会出现什么问题呀?
明白了。不加 nextTick 会导致ApiSelect的onChange事件无法获取正确的 option 参数
onChange: function(value, option:Option/Array<Option>) {
}
是的 为了避免破坏性更新,目前我只能这么改了,但是作者并没有合并,也没有改这里bug,可能这个方案还是不行
经过讨论和研究,发现 问题1 触发2次验证是由于 BasicFrom.vue 中如下代码导致:
function setFormModel(key: string, value: any) {
formModel[key] = value;
const { validateTrigger } = unref(getBindValue);
if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {}); // 这行代码导出触发2次表单验证
}
}
不知 @mynetfan 为何加此代码?
可以看下pr1595,是不是和你描述的类似? 我去掉了useRuleFormItem,目前没有找到合适的方案,如果有请告知
建议useRuleFormItem只保留emit(update:${key}
)事件, emit('change')放到组件的handleChange方法里 ; 建议去掉组件handleChange方法获得emitData,因为antd组件的emit('update:value')方法一般先于$emit('change');
只能坐等各位解决------后端开发者
可以看下pr1595,是不是和你描述的类似? 我去掉了useRuleFormItem,目前没有找到合适的方案,如果有请告知
建议useRuleFormItem只保留emit(
update:${key}
)事件, emit('change')放到组件的handleChange方法里 ; 建议去掉组件handleChange方法获得emitData,因为antd组件的emit('update:value')方法一般先于$emit('change');
按照您的说法调整代码后,测试了几种场景,感觉bug已消除 。修改如下:
- ApiSelect.vue 的 handleChange 方法修改为:
function handleChange(value, ...options) {
emitData.value = options;
emit('change', value, ...options);
}
- useFormItem.ts 的 useRuleFormItem 方法修改为:
const state: any = computed({
get() {
return innerState.value;
},
set(value) {
if (isEqual(value, defaultState.value)) return;
innerState.value = value as T[keyof T];
// 注释 下面3行
// nextTick(() => {
// emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || []));
// });
},
});
可以看下pr1595,是不是和你描述的类似? 我去掉了useRuleFormItem,目前没有找到合适的方案,如果有请告知
建议useRuleFormItem只保留emit(
update:${key}
)事件, emit('change')放到组件的handleChange方法里 ; 建议去掉组件handleChange方法获得emitData,因为antd组件的emit('update:value')方法一般先于$emit('change');按照您的说法调整代码后,测试了几种场景,感觉bug已消除 。修改如下:
- ApiSelect.vue 的 handleChange 方法修改为:
function handleChange(value, ...options) { emitData.value = options; emit('change', value, ...options); }
- useFormItem.ts 的 useRuleFormItem 方法修改为:
const state: any = computed({ get() { return innerState.value; }, set(value) { if (isEqual(value, defaultState.value)) return; innerState.value = value as T[keyof T]; // 注释 下面3行 // nextTick(() => { // emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || [])); // }); }, });
@Perfect404 @lzdjack 感兴趣可以试试看
好的
同ApiSelect目录下的几个ApiXXXX都需要修改,暂时好像没发现什么问题
@zuihou @lzdjack
可以看下pr1595,是不是和你描述的类似? 我去掉了useRuleFormItem,目前没有找到合适的方案,如果有请告知
建议useRuleFormItem只保留emit(
update:${key}
)事件, emit('change')放到组件的handleChange方法里 ; 建议去掉组件handleChange方法获得emitData,因为antd组件的emit('update:value')方法一般先于$emit('change');按照您的说法调整代码后,测试了几种场景,感觉bug已消除 。修改如下:
- ApiSelect.vue 的 handleChange 方法修改为:
function handleChange(value, ...options) { emitData.value = options; emit('change', value, ...options); }
- useFormItem.ts 的 useRuleFormItem 方法修改为:
const state: any = computed({ get() { return innerState.value; }, set(value) { if (isEqual(value, defaultState.value)) return; innerState.value = value as T[keyof T]; // 注释 下面3行 // nextTick(() => { // emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || [])); // }); }, });
貌似不需要修改useFormItem.ts也可以
求更新!~
已找到问题根源:由于 antd 的 select option 组件在点击时,会触发 form item 的 onFieldChange,该方法会直接调用 validateRules从而校验该字段,只需要给 form Item 添加 一个 props autoLink 即可解决该问题
const [register ] = useForm({
schemas:[
{
field: 'field1',
label: 'field1',
itemProps:{
autoLink: false, // 添加该属性即可解决多触发一次 validator,value 为空值
},
}
]
})
已找到问题根源:由于 antd 的 select option 组件在点击时,会触发 form item 的 onFieldChange,该方法会直接调用 validateRules从而校验该字段,只需要给 form Item 添加 一个 props autoLink 即可解决该问题
const [register ] = useForm({ schemas:[ { field: 'field1', label: 'field1', itemProps:{ autoLink: false, // 添加该属性即可解决多触发一次 validator,value 为空值 }, } ] })
感谢大佬指导,终于找到问题根源了。
感谢您提出的问题!在我维护这个项目之前,似乎已经有人修复了这个问题并且没有关闭这个issue。由于我无法复现该错误,我相信该问题已经解决。如果您仍然遇到类似的问题,请随时告诉我,我会尽力提供帮助!