el-form多个表单共用一个ref,引发的校验问题及解决方案

文章讲述了在使用ElementPlus的Form组件时,由于多个Tab共享同一个ref导致上一个Tab的表单项在切换时仍进行校验的问题。解决方案是为每个Tab创建独立的Formref实例,确保校验仅针对当前激活的Tab。

问题

代码当中有多个tab,会循环渲染多个Form(底层是用elment-plus的Form二次封装的),多个Form共用一个ref,这样导致的问题是tab切换时,上一个tab的表单项还会校验,需求是只校验当前tab的表单项。

function renderForm(tab: string) {
      if (!widgetRef.value![tab]) return null;

      const { props, template } = widgetRef.value![tab];
      const { itemWidth, size, labelWidth, disabled, effects } = props;
      const formProps = {};
      itemWidth && (formProps['item-width'] = itemWidth);
      labelWidth && (formProps['label-width'] = labelWidth);
      size && (formProps['size'] = size);
      disabled !== false && (formProps['disabled'] = disabled);
      return (
        <Form ref={formRef} model={dataRef.value} {...formProps}>
          {template.map((node) => renderComponent(node, effects, tab))}
        </Form>
      );
 }

问题分析

查看element-plus源码后发现,el-form的validate方法会循环校验每个field,返回一个Promise
结合上述问题分析,formRef还绑定的是上一个tab的表单实例,没有更新过来,导致校验的是上一个tab的表单项
在这里插入图片描述

解决方案

function renderForm(tab: string) {
      if (!widgetRef.value![tab]) return null;

      const { props, template } = widgetRef.value![tab];
      const { itemWidth, size, labelWidth, disabled, effects } = props;
      const formProps = {};
      itemWidth && (formProps['item-width'] = itemWidth);
      labelWidth && (formProps['label-width'] = labelWidth);
      size && (formProps['size'] = size);
      disabled !== false && (formProps['disabled'] = disabled);
      // 此处多个Form用不同的ref,ref实例存储存储在一个对象中,解决了tab切换时,上一个tab的表单项还会校验问题
      const ref = [PageTypeEnum.Setting, PageTypeEnum.TenantSetting].includes(pageTypeRef.value)
        ? formRef[tab]
        : formRef;
      return (
        <Form ref={ref} model={dataRef.value} {...formProps}>
          {template.map((node) => renderComponent(node, effects, tab))}
        </Form>
      );
 }
<template> <div class="approveTableDetails" style="width: 96%;height: 94vh;margin: -47vh 0 0 -48%;"> <div class="myResourceTablePopUpTable"> <div class="myResourceTableTitle"> <a class="myResourceTablePopUpTableA" style="width: 94%;margin: 1vh 0 0 3%;">Details</a> <em class="iconfont icon-guanbi" @click="detailsPopupOff" style="right: 3%"></em> </div> <div style="padding: 0 2% 0 0;border-bottom: none;height: 77vh;overflow-y: auto;overflow-x: hidden;"> <div class="approveForm" style="width: 44%;margin: 2vh 2% 0px;padding: 0;border-bottom: none;"> <el-form ref="lineCmbdxDetailsData" label-width="47%" :model="lineCmbdxDetailsData" :rules="lineCmbdxDetailsRules"> <el-form-item label="Link Name"> <el-input v-model="lineCmbdxDetailsData.linkName" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Local Site" prop="localSite"> <el-input v-model="lineCmbdxDetailsData.localSite" placeholder="Please Input Local Site" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Remote Site" prop="remoteSite"> <el-input v-model="lineCmbdxDetailsData.remoteSite" placeholder="Please Input Remote Site" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Bandwidth" prop="bandwidth"> <el-input v-model="lineCmbdxDetailsData.bandwidth" placeholder="Please Input Bandwidth" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Circuit ID" prop="leasedCircuitId"> <el-input v-model="lineCmbdxDetailsData.leasedCircuitId" placeholder="Please Input Circuit ID" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Remote Location"> <el-input v-model="lineCmbdxDetailsData.remoteLocation" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP"> <el-input v-model="lineCmbdxDetailsData.isp" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Remote Address"> <el-input v-model="lineCmbdxDetailsData.leasedRemoteAddress" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Circuit Type"> <el-input v-model="lineCmbdxDetailsData.leasedCircuitType" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Business ID"> <el-input v-model="lineCmbdxDetailsData.leasedBusinessId" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local Location"> <el-input v-model="lineCmbdxDetailsData.bbaLocalLocation" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local Rack No."> <el-input v-model="lineCmbdxDetailsData.bbaLocalRackNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local Unit No."> <el-input v-model="lineCmbdxDetailsData.bbaLocalUnitNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local IP Address(Loopback)"> <el-input v-model="lineCmbdxDetailsData.bbaLocalIpAddressLoopback" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local Shenyang WAN Edge Device"> <el-input v-model="lineCmbdxDetailsData.bbaLocalShenyangWanEdgeDevice" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local to Remote WAN Interface"> <el-input v-model="lineCmbdxDetailsData.bbaLocalToRemoteWanInterface" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Local IP Address"> <el-input v-model="lineCmbdxDetailsData.bbaLocalIpAddress" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Route"> <el-input type="textarea" autosize resize="none" v-model="lineCmbdxDetailsData.route" :disabled="editDisabled"></el-input> </el-form-item> </el-form> </div> <div class="approveForm" style="width: 51%;margin: 2vh 1% 0px 0;padding: 0;border-bottom: none;"> <el-form ref="lineCmbdxDetailsData" label-width="49%" :model="lineCmbdxDetailsData"> <el-form-item label="Update User" v-if="editDisabled"> <el-input v-model="lineCmbdxDetailsData.updateUser" disabled></el-input> </el-form-item> <el-form-item label="Update Time" v-if="editDisabled"> <el-input v-model="lineCmbdxDetailsData.updateTime" disabled></el-input> </el-form-item> <el-form-item label="BBA Remote Site IP Address"> <el-input v-model="lineCmbdxDetailsData.bbaRemoteIpAddress" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Remote Site To Shenyang WAN Interface"> <el-input v-model="lineCmbdxDetailsData.bbaRemoteToShenyangWanInterface" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Remote Site Remote Site WAN Edge Device"> <el-input v-model="lineCmbdxDetailsData.bbaRemoteRemoteSiteWanEdgeDevice" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Remote Site IP Address(Loopback)"> <el-input v-model="lineCmbdxDetailsData.bbaRemoteIpAddressLoopback" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="BBA Remote Site Rack No."> <el-input v-model="lineCmbdxDetailsData.bbaRemoteRackNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Local Location"> <el-input v-model="lineCmbdxDetailsData.ispLocalLocation" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Local Rack No."> <el-input v-model="lineCmbdxDetailsData.ispLocalRackNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Local Unit No."> <el-input v-model="lineCmbdxDetailsData.ispLocalUnitNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Local Interface"> <el-input v-model="lineCmbdxDetailsData.ispLocalLocalInterface" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Local Power Supply"> <el-input v-model="lineCmbdxDetailsData.ispLocalPowerSupply" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Remote Location"> <el-input v-model="lineCmbdxDetailsData.ispRemoteLocation" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Remote Rack No."> <el-input v-model="lineCmbdxDetailsData.ispRemoteRackNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Remote Unit No."> <el-input v-model="lineCmbdxDetailsData.ispRemoteUnitNo" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Remote Interface"> <el-input v-model="lineCmbdxDetailsData.ispRemoteLocalInterface" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="ISP Remote Power Supply"> <el-input v-model="lineCmbdxDetailsData.ispRemotePowerSupply" :disabled="editDisabled"></el-input> </el-form-item> <el-form-item label="Remark"> <el-input type="textarea" autosize resize="none" v-model="lineCmbdxDetailsData.remark" :disabled="editDisabled"></el-input> </el-form-item> </el-form> </div> </div> <div class="dmzFormButton" style="width: 92%;margin: 3vh 4% 0;"> <el-button type="primary" @click="submitForm('lineCmbdxDetailsData')" v-if="editDisabled == false && typeTitleData == 'Edit'">Save</el-button> <el-button type="info" plain @click="detailsPopupOff">Cancel</el-button> </div> </div> </div> </template> <script> import { lineCMDBListEdit,lineCMDBListCopyDetails } from "../../../assets/js/apis/api"; export default { name: "lineCmdbDetails", props: { detailsData: Object, isEditDisabled: Boolean, typeTitle: String, }, data() { return { typeTitleData: '', lineCmbdxDetailsData: {}, lineCmbdxDetailsRules: { localSite: [ { required: true, message: 'Please Input Local Site', trigger: 'blur' }, ], remoteSite: [ { required: true, message: 'Please Input Remote Site', trigger: 'blur' }, ], bandwidth: [ { required: true, message: 'Please Input Bandwidth', trigger: 'blur' }, ], leasedCircuitId: [ { required: true, message: 'Please Input Circuit ID', trigger: 'blur' }, ], }, disabled: true, editDisabled: true, lineCmdbLoad: false, originalForm: {}, newForm: {} } }, methods: { detailsPopupOff() { this.$parent.closedDetail() }, saveChanges(changedFields) { this.newForm = changedFields // console.log('newForm',this.newForm) // console.log('originalForm',this.originalForm) }, submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { // this.lineCmdbLoad = this.$loading(); // if(this.originalForm.saveLog == true) { // let params = {} // params = this.newForm // if(this.typeTitleData == 'Edit') { // params["id"] = this.lineCmbdxDetailsData.id // } // params["updateUser"] = localStorage.getItem("displayName") // // console.log('params',params) // // lineCMDBListEdit(params).then(res => { // // // console.log('res',res) // // let resData = res["data"] // // if(resData["code"] == 200) { // // this.lineCmdbLoad.close(); // // this.$message.success("Save Successfully!") // // this.$parent.closedEditDetail() // // } else { // // this.lineCmdbLoad.close(); // // this.$message.error("System Error") // // } // // }) // } else { // this.lineCmdbLoad.close(); // this.detailsPopupOff() // } } }) } }, watch: { lineCmbdxDetailsData: { deep: true, // 深度监听对象内部的变化 handler(newVal, oldVal) { const changedFields = {}; // 存储发生变化的字段 // let index = 0; // console.log('newVal:', newVal); // console.log('Changed Fields:', changedFields); for (const key in newVal) { // console.log('newVal[key]:',newVal[key]) // console.log('this.originalForm[key]:',this.originalForm[key]) if (newVal.hasOwnProperty(key)) { if(Object.prototype.toString.call(this.originalForm[key]) !== '[object String]') { continue } if (newVal[key] !== this.originalForm[key]) { // console.log('测试进入次数:',index=index+1) // changedFields[key] = { original: this.originalForm[key], modified: newVal[key] };- // 发生变化的字段和它们的旧值 changedFields[key] = newVal[key] ; } } } // console.log('Changed Fields:', changedFields); // 输出发生变化的字段和它们的值 // 这里你可以调用保存更改的函数,比如 saveChanges(changedFields) if(JSON.stringify(changedFields) === '{}') { this.originalForm.saveLog = false } else { this.originalForm.saveLog = true } this.saveChanges(changedFields) } } }, created() { console.log(this.detailsData,this.isEditDisabled,this.typeTitle) this.lineCmbdxDetailsData = JSON.parse(JSON.stringify(this.detailsData)) this.detailsData["saveLog"] = false this.originalForm = { ...this.detailsData }; this.editDisabled = this.isEditDisabled this.typeTitleData = this.typeTitle } } </script> <style scoped> /deep/ .approveForm .el-form-item__label { line-height: 4.5vh; font-size: 12px; color: rgba(32, 34, 36, .9); /*padding: 0;*/ text-align: left; } /deep/ .approveForm .el-form-item__label { padding: 0 5px 0 10.5px; } /deep/ .approveForm .is-required .el-form-item__label { padding: 0; } /deep/ .approveForm .el-form-item__content { line-height: 4.5vh; font-size: 12px; } /deep/ .approveForm span { display: block; height: 4.5vh; line-height: 4.5vh; font-size: 12px; padding: 0 25px 0 10px; color: rgba(32, 34, 36, .9); text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } /deep/ .approveForm .el-input__inner { height: 4.5vh; line-height: 4.5vh; font-size: 12px; font-weight: initial; border-radius: 2px; padding: 0 25px 0 5px; color: rgba(32, 34, 36, .9); /*border: 1px solid #D5D5D5;*/ /*background-color: #F5F6FA;*/ font-family: BMWTNR, sans-serif !important; } /deep/ .approveForm .is-disabled .el-input__inner, /deep/ .approveForm .is-disabled .el-textarea__inner { background-color: #F5F6FA !important; border: 1px solid #D5D5D5; } /deep/ .approveForm .el-input__suffix-inner .el-input__icon { display: block; height: 4.5vh; line-height: 4.5vh; position: absolute; top: 0; right: 0; } /deep/ .approveForm .el-input .el-input__count .el-input__count-inner { background-color: transparent !important; position: absolute; right: -75px; display: block; text-align: left; width: 55px; line-height: 4.5vh; font-size: 12px; } /deep/ .approveForm .el-select .el-input__icon { line-height: 4.5vh; } /deep/ .approveForm .el-form-item:nth-child(4) { margin-bottom: 2vh; } /deep/ .approveForm .el-textarea span { padding: 0 !important; bottom: -4vh !important; background: transparent !important; } /deep/ .approveForm .el-textarea .el-textarea__inner { padding: 5px 2%; font-size: 12px; border-radius: 2px; height: 7vh; resize: none; background-color: transparent; font-family: BMWTNR, sans-serif !important; } /deep/ .dmzFormTableData .el-table tr { cursor: pointer; /*background-color: transparent;*/ } /deep/ .dmzFormTableData .el-table th { font-weight: 600; color: #666666; } /deep/ .dmzFormTableData .el-table td, /deep/ .dmzFormTableData .el-table th { padding: 0; background-color: transparent; height: 6vh; } /deep/ .dmzFormTableData .el-table td { height: 6vh; } /deep/ .dmzFormButton .el-button { width: 82px; height: 4vh; line-height: 4vh; padding: 0; border-radius: 2px; font-size: 12px; font-family: BMWTNR, sans-serif !important; } /deep/ .approveForm .el-form-item { margin-bottom: 3vh; } /deep/ .formTextarea .el-textarea .el-textarea__inner { line-height: 4vh; padding: 0 5px; border-radius: 2px; font-size: 12px; font-family: BMWTNR, sans-serif !important; } /deep/ .formTextarea .el-textarea { width: 100%; min-height: 4.5vh; } /deep/ .formTextarea .el-form-item__content { height: initial !important; } </style> 上述代码,点击save按钮,必填项非空验证没有实现,是什么原因
最新发布
12-19
<el-form :model="dynamicValidateForm" ref="dynamicValidateForm" label-width="100px" class="demo-dynamic"> <el-form-item prop="email" label="邮箱" :rules="[ { required: true, message: '请输入邮箱地址', trigger: 'blur' }, { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] } ]" > <el-input v-model="dynamicValidateForm.email"></el-input> </el-form-item> <el-form-item v-for="(domain, index) in dynamicValidateForm.domains" :label="'域名' + index" :key="domain.key" :prop="'domains.' + index + '.value'" :rules="{ required: true, message: '域名不能为空', trigger: 'blur' }" > <el-input v-model="domain.value"></el-input><el-button @click.prevent="removeDomain(domain)">删除</el-button> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('dynamicValidateForm')">提交</el-button> <el-button @click="addDomain">新增域名</el-button> <el-button @click="resetForm('dynamicValidateForm')">重置</el-button> </el-form-item> </el-form> <script> export default { data() { return { dynamicValidateForm: { domains: [{ value: '' }], email: '' } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); }, removeDomain(item) { var index = this.dynamicValidateForm.domains.indexOf(item) if (index !== -1) { this.dynamicValidateForm.domains.splice(index, 1) } }, addDomain() { this.dynamicValidateForm.domains.push({ value: '', key: Date.now() }); } } } </script> 写一个动态表单验证,当input进行push时如果input的value值相同时,则通过blur事件弹出form表单验证
11-12
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值