`

ExtJS5的grid filter改造

阅读更多

grid控件的检索是前端界面最常见的功能之一。

ExtJS5提供了官方的grid通用检索实现:http://dev.sencha.com/ext/5.1.0/examples/kitchensink/#grid-filtering

该实现用了几个子类分别支持不同的检索类型:



 

 

各种类型的效果如下:

数字型:


 

文本型包含检索:
 

 

枚举型检索:
 

日期型检索:
 

布尔型检索:
 

 

最值得称道的是:上述检索的应用,只需要在grid的config中声明引用该plugin,并在grid的column config中定义检索类型即可。

将这种检索形式给客户演示时,客户方认为这种检索形式比较隐蔽和零散,如果需要检索的列在最右边,需要滚动到最右再检索。

窃以为客户的话有道理,国人的使用习惯的确和老外常常背道而驰,于是决定实现一种如下的检索形式:将所有检索归并到工具栏的一个下拉菜单项。

 

鉴于ExtJS官方提供的上述分类检索实现合理而且强大,不想另起炉灶从头做起,我的解决思路是改写一个plugin 封装和引用ExtJS的具体检索实现:

代码如下:

Ext.define('DCApp.view.GridFilters', {
    extend: 'Ext.plugin.Abstract',

    requires: [
        'Ext.grid.filters.filter.*'
    ],

    mixins: [
        'Ext.util.StoreHolder'
    ],

    alias: 'plugin.gfilters',

    pluginId: 'gfilters',

    /**
     * @property {Object} defaultFilterTypes
     * This property maps {@link Ext.data.Model#cfg-field field type} to the appropriate
     * grid filter type.
     * @private
     */
    defaultFilterTypes: {
        'boolean': 'boolean',
        'int': 'number',
        date: 'date',
        number: 'number'
    },

    /**
     * @property {String} [filterCls="x-grid-filters-filtered-column"]
     * The CSS applied to column headers with active filters.
     */
    filterCls: Ext.baseCSSPrefix + 'grid-filters-filtered-column',

    /**
     * @cfg {String} [menuFilterText="Filters"]
     * The text for the filters menu.
     */
    menuFilterText: 'Filters',

    /**
     * @cfg {Boolean} showMenu
     * Defaults to true, including a filter submenu in the default header menu.
     */
    showMenu: true,

    /**
     * @cfg {String} stateId
     * Name of the value to be used to store state information.
     */
    stateId: undefined,

    init: function (grid) {
        var me = this,
            store, headerCt;

        //<debug>
        Ext.Assert.falsey(me.grid);
        //</debug>

        me.grid = grid;
        grid.filters = me;

        if (me.grid.normalGrid) {
            me.isLocked = true;
        }

        grid.clearFilters = me.clearFilters.bind(me);

        store = grid.store;
        headerCt = grid.headerCt;
//c4w 将菜单从列头位置改到工具栏,不再为列头菜单生成filter菜单项
/*        headerCt.on({
            scope: me,
            add: me.onAdd,
            menucreate: me.onMenuCreate
        });
*/
        grid.on({
            scope: me,
            destroy: me.onGridDestroy,
            beforereconfigure: me.onBeforeReconfigure,
            reconfigure: me.onReconfigure
        });

        me.bindStore(store);

        if (grid.stateful) {
            store.statefulFilters = true;
        }

        me.initColumns();
    },

    /**
     * Creates the Filter objects for the current configuration.
     * Reconfigure and on add handlers.
     * @private
     */
    initColumns: function () {
        var grid = this.grid,
            store = grid.getStore(),
            columns = grid.columnManager.getColumns(),
            len = columns.length,
            i, column,
            filter, filterCollection, block;

        // We start with filters defined on any columns.
        //c4w create menu on toolbar
        var me = this;    
        var its=[];
        for (i = 0; i < len; i++) {
            column = columns[i];
            filter = column.filter;

            if (filter && !filter.isGridFilter) {
                if (!filterCollection) {
                    filterCollection = store.getFilters();
                    filterCollection.beginUpdate();
                }
                this.createColumnFilter(column);
                column.filter.createMenu();
				its.push({text:column.text,
					checked:false,
					menu:column.filter.menu,
					filter:column.filter,
		          	listeners: {
		                scope: me,
		                checkchange: me.onCheckChange,
                		activate:me.onBeforeActivate
		            }
  				});
            }
        }
        //
		var tbar = grid.down('toolbar');
 		tbar.insert(0,{
			xtype:'splitbutton',
            text:'检索',
            iconCls: null,
            menu:its,
            handler: 'onClearFilters',
            scope: me
        });
        if (filterCollection) {
            filterCollection.endUpdate();
        }
    },
    
	onClearFilters: function () {
        // The "filters" property is added to the grid (this) by gridfilters
        this.clearFilters();
    },

    createColumnFilter: function (column) {
        var me = this,
            columnFilter = column.filter,
            filter = {
                column: column,
                grid: me.grid,
                owner: me
            },
            field, model, type;

        if (Ext.isString(columnFilter)) {
            filter.type = columnFilter;
        } else {
            Ext.apply(filter, columnFilter);
        }

        if (!filter.type) {
            model = me.store.getModel();
            // If no filter type given, first try to get it from the data field.
            field = model && model.getField(column.dataIndex);
            type = field && field.type;

            filter.type = (type && me.defaultFilterTypes[type]) ||
                           column.defaultFilterType || 'string';
        }

        column.filter = Ext.Factory.gridFilter(filter);
    },

    onAdd: function (headerCt, column, index) {
        var filter = column.filter;

        if (filter && !filter.isGridFilter) {
            this.createColumnFilter(column);
        }
    },

    /**
     * @private Handle creation of the grid's header menu.
     */
    onMenuCreate: function (headerCt, menu) {
        menu.on({
            beforeshow: this.onMenuBeforeShow,
            scope: this
        });
    },

    /**
     * @private Handle showing of the grid's header menu. Sets up the filter item and menu
     * appropriate for the target column.
     */
    onMenuBeforeShow: function (menu) {
        var me = this,
            menuItem, filter, ownerGrid, ownerGridId;

        if (me.showMenu) {
            // In the case of a locked grid, we need to cache the 'Filters' menuItem for each grid since
            // there's only one Filters instance. Both grids/menus can't share the same menuItem!
            if (!me.menuItems) {
                me.menuItems = {};
            }

            // Don't get the owner grid if in a locking grid since we need to get the unique menuItems key.
            ownerGrid = menu.up('grid');
            ownerGridId = ownerGrid.id;

            menuItem = me.menuItems[ownerGridId];

            if (!menuItem || menuItem.isDestroyed) {
                menuItem = me.createMenuItem(menu, ownerGridId);
            }

            me.activeFilterMenuItem = menuItem;

            filter = me.getMenuFilter(ownerGrid.headerCt);
            if (filter) {
                filter.showMenu(menuItem);
            }

            menuItem.setVisible(!!filter);
            me.sep.setVisible(!!filter);
        }
    },

    createMenuItem: function (menu, ownerGridId) {
        var me = this,
            item;

        me.sep = menu.add('-');

        item = menu.add({
            checked: false,
            itemId: 'filters',
            text: me.menuFilterText,
            listeners: {
                scope: me,
                checkchange: me.onCheckChange
            }
        });

        return (me.menuItems[ownerGridId] = item);
    },

    /**
     * Handler called by the grid 'beforedestroy' event
     */
    onGridDestroy: function () {
        var me = this,
            menuItems = me.menuItems,
            item;

        me.bindStore(null);
        me.sep = Ext.destroy(me.sep);

        for (item in menuItems) {
            menuItems[item].destroy();
        }

        me.grid = null;
    },

    onUnbindStore: function(store) {
        store.getFilters().un('remove', this.onFilterRemove, this);
    },

    onBindStore: function(store, initial, propName) {
        this.local = !store.getRemoteFilter();
        store.getFilters().on('remove', this.onFilterRemove, this);
    },

    onFilterRemove: function (filterCollection, list) {
        // We need to know when a store filter has been removed by an operation of the gridfilters UI, i.e.,
        // store.clearFilter().  The preventFilterRemoval flag lets us know whether or not this listener has been
        // reached by a filter operation (preventFilterRemoval === true) or by something outside of the UI
        // (preventFilterRemoval === undefined).
        var len = list.items.length,
            columnManager = this.grid.columnManager,
            i, item, header, filter;


        for (i = 0; i < len; i++) {
            item = list.items[i];

            header = columnManager.getHeaderByDataIndex(item.getProperty());
            if (header) {
                // First, we need to make sure there is indeed a filter and that its menu has been created. If not,
                // there's no point in continuing.
                //
                // Also, even though the store may be filtered by this dataIndex, it doesn't necessarily mean that
                // it was created via the gridfilters API. To be sure, we need to check the prefix, as this is the
                // only way we can be sure of its provenance (note that we can't check `operator`).
                //
                // Note that we need to do an indexOf check on the string because TriFilters will contain extra
                // characters specifiying its type.
                //
                // TODO: Should we support updating the gridfilters if one or more of its filters have been removed
                // directly by the bound store?
                filter = header.filter;
                if (!filter || !filter.menu || item.getId().indexOf(filter.getBaseIdPrefix()) === -1) {
                    continue;
                }

                if (!filter.preventFilterRemoval) {
                    // This is only called on the filter if called from outside of the gridfilters UI.
                    filter.onFilterRemove(item.getOperator());
                }
            }
        }
    },

    /**
     * @private
     * Get the filter menu from the filters MixedCollection based on the clicked header.
     */
    getMenuFilter: function (headerCt) {
        return headerCt.getMenu().activeHeader.filter;
    },
	onBeforeActivate:function(item,value){
		this.activeFilterMenuItem=item;
		this.activeFilterMenuItem.activeFilter=item.filter;
	},
    /** @private */
    onCheckChange: function (item, value) {
        // Locking grids must lookup the correct grid.
        var grid = this.isLocked ? item.up('grid') : this.grid,
            //filter = this.getMenuFilter(grid.headerCt);
        	filter = item.filter;

        filter.setActive(value);
    },

    getHeaders: function () {
        return this.grid.view.headerCt.columnManager.getColumns();
    },

    /**
     * Checks the plugin's grid for statefulness.
     * @return {Boolean}
     */
    isStateful: function () {
        return this.grid.stateful;
    },

    /**
     * Adds a filter to the collection and creates a store filter if has a `value` property.
     * @param {Object/Ext.grid.filter.Filter} filters A filter configuration or a filter object.
     */
    addFilter: function (filters) {
        var me = this,
            grid = me.grid,
            store = me.store,
            hasNewColumns = false,
            suppressNextFilter = true,
            dataIndex, column, i, len, filter, columnFilter;

        if (!Ext.isArray(filters)) {
            filters = [filters];
        }

        for (i = 0, len = filters.length; i < len; i++) {
            filter = filters[i];
            dataIndex = filter.dataIndex;
            column = grid.columnManager.getHeaderByDataIndex(dataIndex);

            // We only create filters that map to an existing column.
            if (column) {
                hasNewColumns = true;

                // Don't suppress active filters.
                if (filter.value) {
                    suppressNextFilter = false;
                }

                columnFilter = column.filter;

                // If already a gridfilter, let's destroy it and recreate another from the new config.
                if (columnFilter && columnFilter.isGridFilter) {
                    columnFilter.deactivate();
                    columnFilter.destroy();

                    if (me.activeFilterMenuItem) {
                        me.activeFilterMenuItem.menu = null;
                    }
                }

                column.filter = filter;
            }
        }

        // Batch initialize all column filters.
        if (hasNewColumns) {
            store.suppressNextFilter = suppressNextFilter;
            me.initColumns();
            store.suppressNextFilter = false;
        }
    },

    /**
     * Adds filters to the collection.
     * @param {Array} filters An Array of filter configuration objects.
     */
    addFilters: function (filters) {
        if (filters) {
            this.addFilter(filters);
        }
    },

    /**
     * Turns all filters off. This does not clear the configuration information.
     * @param {Boolean} autoFilter If true, don't fire the deactivate event in
     * {@link Ext.grid.filters.filter.Base#setActive setActive}.
     */
    clearFilters: function (autoFilter) {
        var grid = this.grid,
            columns = grid.columnManager.getColumns(),
            store = grid.store,
            oldAutoFilter = store.getAutoFilter(),
            column, filter, i, len, filterCollection;

        if (autoFilter !== undefined) {
            store.setAutoFilter(autoFilter);
        }

        // We start with filters defined on any columns.
        for (i = 0, len = columns.length; i < len; i++) {
            column = columns[i];
            filter = column.filter;

            if (filter && filter.isGridFilter) {
                if (!filterCollection) {
                    filterCollection = store.getFilters();
                    filterCollection.beginUpdate();
                }

                filter.setActive(false);
            }
        }

        if (filterCollection) {
            filterCollection.endUpdate();
        }

        if (autoFilter !== undefined) {
            store.setAutoFilter(oldAutoFilter);
        }
    },

    onBeforeReconfigure: function(grid, store, columns) {
        if (store) {
            store.getFilters().beginUpdate();
        }

        this.reconfiguring = true;
    },

    onReconfigure: function(grid, store, columns, oldStore) {
        var me = this;

        if (store && oldStore !== store) {
            me.bindStore(store);
        }

        if (columns) {
            me.initColumns();
        }
        
        if (store) {
            store.getFilters().endUpdate();
        }

        me.reconfiguring = false;
    }
});

 

在线调用示例:

https://fiddle.sencha.com/#fiddle/mje

 

 

 

 

  • 大小: 145.8 KB
  • 大小: 145.6 KB
  • 大小: 151.2 KB
  • 大小: 190.9 KB
  • 大小: 157.1 KB
  • 大小: 164.4 KB
  • 大小: 159.5 KB
  • 大小: 15.3 KB
0
0
分享到:
评论

相关推荐

    Extjs4.2 Grid filter Demo 表格过滤实验

    这是利用sencha cmd 生成的GridFilterDemo工程中的app和build文件夹,其余文件过大并且与主题无关,因此未包含。具体方法,请参看我的博客: 《Extjs4.2 Grid Filter Feature 表格过滤特性》

    ExtJS grid过滤操作

    - **Filter Model**:ExtJS Grid中的过滤功能基于Filter Model。每个列都可以有自己的过滤器,用户可以根据需要定义过滤条件。 - **Filter Types**:多种内置过滤器类型,如文本、数字、日期等,满足不同数据类型...

    ExtJs Grid选择行

    在ExtJs框架中,Grid组件是一种非常常用的展示数据表格的方式,它提供了丰富的功能,包括数据的排序、分页、过滤以及行选择等。在实际应用中,经常需要根据用户交互来判断Grid中的行是否被选中,以便进行下一步的...

    ExtJs Filter 表格过滤

    在ExtJs中,"Filter"功能是用来处理数据网格(Grid)中的数据过滤,它允许用户根据特定条件快速筛选和查找所需信息。表格过滤是提高用户体验和数据管理效率的重要工具,尤其在处理大量数据时。 在"ExtJs Filter ...

    ExtJS Filter 实现表格过滤

    表格过滤(Grid Filter)是ExtJS Grid提供的一种功能,用户可以根据需求自定义条件,快速筛选出符合特定条件的行,使得数据浏览更加高效。通过在列头设置过滤器,用户可以对每一列的数据进行过滤操作,如文本、数字...

    ExtJS4.0 分享GridHeaderFilters插件

    ExtJS提供了API接口来编程式地控制过滤器,如`grid.filters.getFilter(id)`可以获取特定列的过滤器,`filter.enable()`或`filter.disable()`可以启用或禁用过滤器,`grid.filters.apply()`将所有过滤器的条件应用到...

    extjs grid

    EXTJS Grid是EXTJS库中的一个核心组件,用于创建数据密集型的表格视图,它在Web应用程序中常用于展示和操作大量结构化数据。EXTJS Grid提供了丰富的功能,包括分页、排序、过滤、编辑等,使得用户可以高效地管理和...

    extjs grid示例代码

    ExtJS Grid是Sencha ExtJS框架中的一个核心组件,它用于构建数据驱动的表格视图。Grid组件在Web应用中广泛使用,因为它提供了强大的数据展示、排序、分页、筛选和编辑功能。以下是对`ExtJS Grid`示例代码的详细解析...

    extjs4.0 grid

    这个压缩包文件"Extjs4.0.2a grid 增删改查"很可能是包含了一组示例代码和资源,用于演示如何在ExtJS 4.0中实现Grid组件的增删改查功能,以及搜索关键字、多条删除和批量更改等操作。 首先,让我们详细了解一下Grid...

    ExtJS4 动态生成的grid导出为excel示例

    var columns = Ext.Array.filter(grid.columnManager.columns); ``` 此外,为了解决因未清理旧数据导致的格式错误,可能需要在生成新Excel文件前初始化一些关键变量,比如在`workbook.js`的77和78行: ```...

    Extjs+Gride使用方法

    grid.columns[1].filter = { type: 'string' }; ``` 3. **行选择**:启用多行选择。 ```javascript grid.selModel = { selType: 'checkboxmodel', showHeaderCheckbox: true }; ``` 4. **事件监听**:监听Grid...

    ExtjsGrid Demo

    ExtJS Grid是一个强大的数据展示组件,常用于构建复杂的Web应用程序中的数据表格。它提供了丰富的功能,包括分页、排序、过滤、编辑等,使得数据管理变得直观且高效。"ExtjsGrid Demo"是一个示例项目,旨在展示如何...

    ExtJs简单的快速搜索例子[search]

    在ExtJS中,这可以通过监听键盘事件,结合`store`的`filter`方法来实现。用户输入时,可以实时更新过滤条件,使Grid显示匹配搜索条件的行。 3. **事件监听**:在JavaScript中,事件监听是处理用户交互的关键。在这...

    Extjs 表格

    创建一个ExtJS表格首先需要定义一个`Ext.grid.Panel`实例,设置其配置项,包括数据源、列定义、存储配置等。例如: ```javascript var grid = Ext.create('Ext.grid.Panel', { store: store, // 数据源 columns...

    ExtJs通用筛选查询控件

    Ext.create('Ext.grid.Panel', { store: { fields: ['name', 'email', 'phone'], data: [...], filters: [{ type: 'string', field: 'name' }, { type: 'string', field: 'email' }] }, columns: [ { ...

    extJs 简单的增删改查

    6. **实现查询(Query)**:可以使用store的filter或query方法来筛选数据,或者在表格上添加下拉菜单、文本框等进行动态查询。 7. **显示组件**:将表格和表单添加到布局中,展示给用户。 ```javascript var ...

    Extjs2.02 Gridpanel

    - **过滤和搜索**:通过`filter`配置,用户可以对数据进行过滤。 - **列拖动**:允许用户通过拖动调整列的顺序。 - **行选择模式**:可以选择单行、多行或全选模式。 - **工具栏**:可以添加自定义的工具栏,提供...

    ExtJS4 2学习 21 动态菜单与表格数据展示操作总结篇

    - 表格过滤:使用`Ext.grid.feature.Filter`特性实现列过滤,让用户自定义筛选条件。 总结来说,理解和掌握动态菜单和表格数据展示在ExtJS4.2中的操作,对于开发高效、灵活的Web应用程序至关重要。这些组件和功能...

    Python django Extjs 项目开发中的错误小记

    - 如果在使用Extjs的tree和grid控件时遇到点击事件不响应的问题,可能是因为事件没有被正确绑定,或者存在其他JavaScript错误。排查这类问题时,可以利用浏览器的开发者工具来检查JavaScript的执行情况。 - 如果...

    extjs完成用户增删改查

    在4.0版本中,ExtJS提供了一套完整的UI组件,如表格(Grid)、表单(Form)、树形视图(Tree)、图表(Charts)等,以及数据绑定机制,非常适合构建数据驱动的应用。 2. **数据模型(Model)**:在用户增删改查中,...

Global site tag (gtag.js) - Google Analytics