数据表:如何在 Ajax 重新加载后保持子行打开

Datatables: How can I keep child rows opened after the Ajax reload

我正在使用 Ajax 源来生成 table。这一个每 5000 毫秒刷新一次,当子行打开时,它会被 table 的重绘关闭。我怎样才能让这些打开?

我的代码:

    /* Formatting function for row details - modify as you need */
    function format ( d ) {
        // `d` is the original data object for the row
        return '<p>Text in child row</p>';
    }

    $(document).ready(function() {
        $('#table').DataTable( {
        ...
        } );

    var tr;
    var row;

    $('#table tbody').on('click', 'td.details-control', function () {

        if (tr !== null && tr !== undefined && row !== null && tr !== undefined) {
            row.child.hide();
            tr.removeClass('shown');            
        }

        tr = $(this).closest('tr');
        row = table.row( tr );

        if ( row.child.isShown() ) {
            // This row is already open - close it
            row.child.hide();
            tr.removeClass('shown');
        }
        else {
            // Open this row
            row.child( format(row.data()) ).show();
            tr.addClass('shown');
        }

    } );

    $.fn.dataTable.ext.errMode = 'none';    
    var table = $('#table').DataTable();
    setInterval( function () {
        table.ajax.reload( function () {
            if ( row.child.isShown() ) {
                // This row is already open - close it
                row.child.hide();
                tr.removeClass('shown');
            }
            else {          
                if (tr.hasClass('shown')) {
                    // Open this row
                    row.child( format(row.data()) ).show();
                    tr.addClass('shown');                       
                }
            }
        } );
    }, 5000 );  

    $('table td .details-control').html('<button><i class="fa fa-plus"></i></button>');

} );

参考Child rows example and ajax.reload()方法。

据我所知,您正在用来自 ajax 的数据创建的新 table 清除之前的 table。 您将必须保存打开的行的状态,并且每当您完成刷新时 table 使用保存的状态展开行。

要回答最初的问题(如何在 DataTable AJAX 重新加载时保持子行打开),请参阅以下实现。

我使用 cookie 来保持子行打开,我正在使用 js-cookie 找到的插件 here

最好将唯一标识符作为 table 的列,以便 re-opened 行是正确的。

$(function(){

  var dt = $('#my_table').DataTable(...);
  var reloadInterval = 10000; // milliseconds

  // Add extra-info row
  dt_add_details('my_table', 'details-control', formatCallback, 'id');
  var reloadCallback = function(json){
    dt_reopen_rows('my_table', formatCallback, 'id')
  };

  // Reload AJAX source every X seconds
  setInterval(function(){
    dt.ajax.reload(reloadCallback, false);
  }, reloadInterval)
});

/**
 * Format child row data.
 */
function formatCallback(d){
  ...
}

/**
 * Show / Hide extra-info when clicking on the column identified by className.
 * @param {String} selector - The HTML selector for the table.
 * @param {String} className - The column class name that holds the extra-info.
 * @param {Function} formatCallback - Function used to format the data of the
 * child row.
 * @param {String} cookieDataIndex - The data index to keep in cookie.
 */

function dt_add_details(selector, className, formatCallback, cookieDataIndex){
  $(selector + ' tbody').on('click', 'td.' + className, function () {
    var ckey = 'openRows_' + selector;
    var openRows = Cookies.getJSON(ckey);

    // Create cookie if never created
    if (typeof openRows == 'undefined'){
      Cookies.set(ckey, [], {'path': ''});
      var openRows = Cookies.getJSON(ckey);
    };

    // Get current info
    var tr = $(this).closest('tr');
    var row = $(selector).DataTable().row(tr);
    var id = row.data()[cookieDataIndex];

    if (row.child.isShown()){
        // This row is already open - close it
        row.child.hide();
        tr.removeClass('shown');

        // Remove opened row from cookie
        var idx = openRows.indexOf(id);
        openRows.splice(idx, 1);
        Cookies.set(ckey, openRows, {path: ''});
    }
    else{
        // Open this row
        row.child(formatCallback(row.data())).show();
        tr.addClass('shown');

        // Add row 'id' field to cookie
        if (openRows.indexOf(id) < 0){
          openRows.push(id);
        }
        Cookies.set(ckey, openRows, {path: ''});
    }
    // console.log("Opened rows: " + Cookies.getJSON('openRows_' + selector))
  });
}

/**
 * Show / Hide extra-info when clicking on the column identified by className.
 * @param {String} selector - The HTML selector for the table.
 * @param {Function} formatCallback - Function used to format the data of the
 * the child row.
 * @param {String} cookieDataIndex - The data index to keep in cookie.
 */
function dt_reopen_rows(selector, formatCallback, cookieDataIndex) {
    var ckey = 'openRows_' + selector;
    var openRows = Cookies.getJSON(ckey);
    if (!openRows)
        return;
    var table = $(selector).DataTable(); // existing DataTable
    $(table.rows().nodes()).each(function (idx, tr) {
        var row = table.row(tr);
        var id = row.data()[cookieDataIndex]
        if (openRows.indexOf(id) >= 0) {
            // console.log("Id " + id + " found in previously opened row. Re-opening.")
            $(tr).addClass("shown");
            row.child(formatCallback(row.data())).show();
        }
    });
}

使用此解决方案,table 中的每一行都应该有一个行 ID。有关详细信息,请参阅:https://datatables.net/reference/option/rowId

HTML

<script src="http://code.jquery.com/jquery-latest.min.js"
        type="text/javascript"></script>
<link rel="stylesheet" type="text/css"
      href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
<script src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>

table id="example" class="display" style="width:100%">
    <thead>
    <tr>
        <th></th>
        <th>Name</th>
        <th>Position</th>
        <th>Office</th>
        <th>Salary</th>
    </tr>
    </thead>
    <tfoot>
    <tr>
        <th></th>
        <th>Name</th>
        <th>Position</th>
        <th>Office</th>
        <th>Salary</th>
    </tr>
    </tfoot>
</table>

Javascript

   /* Function to create a new row, fell free to render your code here */
    function format(d) {
        // `d` is the original data object for the row
        return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
            '<tr>' +
            '<td>Full name:</td>' +
            '<td>' + d.name + '</td>' +
            '</tr>' +
            '<tr>' +
            '<td>Extension number:</td>' +
            '<td>' + d.extn + '</td>' +
            '</tr>' +
            '<tr>' +
            '<td>Extra info:</td>' +
            '<td>And any further details here (images etc)...</td>' +
            '</tr>' +
            '</table>';
    }

    function onClickEventListener() {
        var tr = $(this).closest('tr');
        var row = table.row(tr);

        if (row.child.isShown()) {
            // This row is already open - close it
            row.child.hide();
            tr.removeClass('shown');
        }
        else {
            // Open this row
            row.child(format(row.data())).show();
            tr.addClass('shown');
        }

        let currentRowID = "#" + ($(this).closest('tr').attr('id'));
        if ($.inArray(currentRowID, rowIds) !== -1) {
            //Row is closed, remove row ID from rowIDs array
            var index = rowIds.indexOf(currentRowID);
            if (index !== -1) rowIds.splice(index, 1);
            rowIds.filter(function (val) {
                return val
            });
        } else {
            //Row is opened, add row ID to rowIDs array
            rowIds.push(currentRowID);
        }
    }

    $(document).ready(function () {
        let rowIds = [];
        var table = $('#example').DataTable({
            "ajax": "{{ path('your_data_source') }}",
            "columns": [
                {
                    "className": 'details-control',
                    "orderable": false,
                    "data": null,
                    "defaultContent": ''
                },
                {"data": "name"},
                {"data": "position"},
                {"data": "office"},
                {"data": "salary"}
            ],
            "order": [[1, 'asc']]
        });

        // Add event listener for opening and closing the row
        $('#example tbody').on('click', 'td.details-control', onClickEventListener);

        //set interval to update datatable
        setInterval(function () {
            table.ajax.reload(function () {
                //Iterate through all the open rows and open them again   <--Value is set in the onClickEventListener function
                table.rows(rowIds).every(function (row, index, array) {
                    table.row(row).child(format(this.data())).show();
                    this.nodes().to$().addClass('shown');
                    //Add a minus icon for the open row
                    this.nodes().to$().children('td:first').html('<img style="max-width: 30px;; max-height: 100%;object-fit: contain" src=' + '{{ asset('img/datatables/icon_minus.png') }}' + ' ></img>');
                });
                //Set to false if you don't want the paging to reset after ajax load,otherwise true
            }, false);
        }, 1000);
    });

尝试存储打开的行的索引值(全局)

table重新加载后,调用如下方法

function openChildByIndex(){
        let row = table.row('globally stored index'); //place your index here 
        row.child(format(row.data())).show();
        $(row.node()).addClass('shown');
}