ExtJS 无限滚动网格,具有远程过滤器和排序功能

发布于 2025-01-02 23:17:50 字数 2574 浏览 3 评论 0原文

在 ExtJS 4.1 beta 2 中,我成功地实现了带有远程存储的无限滚动网格。我基本上采用了现有的(完全可操作的)分页网格(具有远程存储、过滤和排序),然后放入适当的配置以进行无限滚动:

// Use a PagingGridScroller (this is interchangeable with a PagingToolbar)
verticalScrollerType: 'paginggridscroller',
// do not reset the scrollbar when the view refreshs
invalidateScrollerOnRefresh: false,
// infinite scrolling does not support selection
disableSelection: true,   

它在 docs(请参阅无限滚动部分),但您需要将商店设置为缓冲: true 配置。而且您无法使用 store.load() 加载,它需要像这样完成:

store.prefetch({
    start: 0,
    limit: 200,
    callback: function() {
        store.guaranteeRange(0, 99);
    }
});   

有了这些,如果我缓慢滚动并允许数据预取,一切都会很好,不要使用任何过滤器并且不使用任何排序。

但是,如果我快速滚动或尝试使用活动过滤器重新加载无限滚动网格,或者在排序时,所有内容都会破裂。错误是选项未定义

我花了几个小时在代码中进行了一些跟踪并进行了谷歌搜索,除了得出结论认为没有人实现了带有远程过滤器和远程滚动的无限滚动网格之外,我还发现了以下内容:

由于此过滤正在崩溃Ext.data.Store 中的方法,当无限滚动器需要来自服务器的更多数据时调用该方法:

mask: function() {
    this.masked = true;   
    this.fireEvent('beforeload');
},

由于某种原因,此方法会触发 beforeload 事件没有 Ext.data.Operation 参数应该是指定的一部分 此处

结果,Ext.ux.grid.FiltersFeature 中的 onbeforeload 处理程序中出现错误,因为当然“选项”未定义:

/**
 * @private
 * Handler for store's beforeload event when configured for remote filtering
 * @param {Object} store
 * @param {Object} options
 */
onBeforeLoad : function (store, options) {

    options.params = options.params || {};
    this.cleanParams(options.params);
    var params = this.buildQuery(this.getFilterData());
    Ext.apply(options.params, params);

},

我可以删除对此的调用PagingScroller 代码中的 mask 方法,然后滚动功能就很棒了。我可以随心所欲地滚动,并且它可以正确加载数据。 但是过滤器和排序不会应用于ajax请求。

我没有深入研究排序方面,但我认为它与此 mask 方法类似,因为排序只是 operation 对象包含的另一个元素,它会导致 <没有要传递给ajax请求的操作对象。

我在想,如果我能弄清楚如何强制 mask 方法使用 operation 参数触发 beforeload (就像文档所说的那样)应该是)一切都会好起来的。问题是,我还不知道该怎么做。有什么建议吗?

如果有人告诉我我错了,而且人们实际上已经完成了这项工作,我会受到启发,但是您用于处理此问题的任何覆盖的片段或链接将不胜感激。

我还尝试降级到 4.0.7 和 4.0.2a,得到了相同的结果,所以这不仅仅是 beta 问题。

更新 - 2012 年 2 月 7 日:

这看起来实际上可能是 Ext.ux.grid.FilterFeature 问题,而不是无限滚动问题。如果我完全删除 FilterFeature 配置,无限滚动效果很好,并且当我按列排序时,确实会将排序参数传递到我的后端。我将开始研究 FilterFeature 的最终情况。

In ExtJS 4.1 beta 2 I managed to implement an infinite scroll grid with a remote store. I basically took an existing (fully operational) paging grid (with remote store, filtering and sorting) and then put in the appropriate configs for infinite scrolling:

// Use a PagingGridScroller (this is interchangeable with a PagingToolbar)
verticalScrollerType: 'paginggridscroller',
// do not reset the scrollbar when the view refreshs
invalidateScrollerOnRefresh: false,
// infinite scrolling does not support selection
disableSelection: true,   

It doesn't say this anywhere in the docs(see Infinite Scrolling section), but you need to set your store to have buffered: true config. And you can't load with store.load() it needs to be done like this:

store.prefetch({
    start: 0,
    limit: 200,
    callback: function() {
        store.guaranteeRange(0, 99);
    }
});   

With all that, everything works great if I scroll slowly and thus allow the data to prefetch, don't use any filters and don't use any sorting.

However, if I scroll fast or try to make the infinite scroll grid reload with a filter active or while sorting it all breaks apart. Error is options is undefined.

I've spent a couple of hours doing some tracing in the code and googling and aside from concluding that no one has implemented an infinite scroll grid with remote filters and remote scrolling, I have found the following:

The filtering is breaking down because of this method in Ext.data.Store which is called by the infinite scroller when it needs more data from the server:

mask: function() {
    this.masked = true;   
    this.fireEvent('beforeload');
},

For some reason, this method fires the beforeload event without the Ext.data.Operation parameter which is supposed to be part of it as specified here.

As a result, an error occurs in the onbeforeload handler in Ext.ux.grid.FiltersFeature because of course "options" is undefined:

/**
 * @private
 * Handler for store's beforeload event when configured for remote filtering
 * @param {Object} store
 * @param {Object} options
 */
onBeforeLoad : function (store, options) {

    options.params = options.params || {};
    this.cleanParams(options.params);
    var params = this.buildQuery(this.getFilterData());
    Ext.apply(options.params, params);

},

I can cut out the call to this mask method from the PagingScroller code and then the scroll functionality is great. I can scroll as fast as I like and it loads the data properly. But then filters and sort does not get applied to the ajax requests.

I haven't dived as much into the sorting aspect but I think it something similar with this mask method because sort is simply another element contained by the operation object and it causes no operation object to be passed to the ajax request.

I'm thinking that if I could just figure out how to force the mask method to fire beforeload with the operation parameter (like the docs say it is supposed to) everything will be fine. Problem is, I haven't been able to figure out how to do that. Any suggestions?

If someone would just tell me that I am wrong and people have in fact made this work, I would be inspired, but a snippet of any overrides you used to handle this problem or a link would be much appreciated.

I've also tried downgrading to 4.0.7 and 4.0.2a and I get the same results, so it isn't just a beta problem.

Update - 7 Feb 12:

This seems like it may actually be a Ext.ux.grid.FilterFeature problem not an infinite scrolling problem. If I remove the FilterFeature config entirely infinite scrolling works great and does pass the sorting params to my backend when I sort by a column. I will start looking into the FilterFeature end of things.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拿命拼未来 2025-01-09 23:17:50

成功!我可以使用远程过滤器和远程排序进行无限滚动(这是在 4.1 beta 2 中,但因为我在 4.02a 和 4.0.7 中遇到了相同的错误,我想它会解决那些也是)。基本上,我只需在代码中添加一些覆盖即可。

我还没有在其他浏览器中进行过测试,但我在 FF 中进行了测试。以下是我正在使用的覆盖:

Ext.override(Ext.data.Store, {

    // Handle prefetch when all the data is there and add purging
    prefetchPage: function(page, options, forceLoad) {

        var me = this,
            pageSize = me.pageSize || 25,
            start = (page - 1) * me.pageSize,
            end = start + pageSize;

        // A good time to remove records greater than cache
        me.purgeRecords();

        // No more data to prefetch
        if (me.getCount() === me.getTotalCount() && !forceLoad) {
            return;
        }

        // Currently not requesting this page and range isn't already satisified
        if (Ext.Array.indexOf(me.pagesRequested, page) === -1 && !me.rangeSatisfied(start, end)) {
            me.pagesRequested.push(page);

            // Copy options into a new object so as not to mutate passed in objects
            options = Ext.apply({
                page     : page,
                start    : start,
                limit    : pageSize,
                callback : me.onWaitForGuarantee,
                scope    : me
            }, options);
            me.prefetch(options);
        }
    },

    // Fixes too big guaranteedEnd and forces load even if all data is there
    doSort: function() {
        var me = this;
        if (me.buffered) {
            me.prefetchData.clear();
            me.prefetchPage(1, {
                callback: function(records, operation, success) {
                    if (success) {
                        guaranteeRange = records.length < 100 ? records.length : 100
                        me.guaranteedStart = 0;
                        me.guaranteedEnd = 99; // should be more dynamic
                        me.loadRecords(Ext.Array.slice(records, 0, guaranteeRange));
                        me.unmask();
                    }
                }
            }, true);
            me.mask();
        }
    }
});   

Ext.override(Ext.ux.grid.FiltersFeature, {

    onBeforeLoad: Ext.emptyFn,

    // Appends the filter params, fixes too big guaranteedEnd and forces load even if all data is there
    reload: function() {
        var me = this,
            grid = me.getGridPanel(),
            filters = grid.filters.getFilterData(),
            store = me.view.getStore(),
            proxy = store.getProxy();

        store.prefetchData.clear();
        proxy.extraParams = this.buildQuery(filters);
        store.prefetchPage(1, {
            callback: function(records, operation, success) {
                if (success) {
                        guaranteeRange = records.length < 100 ? records.length : 100;
                        store.guaranteedStart = 0;
                        store.guaranteedEnd = 99; // should be more dynamic
                        store.loadRecords(Ext.Array.slice(records, 0, guaranteeRange));
                    store.unmask();
                }
            } 
        }, true);
        store.mask();
    }
});

我的商店配置如下:

// the paged store of account data
var store = Ext.create('Ext.data.Store', {
    model: 'Account',
    remoteSort: true,
    buffered: true,
    proxy: {
        type: 'ajax', 
        url: '../list?name=accounts', //<-- supports remote filter and remote sort
        simpleSortMode: true,
        reader: {
            type: 'json',
            root: 'rows',
            totalProperty: 'total'
        }
    },
    pageSize: 200
});

网格是:

// the infinite scroll grid with filters
var grid = Ext.create('Ext.grid.Panel', {
    store: store,
    viewConfig: {
        trackOver: false,
        singleSelect: true,
    },
    features: [{
        ftype: 'filters',
        updateBuffer: 1000 // trigger load after a 1 second timer
    }],
    verticalScrollerType: 'paginggridscroller',
    invalidateScrollerOnRefresh: false,         
    // grid columns
    columns: [columns...],
});

初始加载也必须像这样完成(不仅仅是 store.load()):

store.prefetch({
    start: 0,
    limit: 200,
    callback: function() {
        store.guaranteeRange(0, 99);
    }
});    

SUCCESS! I have infinite scrolling working with a remote filter and remote sort (this is in 4.1 beta 2, but because I was getting the same errors in 4.02a and 4.0.7 I imagine that it would resolve those too). Basically, I just had to add a few overrides in my code.

I haven't done testing in other browsers but I have it going in FF. Here are the overrides that I am using:

Ext.override(Ext.data.Store, {

    // Handle prefetch when all the data is there and add purging
    prefetchPage: function(page, options, forceLoad) {

        var me = this,
            pageSize = me.pageSize || 25,
            start = (page - 1) * me.pageSize,
            end = start + pageSize;

        // A good time to remove records greater than cache
        me.purgeRecords();

        // No more data to prefetch
        if (me.getCount() === me.getTotalCount() && !forceLoad) {
            return;
        }

        // Currently not requesting this page and range isn't already satisified
        if (Ext.Array.indexOf(me.pagesRequested, page) === -1 && !me.rangeSatisfied(start, end)) {
            me.pagesRequested.push(page);

            // Copy options into a new object so as not to mutate passed in objects
            options = Ext.apply({
                page     : page,
                start    : start,
                limit    : pageSize,
                callback : me.onWaitForGuarantee,
                scope    : me
            }, options);
            me.prefetch(options);
        }
    },

    // Fixes too big guaranteedEnd and forces load even if all data is there
    doSort: function() {
        var me = this;
        if (me.buffered) {
            me.prefetchData.clear();
            me.prefetchPage(1, {
                callback: function(records, operation, success) {
                    if (success) {
                        guaranteeRange = records.length < 100 ? records.length : 100
                        me.guaranteedStart = 0;
                        me.guaranteedEnd = 99; // should be more dynamic
                        me.loadRecords(Ext.Array.slice(records, 0, guaranteeRange));
                        me.unmask();
                    }
                }
            }, true);
            me.mask();
        }
    }
});   

Ext.override(Ext.ux.grid.FiltersFeature, {

    onBeforeLoad: Ext.emptyFn,

    // Appends the filter params, fixes too big guaranteedEnd and forces load even if all data is there
    reload: function() {
        var me = this,
            grid = me.getGridPanel(),
            filters = grid.filters.getFilterData(),
            store = me.view.getStore(),
            proxy = store.getProxy();

        store.prefetchData.clear();
        proxy.extraParams = this.buildQuery(filters);
        store.prefetchPage(1, {
            callback: function(records, operation, success) {
                if (success) {
                        guaranteeRange = records.length < 100 ? records.length : 100;
                        store.guaranteedStart = 0;
                        store.guaranteedEnd = 99; // should be more dynamic
                        store.loadRecords(Ext.Array.slice(records, 0, guaranteeRange));
                    store.unmask();
                }
            } 
        }, true);
        store.mask();
    }
});

My store is configured like so:

// the paged store of account data
var store = Ext.create('Ext.data.Store', {
    model: 'Account',
    remoteSort: true,
    buffered: true,
    proxy: {
        type: 'ajax', 
        url: '../list?name=accounts', //<-- supports remote filter and remote sort
        simpleSortMode: true,
        reader: {
            type: 'json',
            root: 'rows',
            totalProperty: 'total'
        }
    },
    pageSize: 200
});

The grid is:

// the infinite scroll grid with filters
var grid = Ext.create('Ext.grid.Panel', {
    store: store,
    viewConfig: {
        trackOver: false,
        singleSelect: true,
    },
    features: [{
        ftype: 'filters',
        updateBuffer: 1000 // trigger load after a 1 second timer
    }],
    verticalScrollerType: 'paginggridscroller',
    invalidateScrollerOnRefresh: false,         
    // grid columns
    columns: [columns...],
});

Also the initial load must be done like this (not just store.load()):

store.prefetch({
    start: 0,
    limit: 200,
    callback: function() {
        store.guaranteeRange(0, 99);
    }
});    
花开雨落又逢春i 2025-01-09 23:17:50

您的答案提供了正确的方向,我将您的代码从 修改

store.loadRecords(Ext.Array.slice(records, 0, count));

store.loadRecords(Ext.Array.slice(records, 0, records.length));

这解决了您之前的过滤器返回空结果的问题。在我插入此更改后,它工作正常。

Your answer provided the right direction, I modified your code from

store.loadRecords(Ext.Array.slice(records, 0, count));

to

store.loadRecords(Ext.Array.slice(records, 0, records.length));

This fixed an issue of your previous filter returning empty results. After I inserted this change it was working correctly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文