Created
June 8, 2011 21:55
-
-
Save larscwallin/1015521 to your computer and use it in GitHub Desktop.
ExtJs Grid filter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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']; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment