Threaded View

  1. #1
    Sencha Premium Member d.zucconi's Avatar
    Join Date
    Jun 2008
    Location
    Piacenza (Italy)
    Posts
    78
    Vote Rating
    5
    d.zucconi is on a distinguished road

      5  

    Default Grid Header Filters

    Grid Header Filters


    This is the ExtJS 4 version of ExtJS 3.x plugin Ext.ux.grid.GridHeaderFilters available on thread
    http://www.sencha.com/forum/showthread.php?41658-Grid-header-filters
    The plugin has been rewritten to be compatible with the new ExtJS version and now works directly on new Ext.data.Store filters, using Ext.util.Filter objects.
    This is only a preview release for now, tested on FF,Chrome and IE9 with local and remote stores.
    Some functions will be improved in future versions.

    Source code
    Preview release - 14/10/11
    16/10/11 - Fixed resizeFilterContainer method (thanks to bsot)
    17/10/11 - Modified beforeheaderfiltersapply event handler result management (thanks to bsot)
    18/10/11 - Fixed wrong variable name in setFieldValue method code

    Version 0.1.0 - 04/11/11
    Modified events parameters adding the number of active (not empty) filters
    Added adjustFilterWidth on grid afterlayout event (thanks to zaggi
    )

    Version 0.2.0 - 05/03/12
    Changed plugin class name (Ext.ux.grid.plugin.HeaderFilters)
    Filters are rendered on headerCt afterrender event (thanks to puja for the hint)
    Modified adjustFilterWidth checking if containers are ready (thanks to Jad
    )
    Filter values are parsed only if the corresponding field isValid
    Adjusted filters tooltip
    Configurable status property for stateful filter values

    Code:
    /**
     * Plugin that enable filters on the grid header.<br>
     * The header filters are integrated with new Ext4 <code>Ext.data.Store</code> filters.<br>
     * It enables:
     * <ul>
     * <li>Instances of <code>Ext.form.field.Field</code> subclasses that can be used as filter fields into column header</li>
     * <li>New grid methods to control header filters (get values, update, apply)</li>
     * <li>New grid events to intercept plugin and filters events</li>
     * </ul>
     * 
     * The plugins also enables the stateful feature for header filters so filter values are stored with grid status if grid is stateful.<br>
     * 
     * # Enable filters on grid columns
     * The plugin checks the <code>filter</code> attribute that can be included into each column configuration.
     * The value of this attribute can be a <code>Ext.form.field.Field</code> configuration or an array of field configurations to enable more
     * than one filter on a single column.<br>
     * Field <code>readOnly</code> and <code>disabled</code> attributes are managed by the plugin to avoid filter update or filter apply.
     * The filter field configuration also supports some special attributes to control filter configuration:
     * <ul>
     * <li>
     *     <code>filterName</code>: the name of the filter that will be used when the filter is applied to store filters (as <code>property</code> of <code>Ext.util.Filter</code> attribute).
     *     If this attribute is not specified the column <code>dataIndex</code> will be used. <b>NOTE</b>: The filter name must be unique in a grid header. The plugin doesn't support correctly filters
     *     with same name.
     * </li>
     * </ul>
     * On the grid configuration the {@link #headerFilters} attribute is supported. The value must be an object with name-values pairs for filters to initialize. 
     * It can be used to initialize header filters in grid configuration.
     * 
     * # Plugin configuration
     * The plugin supports also some configuration attributes that can be specified when the plugin is created (with <code>Ext.create</code>).
     * These parameters are:
     * <ul>
     * <li>{@link #stateful}: Enables filters save and load when grid status is managed by <code>Ext.state.Manager</code>. If the grid is not stateful this parameter has no effects</li>
     * <li>{@link #reloadOnChange}: Intercepts the special {@link #headerfilterchange} plugin-enabled grid event and automatically reload or refresh grid Store. Default true</li>
     * <li>{@link #ensureFilteredVisible}: If one filter on column is active, the plugin ensures that this column is not hidden (if can be made visible).</li>
     * <li>{@link #enableTooltip}: Enable active filters description tootip on grid header</li>
     * </ul>
     * 
     * # Enabled grid methods
     * <ul>
     *     <li><code>setHeaderFilter(name, value, reset)</code>: Set a single filter value</li>
     *     <li><code>setHeaderFilters(filters, reset)</code>: Set header filters</li>
     *     <li><code>getHeaderFilters()</code>: Set header filters</li>
     *     <li><code>getHeaderFilterField(filterName)</code>: To access filter field</li>
     *     <li><code>resetHeaderFilters()</code>: Resets filter fields calling reset() method of each one</li>
     *     <li><code>clearHeaderFilters()</code>: Clears filter fields</li>
     *     <li><code>applyHeaderFilters()</code>: Applies filters values to Grid Store. The store will be also refreshed or reloaded if {@link #reloadOnChange} is true</li>
     * </ul>
     * 
     * # Enabled grid events
     * <ul>
     *     <li>{@link #headerfilterchange} : fired by Grid when some header filter changes value</li>
     *     <li>{@link #headerfiltersrender} : fired by Grid when header filters are rendered</li>
     *     <li>{@link #beforeheaderfiltersapply} : fired before filters are applied to Grid Store</li>
     *     <li>{@link #headerfiltersapply} : fired after filters are applied to Grid Store</li>
     * </ul>
     * 
     * @author Damiano Zucconi - http://www.isipc.it
     * @version 0.2.0
     */
    Ext.define('Ext.ux.grid.plugin.HeaderFilters',{
        
        ptype: 'gridheaderfilters',
        
        alternateClassName: ['Ext.ux.grid.HeaderFilters', 'Ext.ux.grid.header.Filters'],
        
        requires: [
            'Ext.container.Container',
            'Ext.tip.ToolTip'
        ],
    
    
    
    
        grid: null,
        
        fields: null,
        
        containers: null,
        
        storeLoaded: false,
        
        filterFieldCls: 'x-gridheaderfilters-filter-field',
        
        filterContainerCls: 'x-gridheaderfilters-filter-container',
        
        filterRoot: 'data',
        
        tooltipTpl: '{[Ext.isEmpty(values.filters) ? this.text.noFilter : "<b>"+this.text.activeFilters+"</b>"]}<br><tpl for="filters"><tpl if="value != \'\'">{[values.label ? values.label : values.property]} = {value}<br></tpl></tpl>',
        
        lastApplyFilters: null,
        
        bundle: {
            activeFilters: 'Active filters',
            noFilter: 'No filter'
        },
        
    	/**
    	* @cfg {Boolean} stateful
    	* Specifies if headerFilters values are saved into grid status when filters changes.
    	* This configuration can be overridden from grid configuration parameter <code>statefulHeaderFilters</code> (if defined).
    	* Used only if grid <b>is stateful</b>. Default = true.
    	* 
    	*/
    	stateful: true,
        
       /**
       * @cfg {Boolean} reloadOnChange
       * Specifies if the grid store will be auto-reloaded when filters change. The store
       * will be reloaded only if is was already loaded. If the store is local or it doesn't has remote filters
       * the store will be always updated on filters change.
       * 
       */
       reloadOnChange: true,
            
    	/**
       * @cfg {Boolean} ensureFilteredVisible
       * If the column on wich the filter is set is hidden and can be made visible, the
       * plugin makes the column visible.
       */
    	ensureFilteredVisible: true,
            
    	/**
    	* @cfg {Boolean} enableTooltip
    	* If a tooltip with active filters description must be enabled on the grid header
    	*/
    	enableTooltip: true,
    	
    	statusProperty: 'headerFilters',
    	
    	rendered: false,
        
       constructor: function(cfg) 
       {
           if(cfg)
           {
           	Ext.apply(this,cfg);
           }
       },
        
       init: function(grid)
       {
    	   this.grid = grid;
            
            /*var storeProxy = this.grid.getStore().getProxy();
            if(storeProxy && storeProxy.getReader())
            {
                var reader = storeProxy.getReader();
                this.filterRoot = reader.root ? reader.root : undefined;
            }*/
            /**
             * @cfg {Object} headerFilters
             * <b>Configuration attribute for grid</b>
             * Allows to initialize header filters values from grid configuration.
             * This object must have filter names as keys and filter values as values.
             * If this plugin has {@link #stateful} enabled, the saved filters have priority and override these filters.
             * Use {@link #ignoreSavedHeaderFilters} to ignore current status and apply these filters directly.
             */
    	   if(!grid.headerFilters)
    		   grid.headerFilters = {};
            
            
    	   if(Ext.isBoolean(grid.statefulHeaderFilters))
           {
    		   this.setStateful(grid.statefulHeaderFilters);
           }
            
    		this.grid.addEvents(
          /**
            * @event headerfilterchange
            * <b>Event enabled on the Grid</b>: fired when at least one filter is updated after apply.
            * @param {Ext.grid.Panel} grid The grid
            * @param {Ext.util.MixedCollection} filters The applied filters (after apply). Ext.util.Filter objects.
            * @param {Ext.util.MixedCollection} prevFilters The old applied filters (before apply). Ext.util.Filter objects.
            * @param {Number} active Number of active filters (not empty)
            * @param {Ext.data.Store} store Current grid store
            */    
            'headerfilterchange',
            /**
             * @event headerfiltersrender
             * <b>Event enabled on the Grid</b>: fired when filters are rendered
             * @param {Ext.grid.Panel} grid The grid
             * @param {Object} fields The filter fields rendered. The object has for keys the filters names and for value Ext.form.field.Field objects.
             * @param {Object} filters Current header filters. The object has for keys the filters names and for value the filters values.
            */
    			'headerfiltersrender',
            	/**
             * @event beforeheaderfiltersapply
             * <b>Event enabled on the Grid</b>: fired before filters are confirmed. If the handler returns false no filter apply occurs.
             * @param {Ext.grid.Panel} grid The grid
             * @param {Object} filters Current header filters. The object has for keys the filters names and for value the filters values.
             * @param {Ext.data.Store} store Current grid store
             */
            'beforeheaderfiltersapply',
            /**
             * @event headerfiltersapply
             *<b>Event enabled on the Grid</b>: fired when filters are confirmed.
             * @param {Ext.grid.Panel} grid The grid
             * @param {Object} filters Current header filters. The object has for keys the filters names and for value the filters values.
             * @param {Number} active Number of active filters (not empty)
             * @param {Ext.data.Store} store Current grid store
             */
            'headerfiltersapply'
            );
            
            this.grid.on({
            	scope: this,
                columnresize: this.resizeFilterContainer,
                beforedestroy: this.onDestroy,
                beforestatesave: this.saveFilters,
                afterlayout: this.adjustFilterWidth
            });
            
            this.grid.headerCt.on({
                scope: this,
                afterrender: this.renderFilters
            });
            
            this.grid.getStore().on({
                scope: this,
                load: this.onStoreLoad
            });
            
            if(this.reloadOnChange)
            {
                this.grid.on('headerfilterchange',this.reloadStore, this);
            }
            
            if(this.stateful)
            {
                this.grid.addStateEvents('headerfilterchange');
            }
            
            //Enable new grid methods
            Ext.apply(this.grid, 
            {
                headerFilterPlugin: this,
                setHeaderFilter: function(sName, sValue)
                {
                    if(!this.headerFilterPlugin)
                        return;
                    var fd = {};
                    fd[sName] = sValue;
                    this.headerFilterPlugin.setFilters(fd);
                },
                /**
                 * Returns a collection of filters corresponding to enabled header filters.
                 * If a filter field is disabled, the filter is not included.
                 * <b>This method is enabled on Grid</b>.
                 * @method
                 * @return {Array[Ext.util.Filter]} An array of Ext.util.Filter
                 */
                getHeaderFilters: function()
                {
                    if(!this.headerFilterPlugin)
                        return null;
                    return this.headerFilterPlugin.getFilters();
                },
                /**
                 * Set header filter values
                 * <b>Method enabled on Grid</b>
                 * @method
                 * @param {Object or Array[Object]} filters An object with key/value pairs or an array of Ext.util.Filter objects (or corresponding configuration).
                 * Only filters that matches with header filters names will be set
                 */
                setHeaderFilters: function(obj)
                {
                    if(!this.headerFilterPlugin)
                        return;
                    this.headerFilterPlugin.setFilters(obj);
                },
                getHeaderFilterField: function(fn)
                {
                    if(!this.headerFilterPlugin)
                        return;
                    if(this.headerFilterPlugin.fields[fn])
                        return this.headerFilterPlugin.fields[fn];
                    else
                        return null;
                },
                resetHeaderFilters: function()
                {
                    if(!this.headerFilterPlugin)
                        return;
                    this.headerFilterPlugin.resetFilters();
                },
                clearHeaderFilters: function()
                {
                    if(!this.headerFilterPlugin)
                        return;
                    this.headerFilterPlugin.clearFilters();
                },
                applyHeaderFilters: function()
                {
                    if(!this.headerFilterPlugin)
                        return;
                    this.headerFilterPlugin.applyFilters();
                }
            });
       },
        
       
        
    	saveFilters: function(grid, status)
    	{
    		status[this.statusProperty] = (this.stateful && this.rendered) ? this.parseFilters() : grid[this.statusProperty];
    	},
        
        setFieldValue: function(field, value)
        {
        	var column = field.column;
            if(!Ext.isEmpty(value))
            {
                field.setValue(value);
                if(!Ext.isEmpty(value) && column.hideable && !column.isVisible() && !field.isDisabled() && this.ensureFilteredVisible)
                {
                	column.setVisible(true);
                }
            }
            else
            {
            	field.setValue('');
            }
        },
        
        renderFilters: function()
        {
            this.destroyFilters();
            
            this.fields = {};
            this.containers = {};
    
    
    
    
            var filters = this.grid.headerFilters;
            
            /**
             * @cfg {Boolean} ignoreSavedHeaderFilters
             * <b>Configuration parameter for grid</b>
             * Allows to ignore saved filter status when {@link #stateful} is enabled.
             * This can be useful to use {@link #headerFilters} configuration directly and ignore status.
             * The state will still be saved if {@link #stateful} is enabled.
             */
            if(this.stateful && this.grid[this.statusProperty] && !this.grid.ignoreSavedHeaderFilters)
            {
                Ext.apply(filters, this.grid[this.statusProperty]);
            }
            
            var storeFilters = this.parseStoreFilters();
            filters = Ext.apply(storeFilters, filters);
            
            var columns = this.grid.headerCt.getGridColumns(true);
            for(var c=0; c < columns.length; c++)
            {
                var column = columns[c];
                if(column.filter)
                {
                    var filterContainerConfig = {
                        itemId: column.id + '-filtersContainer',
                        cls: this.filterContainerCls,
                        layout: 'anchor',
                        bodyStyle: {'background-color': 'transparent'},
                        border: false,
                        width: column.getWidth(),
                        listeners: {
                            scope: this,
                            element: 'el',
                            mousedown: function(e)
                            {
                                e.stopPropagation();
                            },
                            click: function(e)
                            {
                                e.stopPropagation();
                            },
                            keydown: function(e){
                                 e.stopPropagation();
                            },
                            keypress: function(e){
                                 e.stopPropagation();
                                 if(e.getKey() == Ext.EventObject.ENTER)
                                 {
                                     this.onFilterContainerEnter();
                                 }
                            },
                            keyup: function(e){
                                 e.stopPropagation();
                            }
                        },
                        items: []
                    }
                    
                    var fca = [].concat(column.filter);
                        
                    for(var ci = 0; ci < fca.length; ci++)
                    {
                        var fc = fca[ci];
                        Ext.applyIf(fc, {
                            filterName: column.dataIndex,
                            fieldLabel: column.text || column.header,
                            hideLabel: fca.length == 1
                        });
                        var initValue = Ext.isEmpty(filters[fc.filterName]) ? null : filters[fc.filterName];
                        Ext.apply(fc, {
                            cls: this.filterFieldCls,
                            itemId: fc.filterName,
                            anchor: '-1'
                        });
                        var filterField = Ext.ComponentManager.create(fc);
                        filterField.column = column;
                        this.setFieldValue(filterField, initValue);
                        this.fields[filterField.filterName] = filterField;
                        filterContainerConfig.items.push(filterField);
                    }
                    
                    var filterContainer = Ext.create('Ext.container.Container', filterContainerConfig);
                    filterContainer.render(column.el);
                    this.containers[column.id] = filterContainer;
                    column.setPadding = Ext.Function.createInterceptor(column.setPadding, function(h){return false});
                }
            }
            
            if(this.enableTooltip)
            {
                this.tooltipTpl = new Ext.XTemplate(this.tooltipTpl,{text: this.bundle});
                this.tooltip = Ext.create('Ext.tip.ToolTip',{
                    target: this.grid.headerCt.el,
                    //delegate: '.'+this.filterContainerCls,
                    renderTo: Ext.getBody(),
                    html: this.tooltipTpl.apply({filters: []})
                });        
                this.grid.on('headerfilterchange',function(grid, filters)
                {
                    var sf = filters.filterBy(function(filt){
                        return !Ext.isEmpty(filt.value);
                    });
                    this.tooltip.update(this.tooltipTpl.apply({filters: sf.getRange()}));
                },this);
            }
            
            this.applyFilters();
            this.rendered = true;
            this.grid.fireEvent('headerfiltersrender',this.grid,this.fields,this.parseFilters());
        },
        
        onStoreLoad: function()
        {
            this.storeLoaded = true;
        },
        
        onFilterContainerEnter: function()
        {
            this.applyFilters();
        },
        
        resizeFilterContainer: function(headerCt,column,w,opts)
        {
             if(!this.containers)             return;
            var cnt = this.containers[column.id];
            if(cnt)
            {
                cnt.setWidth(w);
                cnt.doLayout();
            }
        },
        
        destroyFilters: function()
        {
        	this.rendered = false;
    	     if(this.fields)
    	     {
    	         for(var f in this.fields)
    	             Ext.destroy(this.fields[f]);
    	         delete this.fields;
    	     }
    	 
    	     if(this.containers)
    	     {
    	         for(var c in this.containers)
    	             Ext.destroy(this.containers[c]);
    	         delete this.containers;
    	     }
        },
        
        onDestroy: function()
        {
            this.destroyFilters();
            Ext.destroy(this.tooltip, this.tooltipTpl);
        },
        
    	 adjustFilterWidth: function() 
        {
        	if(!this.containers) return;
    		var columns = this.grid.headerCt.getGridColumns(true);        
    		for(var c=0; c < columns.length; c++) 
    		{           
    			var column = columns[c];            
    			if (column.filter && column.flex) 
    			{               
    				this.containers[column.id].setWidth(column.getWidth()-1);            
    			}
    	  	}
    	 },
       
        resetFilters: function()
        {
            if(!this.fields)
                return;
            for(var fn in this.fields)
            {
                var f = this.fields[fn];
                if(!f.isDisabled() && !f.readOnly && Ext.isFunction(f.reset))
                    f.reset();
            }
            this.applyFilters();
        },
        
        clearFilters: function()
        {
            if(!this.fields)
                return;
            for(var fn in this.fields)
            {
                var f = this.fields[fn];
                if(!f.isDisabled() && !f.readOnly)
                    f.setValue('');
            }
            this.applyFilters();
        },
        
        setFilters: function(filters)
        {
            if(!filters)
                return;
            
            if(Ext.isArray(filters))
            {
                var conv = {};
                Ext.each(filters, function(filter){
                    if(filter.property)
                    {
                        conv[filter.property] = filter.value; 
                    }
                });
                filters = conv;
            }
            else if(!Ext.isObject(filters))
            {
                return;
            }
    
    
    
    
            this.initFilterFields(filters);
            this.applyFilters();
        },
        
        getFilters: function()
        {
            var filters = this.parseFilters();
            var res = new Ext.util.MixedCollection();
            for(var fn in filters)
            {
                var value = filters[fn];
                var field = this.fields[fn];
                res.add(new Ext.util.Filter({
                    property: fn,
                    value: value,
                    root: this.filterRoot,
                    label: field.fieldLabel
                }));
            }
            return res;
        },
        
        parseFilters: function()
        {
            var filters = {};
            if(!this.fields)
                return filters;
            for(var fn in this.fields)
            {
                var field = this.fields[fn];
                if(!field.isDisabled() && field.isValid())
                    filters[field.filterName] = field.getSubmitValue();
            }
            return filters;
        },
        
        initFilterFields: function(filters)
        {
            if(!this.fields)
                return;
    
    
    
    
            for(var fn in  filters)
            {
                var value = filters[fn];
                var field = this.fields[fn];
                if(field)
                {
                    this.setFieldValue(filterField, initValue);
                }
            }
        },
        
        countActiveFilters: function()
        {
            var fv = this.parseFilters();
            var af = 0;
            for(var fn in fv)
            {
                if(!Ext.isEmpty(fv[fn]))
                    af ++;
            }
            return af;
        },
        
        parseStoreFilters: function()
        {
            var sf = this.grid.getStore().filters;
            var res = {};
            sf.each(function(filter){
                var name = filter.property;
                var value = filter.value;
                if(name && value)
                {
                    res[name] = value;            
                }
            });
            return res;
        },
        
        applyFilters: function()
        {
            var filters = this.parseFilters();
            if(this.grid.fireEvent('beforeheaderfiltersapply', this.grid, filters, this.grid.getStore()) !== false)
            {
                var storeFilters = this.grid.getStore().filters;
                var exFilters = storeFilters.clone();
                var change = false;
                var active = 0;
                for(var fn in filters)
                {
                    var value = filters[fn];
                    
                    var sf = storeFilters.findBy(function(filter){
                        return filter.property == fn;
                    });
                    
                    if(Ext.isEmpty(value))
                    {
                        if(sf)
                        {
                            storeFilters.remove(sf);
                            change = true;
                        }
                    }
                    else
                    {
                        var field = this.fields[fn];
                        if(!sf || sf.value != filters[fn])
                        {
                            var newSf = new Ext.util.Filter({
                                root: this.filterRoot,
                                property: fn,
                                value: filters[fn],
                                label: field.fieldLabel
                            });
                            if(sf)
                            {
                                storeFilters.remove(sf);
                            }
                            storeFilters.add(newSf);
                            change = true;
                        }
                        active ++;
                    }
                }
                
                this.grid.fireEvent('headerfiltersapply', this.grid, filters, active, this.grid.getStore());
                if(change)
                {
                    var curFilters = this.getFilters();
                    this.grid.fireEvent('headerfilterchange', this.grid, curFilters, this.lastApplyFilters, active, this.grid.getStore());
                    this.lastApplyFilters = curFilters;
                }
            }
        },
        
    	reloadStore: function()
    	{
    		var gs = this.grid.getStore();
    		if(this.grid.getStore().remoteFilter)
    		{
    			if(this.storeLoaded)
    			{
    				gs.currentPage = 1;
    				gs.load();
    			}
    		}
    		else
          {
    			if(gs.filters.getCount()) 
             {
    				if(!gs.snapshot)
    					gs.snapshot = gs.data.clone();
    				else
    				{
    					gs.currentPage = 1;
    				}
                gs.data = gs.snapshot.filter(gs.filters.getRange());
                var doLocalSort = gs.sortOnFilter && !gs.remoteSort;
                if(doLocalSort)
    				{
    					gs.sort();
    				}
                // fire datachanged event if it hasn't already been fired by doSort
                if (!doLocalSort || gs.sorters.length < 1) 
                {
                	gs.fireEvent('datachanged', gs);
    				}
    			}
    		   else
    		   {
    				if(gs.snapshot)
    				{
    					gs.currentPage = 1;
    					gs.data = gs.snapshot.clone();
    		         delete gs.snapshot;
    		         gs.fireEvent('datachanged', gs);
    				}
    			}
    		}
    	}
    });

    Last edited by d.zucconi; 4 Mar 2012 at 11:25 PM. Reason: Updated source code for version 0.2.0

Turkiyenin en sevilen filmlerinin yer aldigi xnxx internet sitemiz olan ve porn sex tarzi bir site olan mobil porno izle sitemiz gercekten dillere destan bir durumda herkesin sevdigi bir site olarak tarihe gececege benziyor. Sitenin en belirgin ozelliklerinden birisi de Turkiyede gercekten kaliteli ve muntazam, duzenli porno izle siteleri olmamasidir. Bu yuzden iste. Ayrica en net goruntu kalitesine sahip adresinde yayinlanmaktadir. Mesela diğer sitelerimizden bahsedecek olursak, en iyi hd porno video arşivine sahip bir siteyiz. "The Best anal porn videos and slut anus, big asses movies set..." hd porno faketaxi