element-ui单选框在已选中的情况下取消选中,v-for后单选框和多选框响应式问题-vue2

855 阅读3分钟

一、开发背景

允许用户打标签,所以会有多组单选框和多选框,每组单选框允许取消当前选中。

image.png

二、整体实现

step1:v-for渲染单选框、多选框组 step2:新建情况下,恢复每个多选框组和单选框组在用户点击后高亮的响应式;编辑情况下,还需在此基础上恢复已选中标签的高亮样式; step3:单选框绑定@click事件,判断用户是否点击已选中;

三、数据结构

1.单选数据结构

image.png

2.多选数据结构

image.png

四、实现

1.html代码

第一层v-for遍历所有数据

第二层v-for遍历该层数据结构下的单选/多选标签

  <div v-for="group in queryResult" :key="group.id" class="check-box">
          <!-- 标题 -->
          <p class="title">{{ group.tagName }}({{ group.singleChoice ? '单选' : '多选' }})</p>
          <!-- 单选框组 -->
          <el-radio-group v-if="group.singleChoice" v-model="selectedRadios[group.id]" size="mini">
            <!-- v-for遍历当前单选框组下的单选标签 -->
            <el-radio v-for="child in group.children" :key="child.id" :label="child.id" @click.native.prevent="clickItem(child.id, group.id)" border>
              {{ child.tagName }}
            </el-radio>
          </el-radio-group>
          <!-- 多选框组 -->
          <el-checkbox-group v-else v-model="selectedCheckboxes[group.id]" size="mini">
            <!-- v-for遍历当前多选框组下的多选标签 -->
            <el-checkbox v-for="child in group.children" class="checkbox-info" :key="child.id" :label="child.id" border>
              {{ child.tagName }}
            </el-checkbox>
          </el-checkbox-group>
        </div>

2.js代码

1.准备双向绑定数据

  data() {
    return {
      queryResult: [], // 从后端获取的数据
      selectedRadios: {},// 单选选中数据
      selectedCheckboxes: {},// 多选选中数据
    };
  },

2.恢复用户点击时的响应及编辑态时恢复高亮情况,我封成了一个函数

以当前的标签独有的不重复的id为单选标签和多选标签的key及label

queryResults.forEach部分代码遍历 queryResults 数组,对于每个 group,如果它是一个单选组(group.singleChoice 为真),则将 selectedRadios 对象中对应的 group.id 设置为 null,表示当前没有选中任何子项。如果它是一个多选组,则在 selectedCheckboxes 对象中创建一个新的空数组,用于存储被选中的子项的 ID。

if (isEdit) 部分:

  • 如果是单选组,查找 group.children 数组中 child.old 为真的子项(表示该子项在之前的状态中是被选中的)。如果找到了这样的子项,就将 selectedRadios 对象中对应的 group.id 设置为该子项的 ID;如果没有找到,则保持为 null
  • 如果是多选组,使用 filter 方法筛选出 group.children 数组中所有 child.old 为真的子项,然后使用 map 方法将这些子项的 ID 提取出来,并将结果数组赋值给 selectedCheckboxes 对象中对应的 group.id
  processQueryResults(queryResults, isEdit) {
      // 恢复用户点击时的数据响应式
      queryResults.forEach((group) => {
        if (group.singleChoice) {
          // 单选
          this.$set(this.selectedRadios, group.id, null);
        } else {
          // 多选
          this.$set(this.selectedCheckboxes, group.id, []);
        }
      });
      // 如果是编辑态要恢复高亮样式
      if (isEdit) {
        queryResults.forEach((group) => {
          if (group.singleChoice) {
            // 单选
            let foundChild = group.children.find((child) => child.old);
            this.selectedRadios[group.id] = foundChild ? foundChild.id : null;
          } else {
            // 多选
            this.selectedCheckboxes[group.id] = group.children.filter((child) => child.old).map((child) => child.id);
          }
        });
      }
    },

调用

          success(res) {
            _this.queryResult = res;
            if (originalSearch) {
            // 接口数据输入到processQueryResults中恢复响应式-第一个参数是数据,第二个参数用于判断是否编辑情况
              _this.processQueryResults(_this.queryResult, true);
            }
          },

3.单选框绑定@click事件,判断用户是否点击已选中;

@click.native.prevent="clickItem(child.id, group.id)"跟踪当前点击的id

检查 selectedRadios 对象中 groupId 对应的值是否等于 childId。如果是,那么说明当前这个“子项”已经被选中。如果“子项”已经被选中(即 this.selectedRadios[groupId] === childId 为真),则使用 this.$set 方法(用于确保响应式地更新对象属性)将 groupId 对应的值设置为 null,从而取消选中该“子项”。

如果“子项”当前没有被选中(即 this.selectedRadios[groupId] !== childId 或 this.selectedRadios[groupId] 不存在),则使用 this.$set 方法将 groupId 对应的值设置为 childId,从而将该“子项”设置为选中状态。

  clickItem(childId, groupId) {
      if (this.selectedRadios[groupId] === childId) {
        // 如果当前已选中,则取消选中
        this.$set(this.selectedRadios, groupId, null);
      } else {
        // 否则设置为选中
        this.$set(this.selectedRadios, groupId, childId);
      }
    },

五、参考资料

1.element-UI的单选框怎么在已选中的情况下,再次点击取消选中:segmentfault.com/q/101000001…

六、注意

queryResults.forEach(group => {
  if (group.singleChoice) {
  // 这种写法是不行的,因为没有排除undefined情况
  this.selectedRadios[group.id] = group.children.find(child => child.old).id || null;