1. #1
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    5
    mjlecomte will become famous soon enough mjlecomte will become famous soon enough

      0  

    Default GridFilters - enhanced filtering for grids

    GridFilters - enhanced filtering for grids


    The original extension has been refactored for use with Ext 3.

    This version has been checked into svn. I made a brief screencast, please click here to see it.

    A few changes / enhancements to note:
    1. each filter may be configured via the column model (less repetition of code)
    2. the filter query format is configurable to use json format
    3. filters will automatically reconfigure if the grid is reconfigured
    4. filters clean up after themselves (there was no cleanup in previous version)
    5. filter classes utilize form fields, so you can configure the fields to use validation for example
    6. filters may be dynamically disabled from user manipulation (still experimental)
    7. icons are modifiable just like any other menu and css (even with IE6 -- see attachment)
    8. complete documentation, click here to see it.
    Attached Images

  2. #2
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    5
    mjlecomte will become famous soon enough mjlecomte will become famous soon enough

      0  

    Default


    FAQs

    1. Where can I download or demo this extension? This extension is currently only in svn. To access svn you must be a svn subscriber. This extension will be included with the ExtJS 3.1 release which is estimated to be released November 15, 2009.

  3. #3
    Sencha Premium Member
    Join Date
    May 2008
    Posts
    24
    Vote Rating
    0
    renoye is on a distinguished road

      0  

    Default can you zip a folder for us to download?

    can you zip a folder for us to download?


    I can not find the place to do svn check out. Also, I do not know the path.

    Can you zip a folder with local grid filter example that you showed? thanks a lot.

  4. #4

  5. #5
    Ext User
    Join Date
    Mar 2009
    Location
    France, Paris
    Posts
    23
    Vote Rating
    0
    slemiere is on a distinguished road

      0  

    Default


    Hello !!

    This seems to be perfect in your screencast but is there a way to get the code to test it ? Or at least have an online demo ? I didn't suscribe to svn.
    Thanks a lot,

    Sylvain

  6. #6
    Sencha User
    Join Date
    Jun 2008
    Posts
    157
    Vote Rating
    0
    wiznia is on a distinguished road

      0  

    Default


    Just posting to get notifications...

  7. #7
    Sencha User galdaka's Avatar
    Join Date
    Mar 2007
    Location
    Spain
    Posts
    1,166
    Vote Rating
    -1
    galdaka is an unknown quantity at this point

      0  

    Thumbs up


    Hi,

    Excellent work.

    Two small things that affect the appearance.

    1) In IE there is a icon position minor error. View attached image (error1.jpg). OK in FF.

    2) In IE and in FF, ".x-grid3-hd-row td.ux-filtered-column" not work as expected. When I filter one column the title not appears with css class. View error2.jpg.

    Greetings,
    Attached Images

  8. #8
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    5
    mjlecomte will become famous soon enough mjlecomte will become famous soon enough

      0  

    Default


    1. is a known issue with textfields in menu, I believe there's an open bug report
    2. need a test case, preferably something standalone. AFAIK the demo example provided in svn applies the css class correctly.

  9. #9
    Sencha Premium Member
    Join Date
    Dec 2007
    Posts
    45
    Vote Rating
    0
    bernd01 is on a distinguished road

      0  

    Default


    Hi!

    I also really would appreciate if the 3.0 version of the filter would be available somewhere without svn? The problem is, that I am not a svn subscriber and would need filters earlier than november...

    Thanks a lot,
    Bernd

  10. #10
    Sencha User
    Join Date
    Feb 2008
    Posts
    10
    Vote Rating
    0
    drieraf is on a distinguished road

      0  

    Default


    Quote Originally Posted by bernd01 View Post
    Hi!

    I also really would appreciate if the 3.0 version of the filter would be available somewhere without svn? The problem is, that I am not a svn subscriber and would need filters earlier than november...

    Thanks a lot,
    Bernd
    Hi. I'm agree with you, but I patched the old code with forum comments. I think that run correctly.

    Code:
    /**
     * Ext.ux.grid.GridFilters v0.2.8
     **/
    
    Ext.namespace("Ext.ux.grid");
    /* PATCH http://extjs.com/forum/showthread.php?t=76280 */
    Ext.override(Ext.grid.GridView, {
        handleHdMenuClick : function(item){
            var index = this.hdCtxIndex,
                cm = this.cm, 
                ds = this.ds,
                id = item.getItemId();
            switch(id){
                case 'asc':
                    ds.sort(cm.getDataIndex(index), 'ASC');
                    break;
                case 'desc':
                    ds.sort(cm.getDataIndex(index), 'DESC');
                    break;
                default:
                    index = cm.getIndexById(id.substr(4));
                    if(index != -1){
                        if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
                            this.onDenyColumnHide();
                            return false;
                        }
                        cm.setHidden(index, item.checked);
                    }
            }
            return true;
        }
    });
    /* END PATCH */
    Ext.ux.grid.GridFilters = function(config){		
    	this.filters = new Ext.util.MixedCollection();
    	this.filters.getKey = function(o){return o ? o.dataIndex : null};
    	
    	for(var i=0, len=config.filters.length; i<len; i++)
    		this.addFilter(config.filters[i]);
    	
    	this.deferredUpdate = new Ext.util.DelayedTask(this.reload, this);
    	
    	delete config.filters;
    	Ext.apply(this, config);
    };
    Ext.extend(Ext.ux.grid.GridFilters, Ext.util.Observable, {
    	/**
    	 * @cfg {Integer} updateBuffer
    	 * Number of milisecond to defer store updates since the last filter change.
    	 */
    	updateBuffer: 500,
    	/**
    	 * @cfg {String} paramPrefix
    	 * The url parameter prefix for the filters.
    	 */
    	paramPrefix: 'filter',
    	/**
    	 * @cfg {String} fitlerCls
    	 * The css class to be applied to column headers that active filters. Defaults to 'ux-filterd-column'
    	 */
    	filterCls: 'ux-filtered-column',
    	/**
    	 * @cfg {Boolean} local
    	 * True to use Ext.data.Store filter functions instead of server side filtering.
    	 */
    	local: false,
    	/**
    	 * @cfg {Boolean} autoReload
    	 * True to automagicly reload the datasource when a filter change happens.
    	 */
    	autoReload: true,
    	/**
    	 * @cfg {String} stateId
    	 * Name of the Ext.data.Store value to be used to store state information.
    	 */
    	stateId: undefined,
    	/**
    	 * @cfg {Boolean} showMenu
    	 * True to show the filter menus
    	 */
    	showMenu: true,
    
    	menuFilterText: 'Filters',
    
    	init: function(grid){
    		if(grid instanceof Ext.grid.GridPanel){
    			this.grid  = grid;
    		  
    			this.store = this.grid.getStore();
    			if(this.local){
    				this.store.on('load', function(store){
    					store.filterBy(this.getRecordFilter());
    				}, this);
    			} else {
    			  this.store.on('beforeload', this.onBeforeLoad, this);
    			}
    			  
    			this.grid.filters = this;
    			 
    			this.grid.addEvents({"filterupdate": true});
    			  
    			grid.on("render", this.onRender, this);
    			  
    			grid.on("beforestaterestore", this.applyState, this);
    			grid.on("beforestatesave", this.saveState, this);
    					  
    		} else if(grid instanceof Ext.PagingToolbar){
    		  this.toolbar = grid;
    		}
    	},
    		
    	/** private **/
    	applyState: function(grid, state){
    		this.suspendStateStore = true;
    		this.clearFilters();
    		if(state.filters)
    			for(var key in state.filters){
    				var filter = this.filters.get(key);
    				if(filter){
    					filter.setValue(state.filters[key]);
    					filter.setActive(true);
    				}
    			}
    			
    		this.deferredUpdate.cancel();
    		if(this.local)
    			this.reload();
    			
    		this.suspendStateStore = false;
    	},
    	
    	/** private **/
    	saveState: function(grid, state){
    		var filters = {};
    		this.filters.each(function(filter){
    			if(filter.active)
    				filters[filter.dataIndex] = filter.getValue();
    		});
    		return state.filters = filters;
    	},
    	
    	/** private **/
    	onRender: function(){
    		var hmenu;
    		
    		if(this.showMenu){
    			hmenu = this.grid.getView().hmenu;
    			
    			this.sep  = hmenu.addSeparator();
    			this.menu = hmenu.add(new Ext.menu.CheckItem({
    					text: this.menuFilterText,
    					menu: new Ext.menu.Menu()
    				}));
    			this.menu.on('checkchange', this.onCheckChange, this);
    			this.menu.on('beforecheckchange', this.onBeforeCheck, this);
    				
    			hmenu.on('beforeshow', this.onMenu, this);
    		}
    		
    		this.grid.getView().on("refresh", this.onRefresh, this);
    		this.updateColumnHeadings(this.grid.getView());
    	},
    	
    	/** private **/
    	onMenu: function(filterMenu){
    		var filter = this.getMenuFilter();
    		if(filter){
    			this.menu.menu = filter.menu;
    			this.menu.setChecked(filter.active, false);
    		}
    		
    		this.menu.setVisible(filter !== undefined);
    		this.sep.setVisible(filter !== undefined);
    	},
    	
    	/** private **/
    	onCheckChange: function(item, value){
    		this.getMenuFilter().setActive(value);
    	},
    	
    	/** private **/
    	onBeforeCheck: function(check, value){
    		return !value || this.getMenuFilter().isActivatable();
    	},
    	
    	/** private **/
    	onStateChange: function(event, filter){
        if(event == "serialize") return;
        
    		if(filter == this.getMenuFilter())
    			this.menu.setChecked(filter.active, false);
    			
    		if(this.autoReload || this.local)
    			this.deferredUpdate.delay(this.updateBuffer);
    		
    		var view = this.grid.getView();
    		this.updateColumnHeadings(view);
    			
    		this.grid.saveState();
    			
    		this.grid.fireEvent('filterupdate', this, filter);
    	},
    	
    	/** private **/
    	onBeforeLoad: function(store, options){
        options.params = options.params || {};
    		this.cleanParams(options.params);		
    		var params = this.buildQuery(this.getFilterData());
    		Ext.apply(options.params, params);
    	},
    	
    	/** private **/
    	onRefresh: function(view){
    		this.updateColumnHeadings(view);
    	},
    	
    	/** private **/
    	getMenuFilter: function(){
    		var view = this.grid.getView();
    		if(!view || view.hdCtxIndex === undefined)
    			return null;
    		
    		return this.filters.get(
    			view.cm.config[view.hdCtxIndex].dataIndex);
    	},
    	
    	/** private **/
    	updateColumnHeadings: function(view){
    		if(!view || !view.mainHd) return;
    		
    		var hds = view.mainHd.select('td').removeClass(this.filterCls);
    		for(var i=0, len=view.cm.config.length; i<len; i++){
    			var filter = this.getFilter(view.cm.config[i].dataIndex);
    			if(filter && filter.active)
    				hds.item(i).addClass(this.filterCls);
    		}
    	},
    	
    	/** private **/
    	reload: function(){
    		if(this.local){
    			this.grid.store.clearFilter(true);
    			this.grid.store.filterBy(this.getRecordFilter());
    		} else {
    			this.deferredUpdate.cancel();
    			var store = this.grid.store;
    			if(this.toolbar){
    				var start = this.grid.getStore().paramNames.start;
    				if(store.lastOptions && store.lastOptions.params && store.lastOptions.params[start])
    					store.lastOptions.params[start] = 0;
    			}
    			store.reload();
    		}
    	},
    	
    	/**
    	 * Method factory that generates a record validator for the filters active at the time
    	 * of invokation.
    	 * 
    	 * @private
    	 */
    	getRecordFilter: function(){
    		var f = [];
    		this.filters.each(function(filter){
    			if(filter.active) f.push(filter);
    		});
    		
    		var len = f.length;
    		return function(record){
    			for(var i=0; i<len; i++)
    				if(!f[i].validateRecord(record))
    					return false;
    				
    			return true;
    		};
    	},
    	
    	/**
    	 * Adds a filter to the collection.
    	 * 
    	 * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object.
    	 * 
    	 * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object.
    	 */
    	addFilter: function(config){
    		var filter = config.menu ? config : 
    				new (this.getFilterClass(config.type))(config);
    		this.filters.add(filter);
    		
    		Ext.util.Observable.capture(filter, this.onStateChange, this);
    		return filter;
    	},
    	
    	/**
    	 * Returns a filter for the given dataIndex, if on exists.
    	 * 
    	 * @param {String} dataIndex The dataIndex of the desired filter object.
    	 * 
    	 * @return {Ext.ux.grid.filter.Filter}
    	 */
    	getFilter: function(dataIndex){
    		return this.filters.get(dataIndex);
    	},
    
    	/**
    	 * Turns all filters off. This does not clear the configuration information.
    	 */
    	clearFilters: function(){
    		this.filters.each(function(filter){
    			filter.setActive(false);
    		});
    	},
    
    	/** private **/
    	getFilterData: function(){
    		var filters = [],
    			fields  = this.grid.getStore().fields;
    		
    		this.filters.each(function(f){
    			if(f.active){
    				var d = [].concat(f.serialize());
    				for(var i=0, len=d.length; i<len; i++)
    					filters.push({
    						field: f.dataIndex,
    						data: d[i]
    					});
    			}
    		});
    		
    		return filters;
    	},
    	
    	/**
    	 * Function to take structured filter data and 'flatten' it into query parameteres. The default function
    	 * will produce a query string of the form:
    	 * 		filters[0][field]=dataIndex&filters[0][data][param1]=param&filters[0][data][param2]=param...
    	 * 
    	 * @param {Array} filters A collection of objects representing active filters and their configuration.
    	 * 	  Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured
    	 *    to be unique as any one filter may be a composite of more basic filters for the same dataIndex.
    	 * 
    	 * @return {Object} Query keys and values
    	 */
    	buildQuery: function(filters){
    		var p = {};
    		for(var i=0, len=filters.length; i<len; i++){
    			var f    = filters[i];
    			var root = [this.paramPrefix, '[', i, ']'].join('');
    			p[root + '[field]'] = f.field;
    			
    			var dataPrefix = root + '[data]';
    			for(var key in f.data)
    				p[[dataPrefix, '[', key, ']'].join('')] = f.data[key];
    		}
    		
    		return p;
    	},
    	
    	/**
    	 * Removes filter related query parameters from the provided object.
    	 * 
    	 * @param {Object} p Query parameters that may contain filter related fields.
    	 */
    	cleanParams: function(p){
    		var regex = new RegExp("^" + this.paramPrefix + "\[[0-9]+\]");
    		for(var key in p)
    			if(regex.test(key))
    				delete p[key];
    	},
    	
    	/**
    	 * Function for locating filter classes, overwrite this with your favorite
    	 * loader to provide dynamic filter loading.
    	 * 
    	 * @param {String} type The type of filter to load.
    	 * 
    	 * @return {Class}
    	 */
    	getFilterClass: function(type){
    		return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter'];
    	}
    });

Thread Participants: 84

  1. VinylFox (1 Post)
  2. galdaka (2 Posts)
  3. Condor (10 Posts)
  4. cgi-bin (4 Posts)
  5. Fabyo (8 Posts)
  6. fshort (1 Post)
  7. digitalkaoz (1 Post)
  8. steffenk (2 Posts)
  9. brookd (1 Post)
  10. jamie.nicholson (1 Post)
  11. sawan (1 Post)
  12. froamer (1 Post)
  13. jaquet (1 Post)
  14. KevinChristensen (1 Post)
  15. bernd01 (2 Posts)
  16. tonedeaf (7 Posts)
  17. seg (1 Post)
  18. calavera (5 Posts)
  19. httpdotcom (1 Post)
  20. Timido (3 Posts)
  21. mfrancey (1 Post)
  22. WixSL (1 Post)
  23. drieraf (1 Post)
  24. nickweavers (2 Posts)
  25. goofy (1 Post)
  26. mynameisyoda (1 Post)
  27. manilodisan (1 Post)
  28. eyetv (1 Post)
  29. renoye (3 Posts)
  30. pouniok (2 Posts)
  31. mcouillard (6 Posts)
  32. Scorpie (1 Post)
  33. wiznia (1 Post)
  34. fulfowi (2 Posts)
  35. benmclendon (1 Post)
  36. roemisch (2 Posts)
  37. zeruyo (1 Post)
  38. dizor (1 Post)
  39. SunWuKung (5 Posts)
  40. mx_starter (1 Post)
  41. jimtyp (2 Posts)
  42. nitingautam (1 Post)
  43. westy (2 Posts)
  44. slemiere (1 Post)
  45. mike2406 (2 Posts)
  46. msinn (12 Posts)
  47. RanmaSaotome (1 Post)
  48. cnicolas (2 Posts)
  49. lsaffre (1 Post)
  50. astraschedule (1 Post)
  51. hankin (2 Posts)
  52. mrinaljena (1 Post)
  53. dtex-lab (1 Post)
  54. yohnan (2 Posts)
  55. Override (2 Posts)
  56. hjones (1 Post)
  57. hexawing (1 Post)
  58. taronja (6 Posts)
  59. asbestos girl (7 Posts)
  60. stalek (1 Post)
  61. mpawlowski (1 Post)
  62. DerSalz (1 Post)
  63. jimm (1 Post)
  64. blueberrymuffins (2 Posts)
  65. gleskinen (1 Post)
  66. pmdarrow (1 Post)
  67. Fallen Zen (1 Post)
  68. filippo.ferrari (6 Posts)
  69. george4rotech (2 Posts)
  70. Ebpo (1 Post)
  71. Reimius (1 Post)
  72. webtime (9 Posts)
  73. Christiand (1 Post)
  74. Oliver Specht (1 Post)
  75. novaku (1 Post)
  76. sizemorew (1 Post)
  77. # eof (1 Post)
  78. ldonofrio (3 Posts)
  79. david777 (1 Post)
  80. ExTriqui (4 Posts)
  81. leowyatt (7 Posts)
  82. JSassy (1 Post)
  83. hachie (1 Post)
  84. radmila80 (1 Post)