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
相关推荐
这是利用sencha cmd 生成的GridFilterDemo工程中的app和build文件夹,其余文件过大并且与主题无关,因此未包含。具体方法,请参看我的博客: 《Extjs4.2 Grid Filter Feature 表格过滤特性》
- **Filter Model**:ExtJS Grid中的过滤功能基于Filter Model。每个列都可以有自己的过滤器,用户可以根据需要定义过滤条件。 - **Filter Types**:多种内置过滤器类型,如文本、数字、日期等,满足不同数据类型...
在ExtJs框架中,Grid组件是一种非常常用的展示数据表格的方式,它提供了丰富的功能,包括数据的排序、分页、过滤以及行选择等。在实际应用中,经常需要根据用户交互来判断Grid中的行是否被选中,以便进行下一步的...
在ExtJs中,"Filter"功能是用来处理数据网格(Grid)中的数据过滤,它允许用户根据特定条件快速筛选和查找所需信息。表格过滤是提高用户体验和数据管理效率的重要工具,尤其在处理大量数据时。 在"ExtJs Filter ...
表格过滤(Grid Filter)是ExtJS Grid提供的一种功能,用户可以根据需求自定义条件,快速筛选出符合特定条件的行,使得数据浏览更加高效。通过在列头设置过滤器,用户可以对每一列的数据进行过滤操作,如文本、数字...
ExtJS提供了API接口来编程式地控制过滤器,如`grid.filters.getFilter(id)`可以获取特定列的过滤器,`filter.enable()`或`filter.disable()`可以启用或禁用过滤器,`grid.filters.apply()`将所有过滤器的条件应用到...
EXTJS Grid是EXTJS库中的一个核心组件,用于创建数据密集型的表格视图,它在Web应用程序中常用于展示和操作大量结构化数据。EXTJS Grid提供了丰富的功能,包括分页、排序、过滤、编辑等,使得用户可以高效地管理和...
ExtJS Grid是Sencha ExtJS框架中的一个核心组件,它用于构建数据驱动的表格视图。Grid组件在Web应用中广泛使用,因为它提供了强大的数据展示、排序、分页、筛选和编辑功能。以下是对`ExtJS Grid`示例代码的详细解析...
这个压缩包文件"Extjs4.0.2a grid 增删改查"很可能是包含了一组示例代码和资源,用于演示如何在ExtJS 4.0中实现Grid组件的增删改查功能,以及搜索关键字、多条删除和批量更改等操作。 首先,让我们详细了解一下Grid...
var columns = Ext.Array.filter(grid.columnManager.columns); ``` 此外,为了解决因未清理旧数据导致的格式错误,可能需要在生成新Excel文件前初始化一些关键变量,比如在`workbook.js`的77和78行: ```...
grid.columns[1].filter = { type: 'string' }; ``` 3. **行选择**:启用多行选择。 ```javascript grid.selModel = { selType: 'checkboxmodel', showHeaderCheckbox: true }; ``` 4. **事件监听**:监听Grid...
ExtJS Grid是一个强大的数据展示组件,常用于构建复杂的Web应用程序中的数据表格。它提供了丰富的功能,包括分页、排序、过滤、编辑等,使得数据管理变得直观且高效。"ExtjsGrid Demo"是一个示例项目,旨在展示如何...
在ExtJS中,这可以通过监听键盘事件,结合`store`的`filter`方法来实现。用户输入时,可以实时更新过滤条件,使Grid显示匹配搜索条件的行。 3. **事件监听**:在JavaScript中,事件监听是处理用户交互的关键。在这...
创建一个ExtJS表格首先需要定义一个`Ext.grid.Panel`实例,设置其配置项,包括数据源、列定义、存储配置等。例如: ```javascript var grid = Ext.create('Ext.grid.Panel', { store: store, // 数据源 columns...
Ext.create('Ext.grid.Panel', { store: { fields: ['name', 'email', 'phone'], data: [...], filters: [{ type: 'string', field: 'name' }, { type: 'string', field: 'email' }] }, columns: [ { ...
6. **实现查询(Query)**:可以使用store的filter或query方法来筛选数据,或者在表格上添加下拉菜单、文本框等进行动态查询。 7. **显示组件**:将表格和表单添加到布局中,展示给用户。 ```javascript var ...
- **过滤和搜索**:通过`filter`配置,用户可以对数据进行过滤。 - **列拖动**:允许用户通过拖动调整列的顺序。 - **行选择模式**:可以选择单行、多行或全选模式。 - **工具栏**:可以添加自定义的工具栏,提供...
- 表格过滤:使用`Ext.grid.feature.Filter`特性实现列过滤,让用户自定义筛选条件。 总结来说,理解和掌握动态菜单和表格数据展示在ExtJS4.2中的操作,对于开发高效、灵活的Web应用程序至关重要。这些组件和功能...
- 如果在使用Extjs的tree和grid控件时遇到点击事件不响应的问题,可能是因为事件没有被正确绑定,或者存在其他JavaScript错误。排查这类问题时,可以利用浏览器的开发者工具来检查JavaScript的执行情况。 - 如果...
在4.0版本中,ExtJS提供了一套完整的UI组件,如表格(Grid)、表单(Form)、树形视图(Tree)、图表(Charts)等,以及数据绑定机制,非常适合构建数据驱动的应用。 2. **数据模型(Model)**:在用户增删改查中,...