项目场景:
页面由三个tabs切换的表单数据,包括各种计算逻辑等复杂的交互,但是三个表单数据需要同时填写完成后,一并提交,使用到component动态组件。
问题描述
提交按钮在父组件也就是和component同级,无法拿到每个组件的表单验证结果
页面部分代码:
<component
:is="element[current].type"
:addEditInfo="dataInfo"
:key="current"
@ruleFormRefValidateResult="ruleFormRefValidateResult"
ref="componentRuleFormRef"
></component>
<Button @click="handleBack" class="mr10" type="default">返回</Button>
<Button @click="handleSave" :loading="draftLoading" class="mr10" type="primary" ghost>保存</Button>
原因分析:
由于页面其他组件已经被销毁,ref再去获取的时候就无法得到除了当前显示的其余组件内部方法包括表单验证数据等~
尝试解决过程:
尝试了不用component,把每个组件写到页面上,用v-show来控制对应的显示与隐藏,结果是:由于v-show是通过css的方式来进行显示隐藏切换,这样导致页面tabs切换的时候始终显示的是第一个组件,其他组件无法正常切换显示。这时候可能有小伙伴会说,v-if呗,v-if确实能解决页面显示隐藏的正常切换,但。。。这又回到了最初的问题,页面始终只会有一个组件未被销毁。
尝试另一种方案:外层套一个form包裹,内部使用formitem, 依然会有切换时已被销毁掉的数据拿不到的情况
最终解决方案:
1.先在component组件初始化每个组件的验证结果都为false
// 初始每个子组件的验证结果,如果不是必填的表单默认值可以直接为true
const childFormResult = ref({
infoValidResult: false,
leaseAgreementValidResult: false,
feeItemsValidResult: false,
});
2.在每个组件命名一个相同的方法,并在里面处理每个页面的最终逻辑和数据,包括表单的验证结果等
某一组件示例:
const exposeFormRefValidateResult = () => {
ruleFormRef.value.validate(valid => {
emits('ruleFormRefValidateResult', {
validResult: valid,//表单验证结果
addEditInfo,//这是页面数据
typeKey: 'leaseAgreementValidResult',//对应组件的标识
});
});
};
defineExpose({
exposeFormRefValidateResult,
});
3.component组件切换tabs时拿到exposeFormRefValidateResult方法,并处理逻辑,便于后面提交使用
/**
* @description: 切换tab
* @param key
*/
const changeTab = (key: string) => {
componentRuleFormRef.value.exposeFormRefValidateResult();
};
/**
* @description: 接收子组件数据
* @param result
*/
const ruleFormRefValidateResult = (result) => {
const { typeKey, validResult, addEditInfo } = result;
childFormResult.value[typeKey] = validResult;//赋值组件的验证结果
//addEditInfo是每个组件页面的表单数据,根据实际情况看是否需要
};
4.提交
const handleSave = async () => {
componentRuleFormRef.value.exposeFormRefValidateResult();
const allResult = Object.values(childFormResult.value).every(function (value) {
return value === true;
});
if (allResult) {
//全部表单数据都通过啦~~
//可以发起请求...
} else {
if (!childFormResult.value.infoValidResult) return showMessage('根据实际业务提示语!', 'error');
};
到这儿就完美搞定啦~