简体   繁体   中英

Inquiry regarding array reactivity in Vue.js 2

I have written a grid component that supports child rows, ie a togglable row with a cell that spans the entire width of the table, under each normal row. In order to keep track of the child rows state I am using a data property called openChildRows which defaults to an empty array. When the user clicks on the toggle icon which is present on the first column of each row, the child row of that row opens. An additional click closes it. The openChildRows array contains the ids of the open rows.

The issue I am having is that when I use a component for the child row, all open rows that come after the one toggled will be remounted. This is clearly inefficient, especially if the child row component sends an ajax request when mounted. Ideally, what I would want is that only the new child row will be mounted.

I have written the grid using JSX for templates. Below is the relevant code:

Rows template:

module.exports = function(h, that) {
    var rows = [];
    var columns;
    var rowKey = that.opts.uniqueKey;

    var rowClass;
    var data = that.source=='client'?that.filteredData:that.tableData;
    var recordCount = (that.Page-1) * that.limit;

    data.map(function(row, index) {

      index = recordCount + index + 1;

      columns = [];

      if (that.hasChildRow) {
        var childRowToggler = <td><span on-click={that.toggleChildRow.bind(that,row[rowKey])} class={`VueTables__child-row-toggler ` + that.childRowTogglerClass(row[rowKey])}></span></td>;
        if (that.opts.childRowTogglerFirst) columns.push(childRowToggler);
      }


      that.allColumns.map(function(column) {
          let rowTemplate = that.$scopedSlots && that.$scopedSlots[column];

          columns.push(<td class={that.columnClass(column)}>
            {rowTemplate ? rowTemplate({ row, column, index }) : that.render(row, column, index, h)}
        </td>)
      }.bind(that));

      if (that.hasChildRow && !that.opts.childRowTogglerFirst) columns.push(childRowToggler);

      rowClass = that.opts.rowClassCallback?that.opts.rowClassCallback(row):'';

      rows.push(<tr class={rowClass} on-click={that.rowWasClicked.bind(that, row)} on-dblclick={that.rowWasClicked.bind(that, row)}>{columns} </tr>);

    // Below is the code that renders open child rows
      if (that.hasChildRow && this.openChildRows.includes(row[rowKey])) {

        let template = this._getChildRowTemplate(h, row);

        rows.push(<tr class='VueTables__child-row'><td colspan={that.allColumns.length+1}>{template}</td></tr>);
      }

    }.bind(that))

    return rows;

}

Toggle method code:

  module.exports = function (rowId, e) {

  if (e) e.stopPropagation();

  if (this.openChildRows.includes(rowId)) {
    var index = this.openChildRows.indexOf(rowId);
    this.openChildRows.splice(index,1);
  } else {
    this.openChildRows.push(rowId);
  }
};

The _getChildRowTemplate method:

module.exports = function (h, row) {
  // scoped slot
  if (this.$scopedSlots.child_row) return this.$scopedSlots.child_row({ row: row });

  var childRow = this.opts.childRow;

  // render function
  if (typeof childRow === 'function') return childRow.apply(this, [h, row]);

  // component
  return h(childRow, {
    attrs: {
      data: row
    }
  });
};

I changed:

if (that.hasChildRow && this.openChildRows.includes(row[rowKey])) {

        let template = this._getChildRowTemplate(h, row);

        rows.push(<tr class='VueTables__child-row'><td colspan={that.allColumns.length+1}>{template}</td></tr>);
}

to:

  rows.push(that.hasChildRow && this.openChildRows.includes(row[rowKey])?
    <tr class='VueTables__child-row'><td colspan={that.allColumns.length+1}>{this._getChildRowTemplate(h, row)}</td></tr>:h());

And voila!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM