element-ui实现动态表单点击按钮新增行/删除行

最近做 vue2 项目,用到了 element-ui,其中有个需求是实现动态表单,点击按钮新增行/删除行,这里记录一下实现过程。

其实本质上就是操作数组,新增行就是往数组里 push 一个对象,删除行就是从数组里删除一项。

大致效果如下:

使用方式

这里外层是一个el-form,增删行是其中一个el-form-item,对应formData的一个字段,代码如下所示:

html 复制代码
<el-form :model="formData">
  <el-form-item label="最低版本号">
    <LimitVersion v-model="formData.limitVersion" />
  </el-form-item>
  <!-- 其他el-form-item -->
</el-form>

LimitVersion的组件实现

LimitVersion的组件逻辑:

  • value:父组件传入的值,即formData.limitVersion
  • innerValue:组件内部的值,这个值是比较特殊的,是一个二维数组,每一项是一个数组,比如[[1, '1.1.1'], [2, '2.2.2']],使用computed属性get来同步父组件传入的值,使用set来同步组件内部的值到父组件,由此实现双向绑定
  • deviceOptions:是下拉选项,这里我使用的是el-cascader,这个选项是异步获取的,所以需要在created钩子中获取,结构是[{ value: 1, label: 'xdf-n1', children: [{ value: 1.1.1, label: '1.1.1' }] }],这里我使用了两个接口,一个是获取设备列表,一个是获取设备对应的版本号列表,因为设备列表不是很多,所以我使用了Promise.all来获取所有设备对应的版本号列表,这样就不用每次选择设备时都去请求一次版本号列表。
  • removeRow:删除行,传入index,删除innerValue中对应的项
  • addRow:新增行,往innerValue中 push 一个空数组

最最重点其实就是v-for循环innerValue,每一项是一个el-cascader,然后每一项后面有一个删除按钮,每个el-cascader的值是innerValue[index],这样就实现了每一行的双向绑定。

以下是LimitVersion的组件实现:

vue 复制代码
<template>
  <div class="limit-box">
    <div v-for="(item, index) in innerValue" :key="index">
      <el-cascader
        clearable
        v-model="innerValue[index]"
        :options="deviceOptions"
      ></el-cascader>
      <el-button
        icon="el-icon-close"
        type="text danger"
        class="del-btn"
        v-if="innerValue.length > 1"
        @click="removeRow($event, index)"
      ></el-button>
    </div>
    <el-button class="add-btn" plain type="primary" @click="addRow"
      >添加</el-button
    >
  </div>
</template>
<script>
import {
  getDeviceDicFormat,
  getRomUpgradeVersionSelectFormat,
} from '@/service.js';

export default {
  name: 'RomLimit',
  props: {
    value: {
      type: Array,
      default: () => [],
    },
  },
  created() {
    this.getVersionOptions();
  },
  data() {
    return {
      deviceOptions: [],
    };
  },
  computed: {
    innerValue: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      },
    },
  },
  methods: {
    async getVersionOptions() {
      const deviceOptions = await getDeviceDicFormat();
      const detailedOptions = await Promise.all(
        deviceOptions.map(({ value }) =>
          getRomUpgradeVersionSelectFormat({ platformType: value })
        )
      );
      this.deviceOptions = deviceOptions.map((item, index) => {
        return {
          ...item,
          children: detailedOptions[index],
        };
      });
    },
    removeRow(e, index) {
      // 防止触发el-form的submit事件
      e.preventDefault();
      this.innerValue.splice(index, 1);
    },
    addRow(e) {
      // 防止触发el-form的submit事件
      e.preventDefault();
      this.innerValue.push([]);
    },
  },
};
</script>
<style scoped lang="less">
.limit-box {
  .add-btn {
    margin-top: 10px;
  }
  .del-btn {
    color: rgb(247, 137, 137);
    font-size: 18px;
  }
  .del-btn:focus,
  .del-btn:hover {
    color: rgba(247, 137, 137, 0.8);
  }
}
</style>

当然上面的一行比较简单,只是数组,如果复杂些是个对象,多个数据,那么就需要在addRow中 push 一个对象,然后在removeRow中删除对象。

对象的增删

使用方式仍然没有发生变化,假设formData.limitVersion,是一个对象数组,[{ id: 1, version: '1.1.1' }, { id: 2, version: '2.2.2' }]

效果如下:

其实实现仍然很简单,对象的话,使用el-form好了。el-formmodel是一个对象,是 innerValue[index],这样就实现了每一行的双向绑定,内部的每项是 innerValue[index].xxx。

html 复制代码
<div class="limit-box">
  <div style="display:flex;" v-for="(item, index) in innerValue" :key="index">
    <el-form
      style="display:flex;margin-bottom: 10px;"
      :inline="true"
      :model="innerValue[index]"
    >
      <el-form-item label="类型">
        <el-input v-model="innerValue[index].id" clearable></el-input>
      </el-form-item>
      <el-form-item label="版本">
        <el-input v-model="innerValue[index].version" clearable></el-input>
      </el-form-item>
    </el-form>
    <!-- <el-button icon="el-icon-close" .... -->
  </div>
  <!-- add -->
</div>

<!-- 
   methods的addRow需要改成这样
   addRow(e) {
      e.preventDefault()
      this.innerValue.push({ id: null, version: null })
    }, 
    
    -->

注意

这边的需求是,至少留一项,所以删除按钮的显示条件是innerValue.length > 1,如果不需要这个条件,可以直接去掉。注意默认值,如果默认留一项,数组的话就是[[]],对象的话就是[{ id: null, version: null }]

总结

其实实现动态表单,就是操作数组,新增行就是往数组里 push 一个对象,删除行就是从数组里删除一项。对象的话,使用el-form好了,el-formmodel是一个对象,是 innerValue[index],这样就实现了每一行的双向绑定,内部的每项是 innerValue[index].xxx。数组的话,根据情况使用不同组件。

这里是一个简单的实现,如果有更复杂的需求,可以根据实际情况来调整。

相关推荐
Saltwater_leo25 分钟前
【无标题】
java·服务器·前端
疯狂小料25 分钟前
React 表单处理与网络请求封装详解[特殊字符][特殊字符]
前端·react.js·php
小李飞飞砖1 小时前
kotlin的协程的基础概念
开发语言·前端·kotlin
夏天想1 小时前
element-plus中的table为什么相同的数据并没有合并成一个
javascript·vue.js·elementui
清风细雨_林木木1 小时前
Skeleton 骨架屏
javascript·vue.js·ecmascript
百事老饼干2 小时前
Vue3实现表格搜索内容高亮
前端·javascript·vue.js
蓝胖子的多啦A梦2 小时前
Vue+Element-ui 中 使用el-table 设置表格单元格 (el -table-column) 保留空格和换行
前端·vue.js·elementui
门豪杰2 小时前
使用Chrome和Selenium实现对Superset等私域网站的截图
前端·chrome·selenium·superset·截图
quan26312 小时前
富文本编辑器(wangeditor)导入附件
javascript·前端框架·html5·wangeditor·mammoth.js
小安同学iter2 小时前
Web开发 -前端部分-HTML5新特性
javascript·css·正则表达式·json·css3·html5