Extjs4.2 Grid搜索Ext.ux.grid.feature.Searching的使用
背景
Extjs4.2 默认提供的Search搜索,功能还是非常强大的,只是对于国内的用户来说,还是不习惯在每列里面单击好几下再筛选,于是相当当初2.2里面的搜索,更加的实用点,于是在4.2里面实现。
国际惯例,先上图
参考文献
https://gist.github.com/aghuddleston/3297619/
国外的大牛已经帮我们实现了在4.0中的应用,但是到4.2还需要做少许变更才可以使用。
修改后的源代码如下[复制如下代码,放到ux/grid/features/Searching.js]:
// JavaScript Document// vim: ts=4:sw=4:nu:fdc=4:nospell /** * Search plugin for Ext.grid.GridPanel, Ext.grid.EditorGrid ver. 2.x or subclasses of them * * @author Ing. Jozef Sakalos * @copyright (c) 2008, by Ing. Jozef Sakalos * @date 17. January 2008 * @version $Id: Ext.ux.grid.Search.js 220 2008-04-29 21:46:51Z jozo $ * * @license Ext.ux.grid.Search is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * * License details: http://www.gnu.org/licenses/lgpl.html */ /* Revised for Ext 4 by Nathan LeBlanc on July 8, 2011 */ Ext.define('Ext.ux.grid.feature.Searching', { extend: 'Ext.grid.feature.Feature', alias: 'feature.searching', /** * cfg {Boolean} autoFocus true to try to focus the input field on each store load (defaults to undefined) */ /** * @cfg {String} searchText Text to display on menu button */ searchText:'查询', /** * @cfg {String} searchTipText Text to display as input tooltip. Set to '' for no tooltip */ searchTipText:'输入关键字回车查询', /** * @cfg {String} selectAllText Text to display on menu item that selects all fields */ selectAllText:'所有列', /** * @cfg {String} position Where to display the search controls. Valid values are top and bottom (defaults to bottom) * Corresponding toolbar has to exist at least with mimimum configuration tbar:[] for position:top or bbar:[] * for position bottom. Plugin does NOT create any toolbar. */ position:'top', /** * @cfg {String} iconCls Icon class for menu button (defaults to icon-magnifier) */ iconCls: 'Zoom', /** * @cfg {String/Array} checkIndexes Which indexes to check by default. Can be either 'all' for all indexes * or array of dataIndex names, e.g. ['persFirstName', 'persLastName'] */ checkIndexes:'all', /** * @cfg {Array} disableIndexes Array of index names to disable (not show in the menu), e.g. ['persTitle', 'persTitle2'] */ disableIndexes:[], /** * @cfg {String} dateFormat how to format date values. If undefined (the default) * date is formatted as configured in colummn model */ dateFormat:undefined, /** * @cfg {Boolean} showSelectAll Select All item is shown in menu if true (defaults to true) */ showSelectAll:true, /** * @cfg {String} menuStyle Valid values are 'checkbox' and 'radio'. If menuStyle is radio * then only one field can be searched at a time and selectAll is automatically switched off. */ menuStyle:'checkbox', /** * @cfg {Number} minChars minimum characters to type before the request is made. If undefined (the default) * the trigger field shows magnifier icon and you need to click it or press enter for search to start. If it * is defined and greater than 0 then maginfier is not shown and search starts after minChars are typed. */ minChars: 2, /** * @cfg {String} minCharsTipText Tooltip to display if minChars is > 0 */ minCharsTipText:'至少输入{0}个字符', /** * @cfg {String} mode Use 'remote' for remote stores or 'local' for local stores. If mode is local * no data requests are sent to server the grid's store is filtered instead (defaults to 'remote') */ mode:'remote', /** * @cfg {Array} readonlyIndexes Array of index names to disable (show in menu disabled), e.g. ['persTitle', 'persTitle2'] */ /** * @cfg {Number} width Width of input field in pixels (defaults to 100) */ width:100, /** * @cfg {Object} paramNames Params name map (defaults to {fields:'fields', query:'query'} */ paramNames: { fields:'fields' ,query:'query' }, /** * @cfg {String} shortcutKey Key to fucus the input field (defaults to r = Sea_r_ch). Empty string disables shortcut */ shortcutKey:'r', /** * @cfg {String} shortcutModifier Modifier for shortcutKey. Valid values: alt, ctrl, shift (defaults to alt) */ shortcutModifier:'alt', /** * @cfg {String} align 'left' or 'right' (defaults to 'left') */ align:'left', /** * @cfg {Number} minLength force user to type this many character before he can make a search */ minLength: 2, /** * @cfg {Ext.Panel/String} toolbarContainer Panel (or id of the panel) which contains toolbar we want to render * search controls to (defaults to this.grid, the grid this plugin is plugged-in into) */ //attachEvents: function() { // this.grid = this.view.up('gridpanel'); // if(this.grid.rendered) // this.onRender(); // else // this.grid.on('render', this.onRender, this); //}, init: function (grid) { this.grid = grid; if (this.grid.rendered) this.onRender(); else this.grid.on('render', this.onRender, this); }, onRender:function() { var panel = this.toolbarContainer || this.grid; var tb = 'bottom' === this.position ? panel.getDockedItems('toolbar[dock="bottom"]') : panel.getDockedItems('toolbar[dock="top"]'); if(tb.length > 0) tb = tb[0] else { tb = Ext.create('Ext.toolbar.Toolbar', {dock: this.position}); panel.addDocked(tb); } // add menu this.menu = Ext.create('Ext.menu.Menu'); // handle position if('right' === this.align) { tb.add('->'); } else { if(0 < tb.items.getCount()) { tb.add('-'); } } // add menu button tb.add({ text:this.searchText ,menu:this.menu ,iconCls:this.iconCls }); // add input field (TwinTriggerField in fact) this.field = Ext.create('Ext.form.TwinTriggerField', { width:this.width, qtip: 'ddd', selectOnFocus:undefined === this.selectOnFocus ? true : this.selectOnFocus, triggerCls: 'x-form-clear-trigger', //triggerCls: this.minChars ? 'x-hidden' : 'x-form-search-trigger', onTrigger1Click: Ext.bind(this.onTriggerClear, this), //onTrigger2Click: this.minChars ? Ext.emptyFn : Ext.bind(this.onTriggerSearch, this), //onTrigger1Click: this.minChars ? Ext.emptyFn : Ext.bind(this.onTriggerSearch, this), minLength:this.minLength }); // install event handlers on input field this.field.on('render', function() { var qtip = this.minChars ? Ext.String.format(this.minCharsTipText, this.minChars) : this.searchTipText; Ext.QuickTips.register({ target: this.field.inputEl, text: qtip }); if(this.minChars) { this.field.el.on({scope:this, buffer:300, keyup:this.onKeyUp}); } // install key map var map = new Ext.KeyMap(this.field.el, [{ key:Ext.EventObject.ENTER ,scope:this ,fn:this.onTriggerSearch },{ key:Ext.EventObject.ESC ,scope:this ,fn:this.onTriggerClear }]); map.stopEvent = true; }, this, {single:true}); tb.add(this.field); // reconfigure this.reconfigure(); // keyMap if(this.shortcutKey && this.shortcutModifier) { var shortcutEl = this.grid.getEl(); var shortcutCfg = [{ key:this.shortcutKey ,scope:this ,stopEvent:true ,fn:function() { this.field.focus(); } }]; shortcutCfg[0][this.shortcutModifier] = true; this.keymap = new Ext.KeyMap(shortcutEl, shortcutCfg); } if(true === this.autoFocus) { this.grid.store.on({scope:this, load:function(){this.field.focus();}}); } } // eo function onRender // }}} // {{{ /** * field el keypup event handler. Triggers the search * @private */ ,onKeyUp:function() { var length = this.field.getValue().toString().length; if(0 === length || this.minChars <= length) { this.onTriggerSearch(); } } // eo function onKeyUp // }}} // {{{ /** * private Clear Trigger click handler */ ,onTriggerClear:function() { if (this.field.getValue()) { //if (this.field.getValue().length < this.minChars) { // this.field.setValue(''); // return; //} this.field.setValue(''); this.field.focus(); this.onTriggerSearch(); } } // eo function onTriggerClear // }}} // {{{ /** * private Search Trigger click handler (executes the search, local or remote) */ ,onTriggerSearch:function() { if(!this.field.isValid()) { return; } var val = this.field.getValue(), store = this.grid.store, proxy = store.getProxy(); // grid's store filter if('local' === this.mode) { store.clearFilter(); if(val) { store.filterBy(function(r) { var retval = false; this.menu.items.each(function(item) { if(!item.checked || retval) { return; } var rv = r.get(item.dataIndex); rv = rv instanceof Date ? Ext.Date.format(rv, this.dateFormat || r.fields.get(item.dataIndex).dateFormat) : rv; var re = new RegExp(val, 'gi'); retval = re.test(rv); }, this); if(retval) { return true; } return retval; }, this); } else { } } // ask server to filter records // your proxy must be a Server proxy else if(proxy instanceof Ext.data.proxy.Server) { // clear start (necessary if we have paging) if(store.lastOptions && store.lastOptions.params) { store.lastOptions.params[store.paramNames.start] = 0; } // get fields to search array var fields = []; this.menu.items.each(function(item) { if(item.checked && item.dataIndex) { fields.push(item.dataIndex); } }); // add fields and query to baseParams of store delete(proxy.extraParams[this.paramNames.fields]); delete(proxy.extraParams[this.paramNames.query]); if (store.lastOptions && store.lastOptions.params) { delete(proxy.lastOptions.params[this.paramNames.fields]); delete(proxy.lastOptions.params[this.paramNames.query]); } if(fields.length) { proxy.extraParams[this.paramNames.fields] = (fields); proxy.extraParams[this.paramNames.query] = (val); } // reload store store.load(); } } // eo function onTriggerSearch // }}} // {{{ /** * @param {Boolean} true to disable search (TwinTriggerField), false to enable */ ,setDisabled:function() { this.field.setDisabled.apply(this.field, arguments); } // eo function setDisabled // }}} // {{{ /** * Enable search (TwinTriggerField) */ ,enable:function() { this.setDisabled(false); } // eo function enable // }}} // {{{ /** * Enable search (TwinTriggerField) */ ,disable:function() { this.setDisabled(true); } // eo function disable // }}} // {{{ /** * private (re)configures the plugin, creates menu items from column model */ ,reconfigure:function() { // {{{ // remove old items var menu = this.menu; menu.removeAll(); // add Select All item plus separator if(this.showSelectAll && 'radio' !== this.menuStyle) { menu.add({ xtype: 'menucheckitem', text:this.selectAllText, checked:!(this.checkIndexes instanceof Array), hideOnClick:false, handler:function(item) { var checked = item.checked; item.parentMenu.items.each(function(i) { if(item !== i && i.setChecked && !i.disabled) { i.setChecked(checked); } }); } },'-'); } // }}} // {{{ // add new items var columns = this.grid.headerCt.items.items; var group = undefined; if('radio' === this.menuStyle) { group = 'g' + (new Date).getTime(); } Ext.each(columns, function(column) { var disable = false; if(column.text && column.dataIndex && column.dataIndex != '') { Ext.each(this.disableIndexes, function(item) { disable = disable ? disable : item === column.dataIndex; }); if(!disable) { menu.add({ xtype: 'menucheckitem', text: column.text, hideOnClick: false, group:group, checked: 'all' === this.checkIndexes, dataIndex: column.dataIndex, }); } } }, this); // }}} // {{{ // check items if(this.checkIndexes instanceof Array) { Ext.each(this.checkIndexes, function(di) { var item = menu.items.findBy(function(itm) { return itm.dataIndex === di; }); if(item) { item.setChecked(true, true); } }, this); } // }}} // {{{ // disable items if(this.readonlyIndexes instanceof Array) { Ext.each(this.readonlyIndexes, function(di) { var item = menu.items.findBy(function(itm) { return itm.dataIndex === di; }); if(item) { item.disable(); } }, this); } // }}} } // eo function reconfigure // }}} }); // eo extend // eof
使用方法
非常简单,代码如下:
Ext.define('WMS.view.SystemGrid', { extend: 'Ext.grid.Panel', alias: 'widget.SystemGrid', ................. features: [ { ftype: 'searching', minChars: 2, width: 150, mode: 'remote',//远程还是本地store position: 'top/bottom',//状态栏还是工具栏 iconCls: 'Zoom',//图标 menuStyle: 'checkbox/radiobox',//单选还是多选 showSelectAll: true, //是否显示全选按钮 checkIndexes: ["SysName"], //默认是否选择所有的列 disableIndexes: ["CreateUser", "CreateTime", "ModifyUser", "ModifyTime", "Disable"] //禁止那些列参与查询 } ], //其他参数参照searching的源代码
查看产生的URL
ok,收工。
下一篇文章将会介绍,MVC如果获取参数,以及如何将Extjs查询的参数动态转换为EntityFramework的Expression<Func<T, bool>>,构造查询SQL(http://www.cnblogs.com/qidian10/p/3209458.html)