Tablesorter 总是在内容编辑后重新加载第一页

Tablesorter always reloads first page after content-edit

我有一个带有表格排序器实现的应用程序。它具有内容可编辑、过滤和 ajax 服务器寻呼机的特点。一切正常,除了一个问题:每次用户在页面 > 1 上提交编辑后的值时,都会提交内容,但 tablesorter 会重新加载第一页。

查看 xhr 请求很明显发出了新的 get 请求(我猜是在幕后,在我的代码中我没有触发任何更新或重新加载)。反正对我来说很好,刷新内容没问题,但我想保留用户编辑前的页面。

如我所见,问题仅发生在页面参数上:如果我更改每页元素的数量,在列中放置过滤器,或者对一个或多个字段进行排序,tablesorter 会跟踪它们,并且它们是在请求 url 参数中正确更新,页面参数除外,该参数始终恢复为 0。

从 xhr 请求端查看的进度示例:

第一个请求(默认): vlansummarys?page=0&size=10&col[10]=0&col[8]=0&col[7]=0&fcol

已添加过滤器: vlansummarys?page=0&size=10&col[10]=0&col[8]=0&col[7]=0&fcol[3]=monocos

页面元素10到20: vlansummarys?page=0&size=20&col[10]=0&col[8]=0&col[7]=0&fcol[ 3]=单体

添加了新的排序字段: vlansummarys?page=0&size=20&col[10]=0&col[8]=0&col[7]=0&col[3] =0&fcol[3]=单体

页面更改为 2: vlansummarys?page=1&size=20&col[10]=0&col[8]=0&col[7]=0&col[3] =0&fcol[3]=单体

内容编辑,之后调用: vlansummarys?page=0&size=20&col[10]=0&col[8]=0&col[7]=0&col [3]=0&fcol[3]=单体

编辑

根据要求,我发布了我的代码。由于很难将其简化为一个简单的工作示例,因此我将粘贴我所有的 tablesorter 初始化函数。它不能单独工作,因为它依赖于页面上计算的其他数据和变量,但我想它应该给出一个很好的主意。请向我询问任何可能有帮助的详细信息。

function LoadTable() {
    $('#table').tablesorter({
        theme: 'bootstrap',
        //widthFixed: true,
        zebra: [
            "even",
            "odd"],
        dateFormat: "ddmmyyyy",
        headerTemplate: '{content}',
        sortList: [[10, 0], [8, 0], [7, 0]], //Order by AdR del Kit ASC, AdR ASC, Centrale ASC
        initWidgets: true,
        widgets: bReadOnly ? ['zebra', 'columns', 'filter', 'uitheme'] : ['zebra', 'columns', 'filter', 'uitheme', 'editable'],
        widgetOptions: {
            filter_columnFilters: true,
            filter_cssFilter: arrHeaderFields,
            editable_columns: (bReadOnly ? null : [21]),       // or "0-2" (v2.14.2); point to the columns to make editable (zero-based index)
            editable_enterToAccept: true,          // press enter to accept content, or click outside if false
            editable_autoAccept: false,          // accepts any changes made to the table cell automatically (v2.17.6)
            editable_autoResort: false,         // auto resort after the content has changed.
            editable_noEdit: 'no-edit',     // class name of cell that is not editable
            editable_editComplete: 'editComplete', // event fired after the table content has been edited
            editable_validate: null,          // return a valid string: function(text, original){ return text; }
            editable_focused: null,/*function (txt, columnIndex, $element) {
                // $element is the div, not the td
                // to get the td, use $element.closest('td')
                //$element.addClass('focused');
                $element.removeClass("emptyPlaceholder");
                //SelectActivationDateText($element.closest('td'));
            },*/
            editable_blur: null,/*function (txt, columnIndex, $element) {
                // $element is the div, not the td
                // to get the td, use $element.closest('td')
                //$element.removeClass('focused');
                RestoreCellStyle($element);
            },*/
            editable_selectAll: null,/*true,function (txt, columnIndex, $element) {
                // note $element is the div inside of the table cell, so use $element.closest('td') to get the cell
                // only select everthing within the element when the content starts with the letter "B"
                //return /^b/i.test(txt) && columnIndex === 0;
            },*/
            editable_wrapContent: null,//'<div>',       // wrap all editable cell content... makes this widget work in IE, and with autocomplete
            /*reorder_axis: 'x', // 'x' or 'xy'
            reorder_delay: 300,
            reorder_helperClass: 'tablesorter-reorder-helper',
            reorder_helperBar: 'tablesorter-reorder-helper-bar',
            reorder_noReorder: 'reorder-false',
            reorder_blocked: 'reorder-block-left reorder-block-end',
            reorder_complete: null // callback*/
        },
    }).tablesorterPager({
        // target the pager markup - see the HTML block below
        container: $(".pager"),

        // use this url format "http:/mydatabase.com?page={page}&size={size}" 
        ajaxUrl: "/application/vlansummarys?page={page}&size={size}&{sortList:col}&{filterList:fcol}",

        // modify the url after all processing has been applied
        customAjaxUrl: function(table, url) { return url; },

        ajaxProcessing: function (data) {
            if (data && data.hasOwnProperty('rows')) {
                var str = "", d = data.rows,
                    // total number of rows (required)
                    total = data.total_rows,
                    // len should match pager set size (c.size)
                    len = d.length;

                for (var i = 0; i < len; i++) {
                    str += '<tr>';
                    for (var column = 0; column < orderedFieldMapping.length; column++) {
                        //Distinzione temporanea per gestire i casi di dato non presente (Data Attivazione) e handler di selezione
                        if (orderedFieldMapping[column].toUpperCase() != 'ACTIVATIONDATE' || bReadOnly)
                            str += '<td class="' + orderedFieldMapping[column].toUpperCase() + '"' + ($('#' + orderedFieldMapping[column].toUpperCase()).prop('checked') ? '' : 'style="display:none;"') + '><div>' + (eval('d[i].' + orderedFieldMapping[column]) != null ? eval('d[i].' + orderedFieldMapping[column]) : '') + '</div></td>';
                        else
                            str += '<td title="Inserire la data nel formato gg/mm/aaaa (click per inserimento)" class="' + orderedFieldMapping[column].toUpperCase() + '"' + ($('#' + orderedFieldMapping[column].toUpperCase()).prop('checked') ? '' : 'style="display:none;"') + '><div contenteditable="true" ' + (eval('d[i].' + orderedFieldMapping[column]) != null ? '' : 'class="emptyPlaceholder" ') + 'onmouseup="javascript:SelectActivationDateText(this);" onblur="javascript:RestoreCellStyle(this);">' + (eval('d[i].' + orderedFieldMapping[column]) != null ? eval('d[i].' + orderedFieldMapping[column]) : emptyTextString) + '</div></td>';
                    }
                    str += '</tr>';
                }

                // in version 2.10, you can optionally return $(rows) a set of table rows within a jQuery object
                return [total, $(str)];
            }
        },

        ajaxObject: {
            dataType: 'json'
        },

        // output string - default is '{page}/{totalPages}';
        // possible variables:
        // {page}, {totalPages}, {startRow}, {endRow} and {totalRows}
        output: '{startRow} to {endRow} ({totalRows})',

        // apply disabled classname to the pager arrows when the rows at
        // either extreme is visible - default is true
        updateArrows: true,

        // starting page of the pager (zero based index)
        page: 0,

        // Number of visible rows - default is 10
        size: 20,

        //Reset pager to this page after filtering; set to desired page number (zero-based index), or false to not change page at filter start (Updated v2.16). 
        pageReset: false,

        // if true, the table will remain the same height no matter how many
        // records are displayed. The space is made up by an empty 
        // table row set to a height to compensate; default is false 
        fixedHeight: true,

        // remove rows from the table to speed up the sort of large tables.
        // setting this to false, only hides the non-visible rows; needed
        // if you plan to add/remove rows with the pager enabled.
        removeRows: false,

        // css class names of pager arrows
        // next page arrow
        cssNext: '.next',
        // previous page arrow
        cssPrev: '.prev',
        // go to first page arrow
        cssFirst: '.first',
        // go to last page arrow
        cssLast: '.last',
        // select dropdown to allow choosing a page
        cssGoto: '.gotoPage',
        // location of where the "output" is displayed
        cssPageDisplay: '.pagedisplay',
        // dropdown that sets the "size" option
        cssPageSize: '.pagesize',
        // class added to arrows when at the extremes 
        // (i.e. prev/first arrows are "disabled" when on the first page)
        // Note there is no period "." in front of this class name
        cssDisabled: 'disabled'
    }).children('tbody').on('editComplete', 'td', function() {
        var $this = $(this),
            //$allRows = $this.closest('table')[0].config.$tbodies.children('tr'),
            newContent = $this.text(),
            /*cellIndex = this.cellIndex, // there shouldn't be any colspans in the tbody
            rowIndex = $allRows.index($this.closest('tr')),*/
            id = $this.closest('tr').find('td.ID').text();

        $.ajax({
            type: "POST",
            crossDomain: true,
            url: '/application/vlansummarys/update/' + id,
            data: JSON.stringify({ activationDate: newContent }),
            dataType: "text",
            contentType: "application/json; charset=utf-8",
            error: function(xhr, textStatus, errorThrown) {
                alert("Errore durante l'inserimento: il dato inserito non è corretto. Contattare sistemi informativi se si ritiene che il messaggio d'errore sia sbagliato.");
            },
            success: function(data, textStatus, xhr) {
                //console.log(xhr);
                if ($this.find('div').hasClass('emptyPlaceholder'))
                    $this.find('div').removeClass('emptyPlaceholder');
            },
        });
    });
}

所以问题原来是寻呼机的问题addon/widget。编辑单元格时,editable 小部件会触发 "updateCell" 事件以更新内部缓存。此过程完成后,核心插件会触发 "updateComplete" 事件。

在寻呼程序代码中,当触发 "updateComplete" 事件时,它会不恰当地更新总行数和总页数,因为正在使用 ajax 并且它只计算当前在 [=] 中的行数27=].

所以基本上,我添加了一个检查以防止在使用 ajax 时重新计算 page/row 计数。

从 GitHub 存储库 master 分支中获取此 latest update for the pager