3

我正在使用 extjs 4.0 并有一个带有 queryMode 'remote' 的组合框。我用来自服务器的数据填充它。问题是来自服务器的记录数量太大,所以我认为最好按部分加载它们。我知道组合框有一个标准的分页器工具,但它不方便,因为需要记录总数。问题是,有没有办法为组合框添加动态滚动?当滚动到列表底部时,我想发送下一部分记录的请求并将它们添加到列表中。我找不到合适的听众来做这件事。

已更新找到解决方案,已发布在答案中

4

4 回答 4

6

以下是我的组合框无限滚动的解决方案,Extjs 4.0

Ext.define('TestProject.testselect', {
    extend:'Ext.form.field.ComboBox',
    alias: ['widget.testselect'],
    requires: ['Ext.selection.Model', 'Ext.data.Store'],

    /**
     * This will contain scroll position when user reaches the bottom of the list 
     * and the store begins to upload data
     */
    beforeRefreshScrollTop: 0,

    /**
     * This will be changed to true, when there will be no more records to upload
     * to combobox
     */
    isStoreEndReached : false,

    /**
     * The main thing. When creating picker, we add scroll listener on list dom element.
     * Also add listener on load mask - after load mask is hidden we set scroll into position 
     * that it was before new items were loaded to list. This prevents 'jumping' of the scroll.
     */
    createPicker: function() {
        var me = this,
        picker = me.callParent(arguments);
        me.mon(picker, {
            'render' : function() {
                Ext.get(picker.getTargetEl().id).on('scroll', me.onScroll, me);
                me.mon(picker.loadMask, {
                   'hide' : function() {
                      Ext.get(picker.id + '-listEl').scroll("down", me.beforeRefreshScrollTop, false);
                   },
                   scope: me
                });
            },
            scope: me
        });
        return picker;
    },

    /**
     * Method which is called when user scrolls the list. Checks if the bottom of the 
     * list is reached. If so - sends 'nextPage' request to store and checks if 
     * any records were received. If not - then there is no more records to load, and 
     * from now on if user will reach the bottom of the list, no request will be sent.
     */
    onScroll: function(){
        var me = this,
        parentElement = Ext.get(me.picker.getTargetEl().id),
        parentElementTop = parentElement.getScroll().top,
        scrollingList = Ext.get(me.picker.id+'-items');
        if(scrollingList != undefined) {
            if(!me.isStoreEndReached && parentElementTop >= scrollingList.getHeight() - parentElement.getHeight()) {
                var multiselectStore = me.getStore(),
                beforeRequestCount = multiselectStore.getCount();
                me.beforeRefreshScrollTop = parentElementTop;
                multiselectStore.nextPage({
                    params: this.getParams(this.lastQuery),
                    callback: function() {
                            me.isStoreEndReached = !(multiselectStore.getCount() - beforeRequestCount > 0);
                        }
                });
            }
        }
    },

    /**
     * Took this method from Ext.form.field.Picker to collapse only if 
     * loading finished. This solve problem when user scrolls while large data is loading.
     * Whithout this the list will close before finishing update.
     */
    collapse: function() {
        var me = this;
        if(!me.getStore().loading) {
            me.callParent(arguments);
        }
    },

    /**
     * Reset scroll and current page of the store when loading all profiles again (clicking on trigger)
     */
    doRawQuery: function() {
        var me = this;
        me.beforeRefreshScrollTop = 0;
        me.getStore().currentPage = 0;
        me.isStoreEndReached = false;
        me.callParent(arguments);
    }
});

创建元素时,应该将 id 传递给 listConfig,我也将模板传递给列表,因为我需要它带有 id。我没有找到更优雅的方法来做到这一点。我很感激任何建议。

{
                    id: 'testcombo-multiselect',
                    xtype: 'testselect',
                    store: Ext.create('TestProject.testStore'),
                    queryMode: 'remote',
                    queryParam: 'keyword',
                    valueField: 'profileToken',
                    displayField: 'profileToken',
                    tpl: Ext.create('Ext.XTemplate',
                        '<ul id="ds-profiles-boundlist-items"><tpl for=".">',
                            '<li role="option" class="' + Ext.baseCSSPrefix + 'boundlist-item' + '">',
                                '{profileToken}',
                            '</li>',
                        '</tpl></ul>'
                    ),
                    listConfig: {
                        id: 'testcombo-boundlist'
                    }
                },

还有商店:

Ext.define('TestProject.testStore',{
    extend: 'Ext.data.Store',
    storeId: 'teststore',
    model: 'TestProject.testModel',
    pageSize: 13, //the bulk of records to receive after each upload
    currentPage: 0, //server side works with page numeration starting with zero
    proxy: {
        type: 'rest',
        url: serverurl,
        reader: 'json'
    },
    clearOnPageLoad: false //to prevent replacing list items with new uploaded items
});
于 2013-01-18T17:41:16.613 回答
3

感谢 me1111 为我们指明了方向。

Ext.define('utils.fields.BoundList', {
    override:'Ext.view.BoundList',
    ///@function utils.fields.BoundList.loadNextPageOnScroll
    ///Add scroll listener to load next page if true.
    ///@since 1.0
    loadNextPageOnScroll:true,
    ///@function utils.fields.BoundList.afterRender
    ///Add scroll listener to load next page if required.
    ///@since 1.0
    afterRender:function(){
        this.callParent(arguments);

        //add listener
        this.loadNextPageOnScroll
        &&this.getTargetEl().on('scroll', function(e, el){
            var store=this.getStore();
            var top=el.scrollTop;
            var count=store.getCount()
            if(top>=el.scrollHeight-el.clientHeight//scroll end
               &&count<store.getTotalCount()//more data
              ){
                  //track state
                  var page=store.currentPage;
                  var clearOnPageLoad=store.clearOnPageLoad;
                  store.clearOnPageLoad=false;

                  //load next page
                  store.loadPage(count/store.pageSize+1, {
                      callback:function(){//restore state
                          store.currentPage=page;
                          store.clearOnPageLoad=clearOnPageLoad;
                          el.scrollTop=top;
                      }
                  });
              }
        }, this);
    },
});
于 2014-09-05T03:06:34.950 回答
2

您可以将无限网格实现为组合框列表。看这个例子来实现另一个选择器:

http://www.sencha.com/forum/showthread.php?132328-CLOSED-ComboBox-using-Grid-instead-of-BoundList

于 2013-01-15T07:43:09.857 回答
1

如果有人在 ExtJS 版本 6 中需要这个,这里是代码:

Ext.define('Test.InfiniteCombo', {
    extend: 'Ext.form.field.ComboBox',
    alias: ['widget.infinitecombo'],

    /**
     * This will contain scroll position when user reaches the bottom of the list
     * and the store begins to upload data
     */
    beforeRefreshScrollTop: 0,

    /**
     * This will be changed to true, when there will be no more records to upload
     * to combobox
     */
    isStoreEndReached: false,

    /**
     * The main thing. When creating picker, we add scroll listener on list dom element.
     * Also add listener on load mask - after load mask is hidden we set scroll into position
     * that it was before new items were loaded to list. This prevents 'jumping' of the scroll.
     */
    createPicker: function () {
        var me = this,
            picker = me.callParent(arguments);
        me.mon(picker, {
            'afterrender': function () {
                picker.on('scroll', me.onScroll, me);
                me.mon(picker.loadMask, {
                    'hide': function () {
                        picker.scrollTo(0, me.beforeRefreshScrollTop,false);
                    },
                    scope: me
                });
            },
            scope: me
        });
        return picker;
    },

    /**
     * Method which is called when user scrolls the list. Checks if the bottom of the
     * list is reached. If so - sends 'nextPage' request to store and checks if
     * any records were received. If not - then there is no more records to load, and
     * from now on if user will reach the bottom of the list, no request will be sent.
     */
    onScroll: function () {
        var me = this,
            parentElement = me.picker.getTargetEl(),
            scrollingList = Ext.get(me.picker.id + '-listEl');
        if (scrollingList != undefined) {
            if (!me.isStoreEndReached && me.picker.getScrollY() + me.picker.getHeight() > parentElement.getHeight()) {
                var store = me.getStore(),
                    beforeRequestCount = store.getCount();
                me.beforeRefreshScrollTop = me.picker.getScrollY();
                store.nextPage({
                    params: this.getParams(this.lastQuery),
                    callback: function () {
                        me.isStoreEndReached = !(store.getCount() - beforeRequestCount > 0);
                    }
                });
            }
        }
    },

    /**
     * Took this method from Ext.form.field.Picker to collapse only if
     * loading finished. This solve problem when user scrolls while large data is loading.
     * Whithout this the list will close before finishing update.
     */
    collapse: function () {
        var me = this;
        if (!me.getStore().loading) {
            me.callParent(arguments);
        }
    },

    /**
     * Reset scroll and current page of the store when loading all profiles again (clicking on trigger)
     */
    doRawQuery: function () {
        var me = this;
        me.beforeRefreshScrollTop = 0;
        me.getStore().currentPage = 1;
        me.isStoreEndReached = false;
        me.callParent(arguments);
    }
});
于 2016-03-18T09:00:04.107 回答