最近碰到一个新的需求,先是需要获取用户组列表(做成表格),表格项可以展开,在展开的时候动态的获取所展开的用户组底下的用户列表。

问题描述

起初,我是把表格数据分开来,即外层用户组列表使用list数组,内层嵌套的用户列表使用innerList数组,两者互不干扰。然后监听外层表格的expand-change事件,在事件回调中配合表格属性expand-row-keys来控制展开的行(需求是手风琴模式,即一次最多展开一行),同时回调中去动态请求数据,数据返回后对innerList进行赋值。
示例Demo代码如下:

<template> 
  <!-- 外层为用户组数据 -->
  <el-table 
 	:data="list" 
 	style="width: 100%" 
 	@expand-change="handleExpandChange" 
 	:expand-row-keys="currentExpandRow" 
 	row-key="id"
  > 
    <el-table-column type="expand"> 
      <!-- 内层为用户列表数组 -->
      <el-table :data="innerList" style="width: 100%"> 
        <el-table-column prop="user_id" label="用户ID" />
        <el-table-column prop="nickname" label="昵称" /> 
      </el-table>
    </el-table-column> 
    <el-table-column prop="id" label="ID" /> 
 	<el-table-column prop="user_group_name" label="组名" />  
  </el-table>  
</template>

对应的data还有methods

<script>
export default {
  data() {
    return {
      list: [],
      innerList: [],
      currentExpandRow: []
    }
  },
  methods: {
    // 获取某个用户组中的用户列表
    async handleExpandChange(row) {
      const currentID = this.currentExpandRow[0] ? this.currentExpandRow[0] : ''
      if (currentID == row.id) {
        this.currentExpandRow = []
        return
      }
      const groupID = row.id
      this.currentExpandRow = [groupID] // 控制展开行
      const res = await this.axios.post('getData', {
        groupID
      })
      this.innerList = res.data.data.data
    }
  }
}
</script>

一番操作后,发现展开后数据确确实实是拿到了,可是视图却不更新,确切的讲,是不会及时更新,展开后的内嵌表格会渲染上一次拿到的数据,即第一次展开无数据,第二次展开渲染第一次获取的数据,依次类推。
于是我便在事件的最后加上this.$forceUpdate()强制更新,然而并没有什么卵用。

如何解决?

首先当然还是百度,谷歌了,可是一番查找后,并没有理想的方案,网上给的方案大多用this.$set去处理,可实际操作起来并没有解决。
在捣鼓的过程中,发现了this.currentExpandRow的赋值会触发视图的更新,于是我便在事件的最后用$nextTick去赋值这个变量,结果是能成功,但是常常会出现拿不到数据(空白)。
怎么办呢?只好返回官网去找答案了。

解决

参照着官网的示例代码,我发现了好像只能在外层的list内部去内嵌innerList
简单点讲,就是表格组件的更新,是看最外层的data所绑定的变量,只要它一改变,那么视图也会相应的更新。
改动后的Demo代码如下:

<template> 
  <!-- 外层为用户组数据 -->
  <el-table
    :data="list"
    style="width: 100%"
    @expand-change="handleExpandChange"
    :expand-row-keys="currentExpandRow"
    row-key="id"
  >
    <el-table-column type="expand">
      <template slot-scope="props">
      	<!-- 内层为用户列表数组 -->
        <el-table :data="props.row.innerList" style="width: 100%">
          <el-table-column prop="user_id" label="用户ID" />
          <el-table-column prop="nickname" label="昵称" />
        </el-table>
      </template>
    </el-table-column>
    <el-table-column prop="id" label="ID" />
    <el-table-column prop="user_group_name" label="组名" />
  </el-table>
</template>

对应的data还有methods

<script>
export default {
  data() {
    return {
      list: [],
      currentExpandRow: []
    }
  },
  methods: {
    // 获取某个用户组中的用户列表
    async handleExpandChange(row) {
      const currentID = this.currentExpandRow[0] ? this.currentExpandRow[0] : ''
      if (currentID == row.id) {
        this.currentExpandRow = []
        return
      }
      let currentIndex = 0
      this.list.some((item, index) => {
        if (item.id === row.id) {
          currentIndex = index
          return true
        }
      })
      const groupID = row.id
      this.currentExpandRow = [groupID] // 控制展开行
      const res = await this.axios.post('getData', {
        groupID
      })
      this.$set(this.list[currentIndex], 'innerList', res.data.data.data)
    }
  }
}
</script>

我将原本的innerList内嵌到list
DOM中是对table-column做多一层template包裹,通过props传每一行的innerList作为内嵌表格的data
事件回调中去获取当前点击行的currentIndex,在获取数据的最后通过$set去做数据的赋值。
这样做就能完美的渲染内嵌表格列表了,后续还能为内嵌表格增加分页功能!

总结

接触了UI库这么久了,感觉有些需要自定义的需求写起来还是有点费脑,不知道是本人代码水平还不够高还是官方文档不够明示,还是得继续学习!
Keep learning…