ExtJS无限网格永远加载(totalProperty==pageSize)

ExtJS Infinite Grid Loads Forever (totalProperty == pageSize)

本文关键字:totalProperty pageSize 加载 无限 无限网 网格 永远 ExtJS      更新时间:2023-09-26

我有一个ExtJS网格(4.2.1版)。它是缓冲的,可以无限滚动。它由一个带有REST代理的存储填充。

请求发送至:

/endpoints.json?_dc=1374783152564&page=1&start=0&limit=30&sort=checkedAt&dir=desc&query=codian

json响应为(截断为1个结果):

{"links":[],
 "count":30,
 "totalCount":30,
 "endpointList":[{
    "links":[{"rel":"self","href":"http://localhost:8080/endpointmanager/endpoints/3"},
             {"rel":"currentStatus","href":"http://localhost:8080/endpointmanager/endpoints/3/status"},
             {"rel":"statusLog","href":"http://localhost:8080/endpointmanager/endpoints/3/statuslog"}],
    "name":"Codian MCU",     
    "managed":false,
    "updateInterval":3,
    "checkedAt":null,
    "timeout":60,
    "deviceType":"Codian MCU",
    "currentStatus":{"links":[],
                   "connected":false,
                   "description":"Unknown Status"}}]}

该响应正是我对该查询的期望。1页。30 totalCount。

请求下一页返回此响应:

{"links":[],"count":0,"totalCount":30,"endpointList":[]}

这对我来说也不错。

问题出在ExtJS上。当我的商店的pageSize=我的请求返回的totalCount属性时,网格旋转;无限加载或直到响应的totalCount!=pageSize

ExtJS在此无限加载期间抛出异常(ext-all-debug.js:104512):

Uncaught Error: NotFoundError: DOM Exception 8

Ext.view.NodeCache.scroll:中的周围代码

scroll: function(newRecords, direction, removeCount) {
    var me = this,
        elements = me.elements,
        recCount = newRecords.length,
        i, el, removeEnd,
        newNodes,
        nodeContainer = me.view.getNodeContainer(),
        frag = document.createDocumentFragment();

    if (direction == -1) {
        for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) {
            el = elements[i];
            delete elements[i];
            el.parentNode.removeChild(el);
        }
        me.endIndex -= removeCount;

        newNodes = me.view.bufferRender(newRecords, me.startIndex -= recCount);
        for (i = 0; i < recCount; i++) {
            elements[me.startIndex + i] = newNodes[i];
            frag.appendChild(newNodes[i]);
        }
        nodeContainer.insertBefore(frag, nodeContainer.firstChild);
    }

    else {
        removeEnd = me.startIndex + removeCount;
        for (i = me.startIndex; i < removeEnd; i++) {
            el = elements[i];
            delete elements[i];
            el.parentNode.removeChild(el);
        }
        me.startIndex = i;

        newNodes = me.view.bufferRender(newRecords, me.endIndex + 1);
        for (i = 0; i < recCount; i++) {
            elements[me.endIndex += 1] = newNodes[i];
            frag.appendChild(newNodes[i]);
            Uncaught Error: NotFoundError: DOM Exception 8 (repeated 5 times)
        }
        nodeContainer.appendChild(frag);
    }
    me.count = me.endIndex - me.startIndex + 1;
}

我的网格:

/**
 * TODO Known Defects: If the size of the result set returned by the search
 * filter == pageSize then the grid loads forever. The requests and responses
 * look correct, but an exception is thrown in extjs
 */
Ext.Loader.setPath('Ext.ux', 'resources/js/extjs/examples/ux');
Ext.require([ 'Ext.grid.*', 'Ext.data.*', 'Ext.util.*',
'Ext.grid.plugin.BufferedRenderer', 'Ext.ux.form.SearchField' ]);
Ext.onReady(function() {
    Ext.QuickTips.init();
    Ext.define('Endpoint', {
        extend : 'Ext.data.Model',
        fields : [ {
            name : 'deviceType',
            type : 'string',
        }, {
            name : 'name',
            type : 'string',
        }, {
            name : 'managed',
            type : 'boolean',
        }, {
            name : 'updateInterval',
            type : 'int',
        }, {
            name : 'timeout',
            type : 'int',
        }, {
            name : 'checkedAt',
            type : 'string',
        }, {
            name : 'currentStatus',
            mapping : 'currentStatus.description',
            type : 'string',
        } ],
    });
    var store = new Ext.create('Ext.data.Store', {
        id : 'store',
        model : 'Endpoint',
        autoLoad : true,
        buffered : true,
        pageSize : 30,
        leadingBufferZone : 5,
        remoteFilter : true,
        remoteSort : true,
        sorters : [ {
            property : 'checkedAt',
            direction : 'desc'
        } ],
        listeners : {
            totalcountchange : onStoreSizeChange
        },
        proxy : {
            type : 'rest',
            url : '/endpointmanager/endpoints.json',
            simpleSortMode : true,
            filterParam : 'query',
            encodeFilters : function(filters) {
                return filters[0].value;
            },
            reader : {
                type : 'json',
                root : 'endpointList',
                totalProperty : 'totalCount',
            },
            writer : {
                type : 'json',
            },
        },
    });
    function onStoreSizeChange() {
        grid.down('#status').update({
            count : store.getTotalCount()
        });
    }
    var grid = Ext
            .create(
                    'Ext.grid.Panel',
                    {
                        store : store,
                        renderTo : 'endpoint-grid',
                        height : 600,
                        forceFit : true,
                        columnLines : true,
                        stripeRows : true,
                        loadMask : true,
                        multiselect : true,
                        viewConfig : {
                            trackOver : false,
                            emptyText : '<h1 style="margin:20px">No matching endpoints found</h1>'
                        },
                        selModel : {
                            pruneRemoved : false
                        },                              
                        columns : [ {
                            xtype : 'checkcolumn',
                            header : 'Managed',
                            dataIndex : 'managed',
                            width : 100,
                            editor : {
                                xtype : 'checkbox',
                                cls : 'x-grid-checkheader-editor'
                            },
                        }, {
                            header : 'Checked At',
                            dataIndex : 'checkedAt',
                            renderer : dateConverter,
                            width : 160,
                        }, {
                            header : 'Device Type',
                            dataIndex : 'deviceType',
                            width : 160,
                        }, {
                            header : 'Name',
                            dataIndex : 'name',
                            width : 160,
                            flex : 1,
                        }, {
                            header : 'Update Interval (sec)',
                            dataIndex : 'updateInterval',
                            width : 160,
                            editor : {
                                xtype : 'numberfield',
                                allowBlank : false,
                                minValue : 10,
                                maxValue : 2600000
                            },
                        }, {
                            header : 'Timeout (sec)',
                            dataIndex : 'timeout',
                            width : 160,
                            editor : {
                                xtype : 'numberfield',
                                allowBlank : false,
                                minValue : -1,
                                maxValue : 3600,
                            }
                        }, {
                            header : 'Status',
                            dataIndex : 'currentStatus',
                            width : 160,
                        } ],
                        dockedItems : [
                                {
                                    xtype : 'toolbar',
                                    dock : 'top',
                                    items : [
                                            {
                                                width : 400,
                                                fieldLabel : 'Search',
                                                labelWidth : 50,
                                                xtype : 'searchfield',
                                                store : store
                                            },
                                            '->',
                                            {
                                                xtype : 'component',
                                                itemId : 'status',
                                                tpl : 'Matching Endpoints: {count}',
                                                style : 'margin-right:5px'
                                            } ]
                                },
                                {
                                    xtype : 'toolbar',
                                    cls : 'listFooter',
                                    dock : 'bottom',
                                    items : [
                                            {
                                                xtype : 'tbfill'
                                            },
                                            {
                                                text : 'remove',
                                                cls : 'gridButton',
                                                iconCls : 'icon-delete',
                                                handler : function() {
                                                    var selection = grid
                                                            .getView()
                                                            .getSelectionModel()
                                                            .getSelection()[0];
                                                    if (selection) {
                                                        store
                                                                .remove(selection);
                                                    }
                                                }
                                            },
                                            '-',
                                            {
                                                text : 'edit',
                                                cls : 'gridButton',
                                                iconCls : 'icon-edit',
                                                handler : function() {
                                                    store
                                                            .insert(
                                                                    0,
                                                                    new Endpoint());
                                                },
                                            },
                                            '-',
                                            {
                                                text : 'inline',
                                                cls : 'gridButton',
                                                iconCls : 'icon-add',
                                                handler : function() {
                                                    store
                                                            .insert(
                                                                    0,
                                                                    new Endpoint());
                                                },
                                            },
                                            '-',
                                            {
                                                text : 'add',
                                                cls : 'gridButton',
                                                iconCls : 'icon-add',
                                                handler : function() {
                                                    window.location = 'endpointManagementAdd';
                                                },
                                            } ],
                                } ],
                    });
    var task = {
        run : function() {
            store.load();
        },
        interval : 30000
    };
    // kick-off refresh task
    Ext.TaskManager.start(task);
});
function dateConverter(data, cell, record, rowIndex, columnIndex, store) {
  if (data == "") {
return;
  }
  var dt = new Date(parseInt(data));
  return dt.toLocaleString();
};

以前有人遇到过这个问题吗?我想知道是否有一种方法可以解决ExtJS中抛出的异常,以及我是否正在做一些异常来触发这种行为。任何见解,不胜感激。除了这一个缺陷外,电网运行良好。

我不确定这是否适合作为"答案",但目前我还没有足够的声誉来添加评论。我在4.2.1中遇到了类似的问题,发现它们在4.2.2中得到了解决。从现在起,你需要他们的高级支持才能获得该版本,所以这可能对你没有帮助。我还没有尝试过旧版本,我正在使用另一个需要4.2版本ExtJS的第三方库。

无论如何,我在Sencha论坛上发布了一个关于DOM异常的问题,以及加载指示器的一般奇怪行为。当我发现4.2.2解决了我的问题时,我尝试了这个场景,从服务器发回的总计数与数据存储页面大小相同,无法重现问题。

您可以尝试使用类似的覆盖

Ext.define('NodeCacheOverride', {
override: 'Ext.view.NodeCache',
/**
 * Appends/prepends records depending on direction flag
 * @param {Ext.data.Model[]} newRecords Items to append/prepend
 * @param {Number} direction `-1' = scroll up, `0` = scroll down.
 * @param {Number} removeCount The number of records to remove from the end. if scrolling
 * down, rows are removed from the top and the new rows are added at the bottom.
 */
scroll: function (newRecords, direction, removeCount) {
    var me = this,
        elements = me.elements,
        recCount = newRecords.length,
        i, el, removeEnd,
        newNodes,
        nodeContainer = me.view.getNodeContainer(),
        frag = document.createDocumentFragment();
    // Scrolling up (content moved down - new content needed at top, remove from bottom)
    if (direction == -1) {
        for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) {
            el = elements[i];
            delete elements[i];
            el.parentNode.removeChild(el);
        }
        me.endIndex -= removeCount;
        // grab all nodes rendered, not just the data rows
        newNodes = me.view.bufferRender(newRecords, me.startIndex -= recCount);
        for (i = 0; i < recCount; i++) {
            elements[me.startIndex + i] = newNodes[i];
            frag.appendChild(newNodes[i]);
        }
        nodeContainer.insertBefore(frag, nodeContainer.firstChild);
    }
    // Scrolling down (content moved up - new content needed at bottom, remove from top)
    else {

        if(me.count == me.endIndex + 1) return; // Override modification
        removeEnd = me.startIndex + removeCount;
        for (i = me.startIndex; i < removeEnd; i++) {
            el = elements[i];
            delete elements[i];
            el.parentNode.removeChild(el);
        }
        me.startIndex = i;
        // grab all nodes rendered, not just the data rows
        newNodes = me.view.bufferRender(newRecords, me.endIndex + 1);
        for (i = 0; i < recCount; i++) {
            elements[me.endIndex += 1] = newNodes[i];
            frag.appendChild(newNodes[i]);
        }
        nodeContainer.appendChild(frag);
    }
    // Keep count consistent.
    me.count = me.endIndex - me.startIndex + 1;
}

});

在声明您的视图之前,请确保包含它。我无法重现这个问题,所以它还没有经过完全测试,所以如果它不能开箱即用,我建议您进行一些调整,直到它处理了挂起渲染操作其余部分的异常。

希望这能帮助你找到正确的解决方案。