Search code examples
javascriptarraysvue.jsspliceelement-ui

Vue Array.splice() doesn't remove DOM element in element-ui table


I have a table with a hierarchical structure. When I try to remove one of the children with "array.splice", VUE does not delete its Dom structure reactively. Has anyone come across this? What are the solutions?

Reproduced this problem by the example of the table c site Vuejs

var Main = {
    data() {
      return {
        tableData: [{
          id: 1,
          date: '2016-05-02',
          name: 'wangxiaohu'
        }, {
          id: 2,
          date: '2016-05-04',
          name: 'wangxiaohu'
        }, {
          id: 3,
          date: '2016-05-01',
          name: 'wangxiaohu',
          children: [{
              id: 31,
              date: '2016-05-01',
              name: 'wangxiaohu'
            }, {
              id: 32,
              date: '2016-05-01',
              name: 'wangxiaohu'
          }]
        }, {
          id: 4,
          date: '2016-05-03',
          name: 'wangxiaohu'
        }],
        tableData1: [{
          id: 1,
          date: '2016-05-02',
          name: 'wangxiaohu'
        }, {
          id: 2,
          date: '2016-05-04',
          name: 'wangxiaohu'
        }, {
          id: 3,
          date: '2016-05-01',
          name: 'wangxiaohu',
          hasChildren: true
        }, {
          id: 4,
          date: '2016-05-03',
          name: 'wangxiaohu'
        }]
      }
    },
    methods: {
      load(tree, treeNode, resolve) {
        resolve([
          {
            id: 31,
            date: '2016-05-01',
            name: 'wangxiaohu'
          }, {
            id: 32,
            date: '2016-05-01',
            name: 'wangxiaohu'
          }
        ])
      },
      removeRow(row){
        this.tableData[2].children.splice(0,1);
        //this.tableData.splice(0,1);
      }
    },
  }
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
@import url("//unpkg.com/element-ui@2.7.2/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui@2.7.2/lib/index.js"></script>
<div id="app">
<template>
<div>
  <el-button @click="removeRow">
    Delete child
  </el-button>
  <el-table
    :data="tableData"
    style="width: 100%;margin-bottom: 20px;"
    border
    row-key="id">
    <el-table-column
      prop="date"
      label="日期"
      sortable
      width="180">
    </el-table-column>
    <el-table-column
      prop="name"
      label="name"
      sortable
      width="180">
    </el-table-column>
  </el-table>

  <el-table
    :data="tableData1"
    style="width: 100%"
    row-key="id"
    border
    lazy
    :load="load"
    >
    <el-table-column
      prop="date"
      label="date"
      width="180">
    </el-table-column>
    <el-table-column
      prop="name"
      label="name"
      width="180">
    </el-table-column>
  </el-table>
</div>
</template>
</div>

I am using Vue 2.6.10 and element-ui 2.7.0


Solution

  • It seems to be an issue with the way element-ui handles data internally. When modifying nested data directly, the table is not getting re-rendered. (But the data was indeed mutated correctly, as could be seen when logging this.tableData after the splice().)

    It helps to create a copy of the tableData (you might need a workaround/polyfill for Array.from() if you'd like to support older browsers), modify that and set tableData to the mutated copy.

    (Open the snipped in fullscreen mode and either click the button twice, or expand the children before clicking the button, as the children are collapsed by default and after re-rendering.)

    var Main = {
        data() {
          return {
            tableData: [{
              id: 1,
              date: '2016-05-02',
              name: 'wangxiaohu'
            }, {
              id: 2,
              date: '2016-05-04',
              name: 'wangxiaohu'
            }, {
              id: 3,
              date: '2016-05-01',
              name: 'wangxiaohu',
              children: [{
                  id: 31,
                  date: '2016-05-01',
                  name: 'wangxiaohu'
                }, {
                  id: 32,
                  date: '2016-05-01',
                  name: 'wangxiaohu'
              }]
            }, {
              id: 4,
              date: '2016-05-03',
              name: 'wangxiaohu'
            }],
            tableData1: [{
              id: 1,
              date: '2016-05-02',
              name: 'wangxiaohu'
            }, {
              id: 2,
              date: '2016-05-04',
              name: 'wangxiaohu'
            }, {
              id: 3,
              date: '2016-05-01',
              name: 'wangxiaohu',
              hasChildren: true
            }, {
              id: 4,
              date: '2016-05-03',
              name: 'wangxiaohu'
            }]
          }
        },
        methods: {
          load(tree, treeNode, resolve) {
            resolve([
              {
                id: 31,
                date: '2016-05-01',
                name: 'wangxiaohu'
              }, {
                id: 32,
                date: '2016-05-01',
                name: 'wangxiaohu'
              }
            ])
          },
          removeRow(row){
            var newTableData = Array.from(this.tableData);
            newTableData[2].children.splice(0,1)
            this.tableData = newTableData;
            //this.tableData.splice(0,1);
          }
        },
      }
    var Ctor = Vue.extend(Main)
    new Ctor().$mount('#app')
    @import url("//unpkg.com/element-ui@2.7.2/lib/theme-chalk/index.css");
    <script src="//unpkg.com/vue/dist/vue.js"></script>
    <script src="//unpkg.com/element-ui@2.7.2/lib/index.js"></script>
    <div id="app">
    <template>
    <div>
      <el-button @click="removeRow">
        Delete child
      </el-button>
      <el-table
        :data="tableData"
        style="width: 100%;margin-bottom: 20px;"
        border
        row-key="id">
        <el-table-column
          prop="date"
          label="日期"
          sortable
          width="180">
        </el-table-column>
        <el-table-column
          prop="name"
          label="name"
          sortable
          width="180">
        </el-table-column>
      </el-table>
    
      <el-table
        :data="tableData1"
        style="width: 100%"
        row-key="id"
        border
        lazy
        :load="load"
        >
        <el-table-column
          prop="date"
          label="date"
          width="180">
        </el-table-column>
        <el-table-column
          prop="name"
          label="name"
          width="180">
        </el-table-column>
      </el-table>
    </div>
    </template>
    </div>