针对el-table中每一行的输入框进行表单验证(校验非空以及重复的值等)

最近遇到一个需求,针对于一个表格,可以点击增加按钮在表格插入输入框,输入相应的值点击确定以增加表格的一行,需要对表格里的输入框进行表单校验,以及确定的提交校验。大概效果图如下:
在这里插入图片描述
具体思路:1、由于确定按钮的校验是针对每一行的表单,所以el-form的ref值需要为动态的值,每次确定的校验也只校验当前行的结果。2、对于是否存在重复的值的校验,需要将当前正处于编辑状态的行与其他的行数据进行匹配,所以在自定义的校验函数里需要传入当前行的信息进行判断。
代码如下:

<template>
  <div class="formData">
      <el-table :data="list" style="width: 100%">
        <el-table-column prop="key" label="键" min-width="180">
          <template slot-scope="{row}">
            <div v-if="!row.isModified">
              {{row.key}}
            </div>
            <el-form 
            	v-else
            	:ref="'keyForm'+row.id" 
            	label-width="0px" 
            	:class="(row.isKeyValid || row.isValueValid) ? 'demo-ruleForm' : '' "  
            	:model="row"
            >
              <el-form-item 
              	prop="key" 
              	:rules="[{ validator: validateKey, trigger: ['blur','change'], id: row.id }]"
              >
                <el-input v-model="row.key" placeholder="请输入内容"></el-input>
              </el-form-item>
            </el-form>
          </template>
        </el-table-column>
        <el-table-column prop="value" label="值" min-width="180">
          <template slot-scope="{row}">
            <div v-if="!row.isModified">
              {{row.value}}
            </div>
            <el-form 
            	:ref="'valueForm'+row.id" 
            	label-width="0px" 
            	:class="(row.isKeyValid || row.isValueValid) ? 'demo-ruleForm' : '' " 
            	v-else 
            	:model="row"
            >
              <el-form-item 
              	prop="value" 
              	:rules="[{ validator: validateValue, trigger: ['blur','change'], id: row.id }]"
              >
                <el-input v-model="row.value" placeholder="请输入内容"></el-input>
              </el-form-item>
            </el-form>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="180">
          <template slot-scope="{row}">
            <div v-if="!row.isModified">
              <el-button type="text" size="small" @click="deleteKey(row.id)">删除</el-button>
              <el-button type="text" size="small" @click="editKey(row.id)">编辑</el-button>
            </div>
            <div v-else>
              <el-button type="text" size="small" @click="confirm(row.id)">确认</el-button>
              <el-button type="text" size="small" @click="cancel(row.id)">取消</el-button>
            </div>
          </template>
        </el-table-column>
      </el-table>
    <el-button @click="addKey">增加</el-button>
  </div>
</template>

<script>
export default {
  name: "formData",
  data() {
    return {
      list: [
        {
          key: "vue",
          value: "2.21",
          isModified: false,
          isKeyValid: false,
          isValueValid: false,
          id: 0
        },
        {
          key: "react",
          value: "16.2",
          isModified: false,
          isKeyValid: false,
          isValueValid: false,
          id: 1
        }
      ]
    };
  },
  computed: {
    existKeys() {
    //过滤出已经存在的key
      return this.list.filter( item => !item.isModified ).map(item => item.key)
    },
    existValues() {
    //过滤出已经存在的value
      return this.list.filter( item => !item.isModified ).map(item => item.value)
    }
  },
  methods: {
    deleteKey(index) {
      this.list.splice(index, 1)
    },
    editKey(index) {
      this.list[index].isModified = true
    },
    addKey() {
      this.list.push(
        {
          key: "",
          value: "",
          isModified: true, //切换编辑状态的标志位
          isKeyValid: false, //判断Key的校验结果
          isValueValid: false,	//判断value的校验结果
          id: this.list.length
        }
      )
    },
    promiseValidate(key) {
      return new Promise((resolve, reject) => {
        try {
          this.$refs[key].validate(isValid => {
            resolve(isValid)
          })
        } catch (err) {
          reject(err)
        }
      })
    },
    async confirm(index) {
      const validates = await Promise.all([ 
      	this.promiseValidate("keyForm" + index), 		
      	this.promiseValidate("valueForm" + index) 
      ])
      if(validates.every(item => item)) {
        this.list[index].isModified = false;
      }
    },
    validateKey(rule, value, callback) {
      if (value === '') {
        this.list[rule.id].isKeyValid = true
        callback(new Error('请输入Key'));
      } else if ( this.existKeys.includes(value) ) {
        this.list[rule.id].isKeyValid = true
        callback(new Error('Key的值已经重复'));
      } else {
        callback();
      }
    },
    validateValue(rule, value, callback) {
      if (value === '') {
        this.list[rule.id].isValueValid = true
        callback(new Error('请输入Value'));
      } else if ( this.existValues.includes(value) ) {
        this.list[rule.id].isValueValid = true
        callback(new Error('value的值已经重复'));
      } else {
        callback();
      }
    }
  }
};
</script>

<style>
  .formData {
    padding: 20px;
  }
  .el-form-item {
    margin-bottom: 0;
  }
  .demo-ruleForm {
    margin: 20px 0;
  }
</style>

关键代码解析:

  1. 绑定动态样式
 :class="(row.isKeyValid || row.isValueValid) ? 'demo-ruleForm' : '' " 

当触发校验错误结果文字提示的时候,可能会只出现输入框边框变红,看不到文字提示信息,如下图
在这里插入图片描述
这时只需要给el-form一个向下的margin值即可将文字展示出来,所以通过key与value的校验结果动态绑定这个class,当校验结果都正确,则表格行的高度恢复正常即可。

  1. 绑定动态的ref
:ref="'keyForm'+row.id" 

确认按钮的校验都是通过ref来进行校验,所以每一行的ref都应该不同,此处直接拼接上行的id,确定的时候传入行的id即可只校验当前行的结果。

  1. 给rules传入当前行的参数校验重复的内容
:rules="[{ validator: validateValue, trigger: ['blur','change'], id: row.id }]"

此处主要是如何将行的信息传入自定义的校验函数中,当时尝试过改造validator函数,写法如下

:rules="[{ validator: (rule, value, callback,row) => { validateValue(rule, value, callback.row) }]"

但是这样不知为啥,在自定义的校验函数中无法拿到row的值,所以直接在rules中自定义一个属性即可获取当前行的信息,以判断当前行与其他行的关系,进行自定义校验。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐