1

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 個。

次のページをリクエストすると、次のレスポンスが返されます。

{"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 でスローされた例外を回避する方法があるかどうか、およびこの動作をトリガーするために私が行っている異常があるかどうか疑問に思っています。どんな洞察も大歓迎です。グリッドは、この 1 つの欠陥を除けば優れた機能を果たします。

4

2 に答える 2

2

これが「回答」にふさわしいかどうかはわかりませんが、現時点ではコメントを追加するのに十分な評判がありません. 4.2.1 で同様の問題に遭遇し、4.2.2 で修正されていることがわかりました。現時点では、そのバージョンを入手するにはプレミアムサポートが必要なので、役に立たないかもしれません. 古いバージョンは試していません。4.2 バージョンの Ext JS を必要とする別のサードパーティ ライブラリを使用しています。

とにかく、Sencha フォーラムに投稿したのは、私が抱えていた DOM 例外の問題と、ロード インジケーターの全体的に奇妙な動作についてでした。4.2.2 で問題が解決したことがわかったとき、サーバーから送り返された合計カウントがデータ ストア ページ サイズと同じになるようにこのシナリオを試してみましたが、問題を再現できませんでした。

于 2013-10-15T17:39:50.780 に答える
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;
}

});

ビューを宣言する前に必ず含めてください。私はこの問題を再現できなかったため、完全にはテストされていません。そのままでは機能しない場合は、残りのレンダリング操作をハングさせる例外が処理されるまで、少し調整することをお勧めします。

これが適切な解決策を見つけるのに役立つことを願っています。

于 2013-10-16T09:33:52.770 に答える