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

时间:2013-07-25 20:53:43

标签: java javascript json rest extjs

我有一个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中抛出的异常以及是否有异常我正在做什么来触发这种行为。任何见解都非常感激。除了这个缺陷外,网格效果很好。

2 个答案:

答案 0 :(得分:2)

我不确定这是否适合“回答”,但我没有足够的声誉来添加评论。我遇到了与4.2.1类似的问题,发现它们已在4.2.2中修复。截至目前,您需要获得高级支持才能获得该版本,这可能对您没有帮助。我没有尝试过旧版本,我正在使用另一个需要4.2版Ext JS的第三方库。

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

答案 1 :(得分:0)

你可以尝试使用像这样的覆盖

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;
}

});

确保在声明您的观点之前加入。 我无法重现这个问题所以它没有经过全面测试,所以如果它没有开箱即用,我会建议你调整一下,直到它处理挂起的异常其余的渲染操作。

希望这有助于您找到正确的解决方案。