Page 1 of 3 123 LastLast
Results 1 to 10 of 172

Thread: Grid header filters

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Sencha Premium User d.zucconi's Avatar
    Join Date
    Jun 2008
    Location
    Piacenza (Italy)
    Posts
    96
    Vote Rating
    5
      0  

    Default Grid header filters

    This is a plugin that enables server-side filters for grid in columns headers. The plugin enables the new ColumnModel configuration option filter. This attribute value must be a valid Ext.form.Field component configuration or an array of filter configuration objects. The field becomes the filter field used into your column header.
    The filter will affect Store request parameters, adding a new parameter to Store.baseParams.Parameter name can be specified in filter configuration using the filterName option. If not specified, the name will be equal to ColumnModel dataIndex.

    An ExtJS 4 version of this plugin is developed and documented on this thread:
    http://www.sencha.com/forum/showthre...276#post660276

    Versions:

    1.0.0 - 12/1/09
    • Some bug fixes
    • Plugin now enables some new methods on grid such as "getHeaderFilter(name)" or
      "getHeaderFilterField(name)"
    • Changed filters rendering
    • Added "filterEncoder" and "filterDecoder" cfg attributes. These parameters allow to enable functions to encode or decode filter value before and after store reload or grid status update
    1.0.2 - 14/04/09
    • Some bug fixes (such as access to load parameters for grid store)
    • Fixed text selection support for TextField filters on Mozilla Firefox (-moz-user-select CSS property)
    • Filters header panel background color now is transparent (not white)
    • Added support for filter highlighting when at least one filter is active (useful if you have an active filter on the last column that could not be visibile without horizontal scroll)
    1.0.5 - 13/07/09 (tested with Ext 3.0)
    • Added stateful configuration parameter to select if filters values should be saved with Ext.state.Provider. This parameter is true by default. Ex: new Ext.ux.grid.GridHeaderFilters({stateful: false}).
    • Fixed column auto-show if a filter has empty value
    • Changed and simplified filters rendering (see onRender())
    • Minor fixes and code cleaning (thanks to dzj)
    1.0.6 - 30/07/09 (tested with Ext 3.0 - 3.0.1 - 3.0.2)
    • The plugin now enables for the grid the new method applyHeaderFilters.
    • Added applyMode configuration parameter to select if filters should be applied every time filter field value changes or only if user select to apply filters. If the value of this parameter is set to 'auto', filters are applied when each field value is changed (default, as previous versions). If the value is set to 'enter', the filters are applied only when the users hits the "ENTER" key on the filter field or when the grid method "applyHeaderFilters" is called (ex. add a button on your grid tbar and use this method as click handler). This would help when you need to change more than one filter before reloading the grid content.
    1.0.9 - 29/10/09 (tested with Ext 3.0.0 up to 3.0.3)
    • The plugin now signals render event when filters rendering is done. This could be useful to load store only when filters are already initialized.
    • Added filters configuration parameter to initialize filters values at configuration time. These values will override values loaded from state (if filters are stateful)
    • Fixed bug for store reload on column move.
    1.0.12 - 06/08/10 (tested with Ext 3.1.2)
    • Added applyFilterEvent filter configuration parameter. Allows developers to choose the field-event that applies filter value.
    • Added ensureFilteredVisible plugin configuration parameter.
    • Fixed some bugs. Thanks to ob1, dolittle and dzj.
    2.0.3 - 23/12/10 (tested with Ext 3.3.1)
    • The plugin now supports more than one filter for each column. The value for the filter column model attribute can be an array of field configurations (with specific filterName for each field)
    • Changed highlight filter mode and introduced highlightCls configuration parameter
    • EXPERIMENTAL support for grid reconfiguration: the plugin now intercepts grid reconfigure event to reconfigure and render filters.
    • Code rewritten, simplified filters render, compatible with Ext 3.3.0
    2.0.5 - 25/02/11 (tested with Ext 3.3.1)
    • Filter values specified into filters config parameter are removed if doesn't match with any available filter field
    • Fixed destroy memory leak caused by typo (thanks to qwikso)
    2.0.6 - 03/03/11
    • Fixed disabled filter field support (thanks to dolittle)


    Usage:
    1. Create a new instance of Ext.ux.grid.GridHeaderFilters and put it into your grid plugins list
    2. Define filter configuration with filter configuration attribute in ColumnModel configuration
    Configuration example:

    Code:
    var gridCfg = {
     xtype: "grid",
      store: articleStore,
     plugins: [new Ext.ux.grid.GridHeaderFilters()],
     cm: new Ext.grid.ColumnModel([{
        header : "Code",
        width: 120,
        sortable: true,
        dataIndex: "CODE",
         filter: {xtype:"textfield", filterName:"CODE"}
      },{
        id: "ART_DESC",
        header: "Description",
        width: 300,
        sortable: true,
        dataIndex: "ART_DESC",
         filter: [{xtype:"textfield"},{xtype:"textfield"}]
      },{
        id: "TYPE_DESC",
        header: "Type",
        width: 100,
        sortable: false,
        dataIndex: "TYPE_DESC",
       filter: {
          xtype: "combo",
          mode: "local",
          store: [["A","Type1"],["B","Type2"]],
          triggerAction: "all"
       }
    
    }]) }
    Source code (version 2.0.6):
    Code:
    
    Ext.namespace("Ext.ux.grid");
    
    /**
     * @class Ext.ux.grid.GridHeaderFilters
     * @extends Ext.util.Observable
     * 
     * Plugin that enables filters in columns headers.
     * 
     * To add a grid header filter, put the "filter" attribute in column configuration of the grid column model.
     * This attribute is the configuration of the Ext.form.Field to use as filter in the header or an array of fields configurations.<br>
     * <br>
     * The filter configuration object can include some special attributes to manage filter configuration:
     * <ul>
     * <li><code>filterName</code>: to specify the name of the filter and the corresponding HTTP parameter used to send filter value to server. 
     * If not specified column "dataIndex" attribute will be used, if more than one filter is configured for the same column, the filterName will be the "dataIndex" followed by filter index (if index &gt; 0)</li>
     * <li><code>value</code>: to specify default value for filter. If no value is provided for filter (in <code>filters</code> plugin configuration parameter or from loaded status), 
     * this value will be used as default filter value</li>
     * <li><code>filterEncoder</code>: a function used to convert filter value returned by filter field "getValue" method to a string. Useful if the filter field getValue() method
     * returns an object that is not a string</li>
     * <li><code>filterDecoder</code>: a function used to convert a string to a valid filter field value. Useful if the filter field setValue(obj) method
     *                         needs an object that is not a string</li>
     * <li><code>applyFilterEvent</code></li>: a string that specifies the event that starts filter application for this filter field. If not specified, the "applyMode" is used. (since 1.0.10)</li>
     *    </ul>
     * <br>
     * Filter fields are rendered in the header cells within an <code>Ext.Panel</code> with <code>layout='form'</code>.<br>
     * For each filter you can specify <code>fieldLabel</code> or other values supported by this layout type.<br>
     * You can also override panel configuration using <code>containerConfig</code> attribute.<br>
     * <br>
     * This plugin enables some new grid methods:
     * <ul>
     * <li>getHeaderFilter(name)</li>
     * <li>getHeaderFilterField(name)</li> 
     * <li>setHeaderFilter(name, value)</li> 
     * <li>setHeaderFilters(object, [bReset], [bReload])</li>
     * <li>resetHeaderFilters([bReload])</li>
     * <li>applyHeaderFilters([bReload])</li>
     * </ul>
     * The "name" is the filterName (see filterName in each filter configuration)
     * 
     * @author Damiano Zucconi - http://www.isipc.it
     * @version 2.0.6 - 03/03/2011
     */
    Ext.ux.grid.GridHeaderFilters = function(cfg){if(cfg) Ext.apply(this, cfg);};
        
    Ext.extend(Ext.ux.grid.GridHeaderFilters, Ext.util.Observable, 
    {
        /**
         * @cfg {Number} fieldHeight
         * Height for each filter field used by <code>autoHeight</code>.
         */
        fieldHeight: 22,
        
        /**
         * @cfg {Number} padding
         * Padding for filter fields. Default: 2
         */
        fieldPadding: 1,
        
        /**
         * @cfg {Boolean} highlightOnFilter
         * Enable grid header highlight if active filters 
         */
        highlightOnFilter: true,
        
        /**
         * @cfg {String} highlightColor
         * Color for highlighted grid header
         */
        highlightColor: 'yellow',
        
        /**
         * @cfg {String} highlightCls
         * Class to apply to filter header when filters are highlighted. If specified overrides highlightColor.
         * See <code>highlightOnFilter</code>. 
         */
        highlightCls: null,
        
        /**
         * @cfg {Boolean} stateful
         * Enable or disable filters save and restore through enabled Ext.state.Provider
         */
        stateful: true,
        
        /**
         * @cfg {String} applyMode
         * Sets how filters are applied. If equals to "auto" (default) the filter is applyed when filter field value changes (change, select, ENTER).
         * If set to "enter" the filters are applied only when user push "ENTER" on filter field.<br> 
         * See also <code>applyFilterEvent</code> in columnmodel filter configuration: if this option is specified in
         * filter configuration, <code>applyMode</code> value will be ignored and filter will be applied on specified event.
         * @since Ext.ux.grid.GridHeaderFilters 1.0.6
         */
        applyMode: "auto",
        
        /**
         * @cfg {Object} filters
         * Initial values for filters (mapped with filters names). If this object is defined,
         * its attributes values overrides the corresponding filter values loaded from grid status or <code>value</code> specified in column model filter configuration.<br>
         * Values specified into column model configuration (filter <code>value</code> attribute) are ignored if this object is specified.<br>
         * See <code>filtersInitMode</code> to understand how these values are mixed with values loaded from grid status.
         * @since Ext.ux.grid.GridHeaderFilters 1.0.9
         */
        filters: null,
        
        /**
         * @cfg {String} filtersInitMode
         * If <code>filters</code> config value is specified, this parameter defines how these values are used:
         * <ul>
         * <li><code>replace</code>: these values replace all values loaded from grid status (status is completely ignored)</li>
         * <li><code>merge</code>: these values overrides values loaded from status with the same name. Other status values are keeped and used to init filters.</li>
         * </ul>
         * This parameter doesn't affect how filter <code>value</code> attribute is managed: it will be always ignored if <code>filters</code> object is specified.<br>
         * Default = 'replace'
         */
        filtersInitMode: 'replace',
        
        /**
         * @cfg {Boolean} ensureFilteredVisible
         * If true, forces hidden columns to be made visible if relative filter is set. Default = true.
         */
        ensureFilteredVisible: true,
        
        cfgFilterInit: false,
        
        /**
         * @cfg {Object} containerConfig
         * Base configuration for filters container of each column. With this attribute you can override filters <code>Ext.Container</code> configuration.
         */
        containerConfig: null,
        
        /**
         * @cfg {Number} labelWidth
         * Label width for filter containers Form layout. Default = 50.
         */
        labelWidth: 50,
        
        fcc: null,
        
        filterFields: null,
        
        filterContainers: null,
        
        filterContainerCls: 'x-ghf-filter-container',
        
        init:function(grid) 
        {
            this.grid = grid;
            
            var gv = this.grid.getView();
            gv.updateHeaders = gv.updateHeaders.createSequence(function(){
                this.renderFilters.call(this);
            },this).createInterceptor(function(){
                this.destroyFilters.call(this);
                return true;
            },this);
            this.grid.on({
                scope: this,
                render: this.onRender,
                resize: this.onResize,
                columnresize: this.onColResize,
                reconfigure: this.onReconfigure,
                beforedestroy: this.destroyFilters
            });
            //this.grid.on("columnmove", this.renderFilters, this);
            if(this.stateful)
            {
                this.grid.on("beforestatesave", this.saveFilters, this);
                this.grid.on("beforestaterestore", this.loadFilters, this);
            }
            
            //Column hide event managed
            this.grid.getColumnModel().on("hiddenchange", this.onColHidden, this);
            
            this.grid.addEvents(
            /**
          * @event filterupdate
          * <b>Event enabled on the GridPanel</b>: fired when a filter is updated
          * @param {String} name Filter name
          * @param {Object} value Filter value
          * @param {Ext.form.Field} el Filter field
          */    
            'filterupdate');
            
            this.addEvents(
                /**
              * @event render
              * Fired when filters render on grid header is completed
              * @param {Ext.ux.grid.GridHeaderFilters} this
              */    
                {'render': true}
            );
            
            //Must ignore filter config value ?
            this.cfgFilterInit = Ext.isDefined(this.filters) && this.filters !== null;
            if(!this.filters)
                this.filters = {};
            
            //Configuring filters
            this.configure(this.grid.getColumnModel());
                
            Ext.ux.grid.GridHeaderFilters.superclass.constructor.call(this);
            
            if(this.stateful)
            {
                if(!Ext.isArray(this.grid.stateEvents))
                    this.grid.stateEvents = [];
                this.grid.stateEvents.push('filterupdate');
            }
            
            //Enable new grid methods
            Ext.apply(this.grid, {
                headerFilters: this,
                getHeaderFilter: function(sName){
                    if(!this.headerFilters)
                        return null;
                    return this.headerFilters.filters[sName];    
                },
                setHeaderFilter: function(sName, sValue){
                    if(!this.headerFilters)
                        return;
                    var fd = {};
                    fd[sName] = sValue;
                    this.setHeaderFilters(fd);
                },
                setHeaderFilters: function(obj, bReset, bReload)
                {
                    if(!this.headerFilters)
                        return;
                    if(bReset)
                        this.resetHeaderFilters(false);
                    if(arguments.length < 3)
                        var bReload = true;
                    var bOne = false;
                    for(var fn in obj)
                    {
                        if(this.headerFilters.filterFields[fn])
                        {
                            var el = this.headerFilters.filterFields[fn];
                            this.headerFilters.setFieldValue(el,obj[fn]);
                            this.headerFilters.applyFilter(el, false);
                            bOne = true;
                        }
                    }
                    if(bOne && bReload)
                        this.headerFilters.storeReload();
                },
                getHeaderFilterField: function(fn)
                {
                    if(!this.headerFilters)
                        return;
                    if(this.headerFilters.filterFields[fn])
                        return this.headerFilters.filterFields[fn];
                    else
                        return null;
                },
                resetHeaderFilters: function(bReload)
                {
                    if(!this.headerFilters)
                        return;
                    if(arguments.length == 0)
                        var bReload = true; 
                    for(var fn in this.headerFilters.filterFields)
                    {
                        var el = this.headerFilters.filterFields[fn];
                        if(Ext.isFunction(el.clearValue)) 
                        {
                            el.clearValue();
                        } 
                        else 
                        {
                            this.headerFilters.setFieldValue(el, '');
                        }
                        this.headerFilters.applyFilter(el, false);
                    }
                    if(bReload)
                        this.headerFilters.storeReload();
                },
                applyHeaderFilters: function(bReload)
                {
                    if(arguments.length == 0)
                        var bReload = true;
                    this.headerFilters.applyFilters(bReload);
                }
            });
            
        },
        
        /**
         * @private
         * Configures filters and containers starting from grid ColumnModel
         * @param {Ext.grid.ColumnModel} cm The column model to use
         */
        configure: function(cm)
        {
            /*Filters config*/
            var filteredColumns = cm.getColumnsBy(function(cc){
                if(Ext.isObject(cc.filter) || Ext.isArray(cc.filter))
                    return true;
                else
                    return false;
            });
            
            /*Building filters containers configs*/
            this.fcc = {};
            for (var i = 0; i < filteredColumns.length; i++) 
            {
                var co = filteredColumns[i];
                var fca = co.filter;
                if(!Ext.isArray(fca))
                    fca = [fca];
                for(var ci = 0; ci < fca.length; ci++)
                {
                    var fc = Ext.apply({
                        filterName: ci > 0 ? co.dataIndex+ci : co.dataIndex
                    },fca[ci]);
                    Ext.apply(fc, {
                        columnId: co.id,
                        dataIndex: co.dataIndex,
                        hideLabel: Ext.isEmpty(fc.fieldLabel),
                        anchor: '100%'
                    });
                    
                    if(!this.cfgFilterInit && !Ext.isEmpty(fc.value))
                    {
                        this.filters[fc.filterName] = Ext.isFunction(fc.filterEncoder) ? fc.filterEncoder.call(this, fc.value) : fc.value;
                    }
                    delete fc.value;
                    
                    /*
                     * Se la configurazione del field di filtro specifica l'attributo applyFilterEvent, il filtro verrŗ applicato
                     * in corrispondenza di quest'evento specifico
                     */
                    if(fc.applyFilterEvent)
                    {
                        fc.listeners = {scope: this};
                        fc.listeners[fc.applyFilterEvent] = function(field){this.applyFilter(field);};
                        delete fc.applyFilterEvent;
                    }
                    else
                    {
                        //applyMode: auto o enter
                        if(this.applyMode === 'auto' || this.applyMode === 'change' || Ext.isEmpty(this.applyMode))
                        {
                            //Legacy mode and deprecated. Use applyMode = "enter" or applyFilterEvent
                            fc.listeners = 
                            {
                                change: function(field)
                                {
                                    var t = field.getXType();
                                    if(t=='combo' || t=='datefield'){ //avoid refresh twice for combo select 
                                    return;
                                    }else{
                                    this.applyFilter(field);
                                    }
                                },
                                specialkey: function(el,ev)
                                {
                                    ev.stopPropagation();
                                    if(ev.getKey() == ev.ENTER) 
                                        el.el.dom.blur();
                                },
                                select: function(field){this.applyFilter(field);},
                                scope: this    
                            };
                        }
                        else if(this.applyMode === 'enter')
                        {
                            fc.listeners = 
                            {
                                specialkey: function(el,ev)
                                {
                                    ev.stopPropagation();
                                    if(ev.getKey() == ev.ENTER) 
                                    {
                                        this.applyFilters();
                                    }
                                },
                                scope: this
                            };
                        }
                    }
                    
                    //Looking for filter column index
                    var containerCfg = this.fcc[fc.columnId];
                    if(!containerCfg)
                    {
                        containerCfg = {
                            cls: this.filterContainerCls,
                            border: false,
                            bodyBorder: false,
                            /*layout: 'vbox',
                            layoutConfig: {align: 'stretch', padding: this.padding},*/
                            labelSeparator: '', 
                            labelWidth: this.labelWidth,
                            layout: 'form',
                            style: {},
                            items: []
                        };
                        if(this.containerConfig)
                            Ext.apply(containerCfg, this.containerConfig);
                        this.fcc[fc.columnId] = containerCfg;
                    }
                    containerCfg.items.push(fc);
                }
            }
        },
        
        renderFilterContainer: function(columnId, fcc)
        {
            if(!this.filterContainers)
                this.filterContainers = {};
            //Associated column index
            var ci = this.grid.getColumnModel().getIndexById(columnId);
            //Header TD
            var td = this.grid.getView().getHeaderCell(ci);
            td = Ext.get(td);
            //Patch for field text selection on Mozilla
            if(Ext.isGecko)
                td.dom.style.MozUserSelect = "text";
            td.dom.style.verticalAlign = 'top';
            //Render filter container
            fcc.width = td.getWidth() - 3;
            var fc = new Ext.Container(fcc);
            fc.render(td);
            //Container cache
            this.filterContainers[columnId] = fc;
            //Fields cache    
            var height = 0;
            if(!this.filterFields)
                this.filterFields = {};
            var fields = fc.findBy(function(cmp){return !Ext.isEmpty(cmp.filterName);});
            if(!Ext.isEmpty(fields))
            {
                for(var i=0;i<fields.length;i++)
                {
                    var filterName = fields[i].filterName;
                    /*if(this.filterFields[filterName])
                    {
                        //Ext.destroy(this.filterFields[filterName])
                        delete this.filterFields[filterName];
                    }*/
                    this.filterFields[filterName] = fields[i];
                    height += fields[i].getHeight();
                }
            }
            
            return fc;
        },
        
        renderFilters: function()
        {
            if(!this.fcc)
                return;
            for(var cid in this.fcc)
            {
                this.renderFilterContainer(cid, this.fcc[cid]);
            }
            this.setFilters(this.filters);
            this.highlightFilters(this.isFiltered());
        },
        
        onRender: function()
        {
            this.renderFilters();
            if(this.isFiltered())
            {
                this.applyFilters(false);
            }
            this.fireEvent("render", this);
        },
        
        getFilterField: function(filterName)
        {
            return this.filterFields ? this.filterFields[filterName] : null;
        },
        
        /**
         * Sets filter values by values specified into fo.
         * @param {Object} fo Object with attributes filterName = value
         * @param {Boolean} clear If current values must be cleared. Default = false
         */
        setFilters: function(fo,clear)
        {
            this.filters = fo;
            
            if(this.filters && this.filterFields)
            {
                //Delete filters that doesn't match with any field
                for(var fn in this.filters)
                {
                    if(!this.filterFields[fn])
                        delete this.filters[fn];
                }
                
                for(var fn in this.filterFields)
                {
                    var field = this.filterFields[fn];
                    var value = this.filters[field.filterName];
                    if(Ext.isEmpty(value))
                    {
                        if(clear)
                            this.setFieldValue(field, '');
                    }
                    else
                        this.setFieldValue(field, value);
                }
            }
        },
        
        onColResize: function(index, iWidth){
            if(!this.filterContainers)
                return;
            var colId = this.grid.getColumnModel().getColumnId(index);
            var cnt = this.filterContainers[colId];
            if(cnt)
            {
                if(isNaN(iWidth))
                    iWidth = 0;
                var filterW = (iWidth < 3) ? 0 : (iWidth - 3);
                cnt.setWidth(filterW);
                //Thanks to ob1
                cnt.doLayout(false,true);
            }
        },
        
        /**
         * @private
         * Resize filters containers on grid resize
         * Thanks to dolittle
         */
        onResize: function() 
        {
            var n = this.grid.getColumnModel().getColumnCount();
            for(var i=0; i<n; i++) {
                var td = this.grid.getView().getHeaderCell(i);
                td = Ext.get(td);
                this.onColResize(i, td.getWidth());
            }
        },
        
        onColHidden: function(cm, index, bHidden){
            if(bHidden)
                return;
            var cw = this.grid.getColumnModel().getColumnWidth(index);
            this.onColResize(index, cw);
        },
        
        onReconfigure: function(grid, store, cm)
        {
            this.destroyFilters();
            this.configure(cm);
            this.renderFilters();
        },
        
        saveFilters: function(grid, status)
        {
            var vals = {};
            for(var name in this.filters)
            {
                vals[name] = this.filters[name];
            }
            status["gridHeaderFilters"] = vals;
            return true;
        },
       
        loadFilters: function(grid, status)
        {
            var vals = status.gridHeaderFilters;
            if(vals)
            {
                if(this.cfgFilterInit)
                {                    
                    if(this.filtersInitMode === 'merge')
                        Ext.apply(vals,this.filters);
                }
                else
                    this.filters = vals;
            }
        },
        
        isFiltered: function()
        {
            for(var k in this.filters)
            {
                if(/*this.filterFields && this.filterFields[k] && */!Ext.isEmpty(this.filters[k]))
                    return true;
            }
            return false;
        },
        
        highlightFilters: function(enable)
        {
            if(!this.highlightOnFilter)
                return;
            if(!this.filterContainers)
                return;
            if(!this.grid.getView().mainHd)
                return;
                
            var tr = this.grid.getView().mainHd.child('.x-grid3-hd-row');
            if(!Ext.isEmpty(this.highlightCls))
            {
                if(enable)
                    tr.addClass(this.highlightCls);
                else
                    tr.removeClass(this.highlightCls);
            }
            else
            {
                tr.setStyle('background-color',enable ? this.highlightColor : '');
            }
            /*for(var i=0; i < this.grid.getColumnModel().getColumnCount(); i++) 
            {
                var hc = Ext.get(this.grid.getView().getHeaderCell(i));
                if(!Ext.isEmpty(this.highlightCls))
                {
                    if(enable)
                        hc.addClass(this.highlightCls);
                    else
                        hc.removeClass(this.highlightCls);
                }
                else
                {
                    hc.setStyle('background-color',enable ? this.highlightColor : 'transparent');
                }
            }*/
            /*var color = enable ? this.highlightColor : 'transparent';
            for(var fn in this.filterContainers)
            {
                var fc = this.filterContainers[fn];
                if(fc.rendered)
                {
                    if(!Ext.isEmpty(this.highlightCls))
                    {
                        if(enable)
                            fc.getEl().addClass(this.highlightCls);
                        else
                            fc.getEl().removeClass(this.highlightCls);
                    }
                    else
                        fc.getEl().setStyle('backgroundColor',color);
                }
            }*/
        },
        
        getFieldValue: function(eField)
        {
            if(Ext.isFunction(eField.filterEncoder))
                return eField.filterEncoder.call(eField, eField.getValue());
            else
                return eField.getValue();
        },
        
        setFieldValue: function(eField, value)
        {
            if(Ext.isFunction(eField.filterDecoder))
                value = eField.filterDecoder.call(eField, value);
            eField.setValue(value);
        },
        
        applyFilter: function(el, bLoad)
        {
            if(arguments.length < 2)
                bLoad = true;
            if(!el)
                return;
                
            if(!el.isValid())
                return;
                
            if(el.disabled && !Ext.isDefined(this.grid.store.baseParams[el.filterName]))
                return;
            
            var sValue = this.getFieldValue(el);
            
            if(el.disabled || Ext.isEmpty(sValue))
            {
                delete this.grid.store.baseParams[el.filterName];
                delete this.filters[el.filterName];
            }
            else    
            {
                this.grid.store.baseParams[el.filterName] = sValue;
                this.filters[el.filterName] = sValue;
                
                if(this.ensureFilteredVisible)
                {
                    //Controllo che la colonna del filtro applicato sia visibile
                    var ci = this.grid.getColumnModel().getIndexById(el.columnId);
                    if((ci >= 0) && (this.grid.getColumnModel().isHidden(ci)))
                            this.grid.getColumnModel().setHidden(ci, false);
                }
            }
            
            //Evidenza filtri se almeno uno attivo
            this.highlightFilters(this.isFiltered());
            
            this.grid.fireEvent("filterupdate",el.filterName,sValue,el);
            
            if(bLoad)
                this.storeReload();
        },
        
        applyFilters: function(bLoad)
        {
            if(arguments.length < 1)
                bLoad = true;
            for(var fn in this.filterFields)
            {
                this.applyFilter(this.filterFields[fn], false);
            }
            if(bLoad)
                this.storeReload();
        },
        
        storeReload: function()
        {
            if(!this.grid.store.lastOptions)
                return;
            var slp = {start: 0};
            if(this.grid.store.lastOptions.params && this.grid.store.lastOptions.params.limit)
                slp.limit = this.grid.store.lastOptions.params.limit;
            this.grid.store.load({params: slp});
        },
        
        getFilterContainer: function(columnId)
        {
            return this.filterContainers ? this.filterContainers[columnId] : null; 
        },
        
        destroyFilters: function()
        {
            if(this.filterFields)
            {
                for(var ff in this.filterFields)
                {
                    Ext.destroy(this.filterFields[ff]);
                    delete this.filterFields[ff];
                }
            }
            
            if(this.filterContainers)
            {
                for(var ff in this.filterContainers)
                {
                    Ext.destroy(this.filterContainers[ff]);
                    delete this.filterContainers[ff];
                }
            }
            
        }
    });
    
    Attached Images Attached Images
    Last edited by d.zucconi; 14 Oct 2011 at 7:57 AM. Reason: New thread for ExtJS 4 versions

  2. #2
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    8
      0  

    Default

    Wow, nice first post!?

  3. #3
    Ext JS Premium Member
    Join Date
    May 2007
    Posts
    64
    Vote Rating
    0
      0  

    Default

    Looking nice. Contrary to other filter extensions you see the filter values at all times. Looking forward to some date from/to support

  4. #4
    Sencha Premium User d.zucconi's Avatar
    Join Date
    Jun 2008
    Location
    Piacenza (Italy)
    Posts
    96
    Vote Rating
    5
      0  

    Default

    Quote Originally Posted by MeDavid View Post
    Looking forward to some date from/to support
    Well, I know... the "period" filter is a need for me too.
    To support this kind of filter I'm thinking to develop (in the future ) a simple widget that groups 2 DateField. The getValue() method of this widget would return an object (or the equivalent JSON string) with 2 attributes (ex. {begin: "20080720", end: "20080820"} or {begin: "", end: "20080820"}). Then you can add this widget into your header filters.

    I've already developed a similar widget to support "checkboxes filter" (see the attachment). This is only a simple (and, sorry, undocumented) example, but it works fine in my use-cases.
    Attached Files Attached Files

  5. #5
    Sencha User
    Join Date
    Oct 2016
    Posts
    1
    Vote Rating
    0
      0  

    Default Doesnt work for me. Could you please share working MultiSelectField?

    Quote Originally Posted by d.zucconi View Post
    Well, I know... the "period" filter is a need for me too.
    To support this kind of filter I'm thinking to develop (in the future ) a simple widget that groups 2 DateField. The getValue() method of this widget would return an object (or the equivalent JSON string) with 2 attributes (ex. {begin: "20080720", end: "20080820"} or {begin: "", end: "20080820"}). Then you can add this widget into your header filters.

    I've already developed a similar widget to support "checkboxes filter" (see the attachment). This is only a simple (and, sorry, undocumented) example, but it works fine in my use-cases.

    Doesnt work for me. Could you please share working MultiSelectField?

  6. #6
    Sencha Premium User d.zucconi's Avatar
    Join Date
    Jun 2008
    Location
    Piacenza (Italy)
    Posts
    96
    Vote Rating
    5
      0  

    Default

    Hi,
    This is my latest version of Ext.ux.form.MultiSelectField (for ExtJS 3.x)
    Code:
    Ext.namespace("Ext.ux.form");
    /**
     * @class Ext.ux.form.MultiSelectField
     * @extends Ext.form.TriggerField
     * Un Field che permette la multiselezione di diversi valori inclusi in uno store.
     * Lo <code>store</code> dev'essere obbligatoriamente specificato nella configurazione.
     * E'possibile scegliere quali campi dei record vengono utilizzati per estrarre il valore e la descrizione
     * degli elementi selezionabili.<br>
     * Il field supporta sia store locali che remoti, pre-caricando eventualmente i valori se viene specificato
     * un <code>value</code> nella configurazione del campo.
     * @xtype multiselectfield
     */
    Ext.ux.form.MultiSelectField = Ext.extend(Ext.form.TriggerField, {
         /**
          * @cfg {Ext.data.Store} store Lo store associato a questo field contenente i valori selezionabili.
          */
         store: null,
        /**
         * @cfg {String} valueField Il campo dei record store che contiene il valore per gli elementi. Default = <code>value</code>
         */
        valueField : "value",
        /**
         * @cfg {String} displayField Il campo dei record store che contiene la descrizione degli elementi. Default = <code>label</code>
         */
        displayField : "label",
        /**
         * @cfg {String} itemSelectedField Il campo dei record store che contiene il flag selezionato/non selezionato. Default = <code>itemSelected</code>
         */
        itemSelectedField : "itemSelected",
        
        selectedValues : [],
        selectedLabels : [],
        expanded : false,
        
        /**
         * @cfg {String} separator Il separatore usato per effettuare il join dei valori elementi selezionati. Default = <code>;</code>
         */
        separator: ';',
        storeLoaded: false,
        hiddenField: null,
        
        /**
         * @cfg {Boolean} showTooltip Se visualizzare un tooltip sul field con le descrizioni dei valori selezionati. Default <code>true</code>.
         */
        showTooltip: true,
        /**
         * @cfg {Boolean} enableClear Se abilitare il bottoncino di cancella selezione sulla lista di selezione. Default <code>false</code>
         */
        enableClear: false,
        /**
         * @cfg {Boolean} enableSelectAll Se abilitare il bottoncino di seleziona tutto sulla lista di selezione. Default <code>false</code>
         */
        enableSelectAll: false,
        /**
         * @cfg {Boolean} enableNotIn
         * Indica se abilitare il toggle button utile a indicare e modificare la logica di selezione tra <code>IN</code>
         * e <code>NOT IN</code>, controllata altrimenti solo tramite il parametro di configurazione <code>selectionMode</code>.
         */
        enableNotIn: false,
        /**
         * @cfg {Boolean} showSummary Se mostrare in testa alla lista di selezione un'area contenente la lista dei valori selezionati. Default <code>false</code>
         */
        showSummary: false,
        itemDescCls: '',
        clearSelectionText: 'Clear selection',
        notInText: 'Not equal to',
        selectAllText: 'Select all',
        selectorWidth: 0,
        /**
         * @cfg {int} minSelectorWidth Larghezza minima dell'area di selezione. Default <code>200</code>
         */
        minSelectorWidth: 200,
        /**
         * @cfg {int} selectorHeight Altezza di default per il pannello di selezione. Default <code>400</code>
         */
        selectorHeight: 400,
        /**
         * @cfg {int} summaryHeight Altezza dell'area di riepilogo presentata se <code>showSummary</code> = <code>true</code>. Default = 28 
         */
        summaryHeight: 28,
        /**
         * @cfg {Ext.XTemplate|String} selectorTpl Il template per produrre l'HTML del selettore. Per default = 
    <pre>
    <tpl for=".">
    <div class="x-ux-msf-item" itemValue="{'+this.valueField+'}">
    <img src="' + Ext.BLANK_IMAGE_URL + '" class="x-ux-msf-item-icon x-ux-msf-item-icon-{[values.' + this.itemSelectedField + '?"selected":"unselected"' + ']}">
    <span class="x-ux-msf-item-desc x-ux-msf-item-desc-{[values.' + this.itemSelectedField + '?"selected":"unselected"' + ']} '+this.itemDescCls+'">{'+this.displayField+'}</span>
    </div>,
    </tpl>
    </pre> 
         */
        selectorTpl: null,
        
        //Private
        selectorShown: false,
        
        /**
         * @cfg {Array|Object} items Utilizzato quando il parametro di configurazione <code>store</code> non è specificato, permette di 
         * definire implicitamente lo store da utilizzare tramite un Array di Array (come previsto da Ext.data.ArrayReader) o
         * tramite un oggetto JavaScript che ha come nomi attributi i codici dei valori da presentare e come valore associato a ciascun
         * attributo la descrizione del valore.<br>
         * I nomi dei campi per i record inclusi in questo store generato automaticamente
         * saranno quelli definiti dai parametri di configurazione <code>valueField</code> e <code>displayField</code>.
         */
        items: null,
        
        /**
         * @cfg {String} selectionMode
         * Indica la modalità di selezione utilizzata dal componente. Questa può essere:<br>
         * <ul>
         * <li><code>in</code>: I valori selezionati sono da intendere con logica <code>IN</code></li>
         * <li><code>notin</code>: I valori selezionati sono da intendere con logica <code>NOT IN</code></li>
         * </ul>
         * Questa modalità influenza il valore generato da {@link #getValue()} che sarà preceduto da <code>&lt;&gt;</code>
         * nel caso di logica <code>notin</code>.
         */
        selectionMode: 'in',
        
        notInButton: null,
    
    
        initComponent : function() {
    
    
            Ext.ux.form.MultiSelectField.superclass.initComponent.call(this);
    
    
            this.addEvents(
                /**
              * @event selectorshow
              * Segnalato quando il pannello di selezione viene mostrato
              * @param {Ext.ux.form.MultiSelectField} this
              * @param {Array} values I valori selezionati attualmente
              * @param {Ext.Panel} selector Il pannello di selezione valori
              */    
                "selectorshow",
                /**
              * @event selectorhide
              * Segnalato quando il pannello di selezione viene nascosto
              * @param {Ext.ux.form.MultiSelectField} this
              * @param {Array} values I valori selezionati attualmente
              * @param {Ext.Panel} selector Il pannello di selezione valori
              */
                "selectorhide",
                /**
              * @event select
              * Segnalato quando un valore viene cliccato nel pannello di selezione (variandone la selezione)
              * @param {Ext.ux.form.MultiSelectField} this
              * @param {Object} value Il valore selezionato
              * @param {Object} desc La descrizione del valore selezionato
              * @param {Boolean} selected Lo stato (selezionato o meno) dopo il click
              * @param {Ext.data.Record} record Il record soggetto della variazione
              * @param {Ext.data.Store} store Lo store
              * @param {String} selectionMode il selection mode corrente
              */
                "select",
                /**
              * @event itemcontextmenu
              * Segnalato quando viene premuto il tasto destro su di un valore selezionabile
              * @param {Ext.ux.form.MultiSelectField} this
              * @param {Boolean} selected Se il valore è selezionato o meno
              * @param {Ext.data.Record} record Il record dello store a cui corrisponde il valore
              * @param {Ext.data.Store} store Lo store
              * @param {Ext.EventObject} ev L'evento
              */
                "itemcontextmenu",
                /**
              * @event itemcontainerclick
              * Segnalato quando viene cliccato il mouse nell'area di selezione senza che questo sia su
              * alcun item selezionabile
              * @param {Ext.ux.form.MultiSelectField} this
              * @param {Ext.EventObject} ev L'evento
              */
                "itemcontainerclick");
    
    
            //this.readOnly = true;
            this.selectorPanel = null;
    
    
            if (!this.store) {
                if (this.items instanceof Ext.data.Store) {
                    this.store = this.items;
                } else if (Ext.isArray(this.items)) {
                    this.store = new Ext.data.ArrayStore( {
                        fields : [ this.valueField, this.displayField ],
                        data : this.items,
                        idProperty : this.valueField
                    });
                } else {
                    var storeData = [];
                    for ( var key in this.items)
                        storeData.push( [ key, this.items[key] ]);
                    this.store = new Ext.data.ArrayStore( {
                        fields : [ this.valueField, this.displayField ],
                        data : storeData,
                        idProperty : this.valueField
                    });
                }
            }
    
    
            //this.store.on("load", this.buildMenu, this);
    
    
            //Modalità store (compatibile con Ext.form.ComboBox)
            if(this.mode)
                this.localStore = this.mode == 'local';
            
            //Se la modalità store non è specificata provo a ricavarla dal tipo store
            if(!Ext.isDefined(this.localStore))
                this.localStore = !this.store.proxy;
    
    
            
            /*this.on("keyup", function(tf, eo){
                if(eo.getKey() == eo.BACKSPACE || eo.getKey() == eo.DELETE)
                {
                    eo.stopEvent();
                    var oldVals = this.values;
                    this.reset();
                    this.fireEvent("change", this, this.values, oldVals);
                }
                else if(eo.getKey() != eo.ENTER)
                {
                    this.setRawValue(this.labels.join(","));
                }
            }, this);*/
        },
        
        onRender : function(ct, position) {
            Ext.ux.form.MultiSelectField.superclass.onRender.call(this, ct,    position);
            
            //Rimuovo nome dall'elemento del trigger field
            this.el.dom.removeAttribute('name');
            //Il nome viene dato all'hidden
            var hiddenCfg = {
                tag:'input', 
                type:'hidden',
                value: this.selectedValues.join(this.separator),
                id: this.id+":selectedValues"
            };
            if(!Ext.isEmpty(this.name))
                hiddenCfg.name = this.name;
            
            this.hiddenField = this.el.insertSibling( hiddenCfg,
                'before',
                true);
            if(Ext.isGecko){
                this.el.dom.setAttribute('autocomplete', 'off');
            }
            this.updateSelectedLabels();
    
    
        },
        
        initValue : function(){
            Ext.ux.form.MultiSelectField.superclass.initValue.call(this);
            if(this.hiddenField)
            {
                this.hiddenField.value =
                    Ext.isDefined(this.hiddenValue) ? this.hiddenValue :
                    Ext.isDefined(this.value) ? this.value : '';
            }
        },
        
        onNotInClick: function(b, notIn)
        {
            var oldJoin = this.buildTextValue();
            
            this.selectionMode = notIn ? 'notin' : 'in';
            
            this.changeSelection(this.selectedValues);
            
            var curJoin = this.buildTextValue();
            if( oldJoin != curJoin)
                this.fireEvent('change', this, curJoin, oldJoin);
        },
        
        buildSelector : function() 
        {
            if(this.selectorPanel)
                return;
            
            if(!this.selectorTpl)
            {
                this.selectorTpl = new Ext.XTemplate(
                        '<tpl for=".">',
                        '<div class="x-ux-msf-item" itemValue="{'+this.valueField+'}">',
                        '<img src="' + Ext.BLANK_IMAGE_URL + '" class="x-ux-msf-item-icon x-ux-msf-item-icon-{[values.' + this.itemSelectedField + '?"selected":"unselected"' + ']}">',
                        '<span class="x-ux-msf-item-desc x-ux-msf-item-desc-{[values.' + this.itemSelectedField + '?"selected":"unselected"' + ']} '+this.itemDescCls+'">{'+this.displayField+'}</span>',
                        '</div>',
                        '</tpl>'
                );
            }
            else if(Ext.type(this.selectorTpl) == 'string')
            {
                this.selectorTpl = new Ext.XTemplate(this.selectorTpl);
            }
            
            this.selectorDataView = new Ext.DataView({
                store: this.store,
                tpl: this.selectorTpl,
                multiSelect: false,
                overClass:'x-ux-msf-item-over',
                itemSelector:'div.x-ux-msf-item',
                region: 'center',
                style: {overflow: 'auto'},
                listeners: {
                    click: this.onItemClick,
                    contextmenu: this.onItemContextMenu,
                    containerclick: this.onItemContainerClick,
                    scope: this
                }
            });
            
            var selItems = [this.selectorDataView];
            
            if(this.showSummary)
            {
                this.selectorSummary = new Ext.form.DisplayField({
                    region: 'north',
                    height: this.summaryHeight,
                    style: {overflow: 'hidden'},
                    value: this.getRawValue(),
                    cls: 'x-ux-msf-summary'
                });
                selItems.push(this.selectorSummary);
            }
            
            var tools = [];
            if(this.enableSelectAll)
            {
                tools.push({
                    id: 'plus',
                    handler: this.onSelectAll,
                    scope: this,
                    qtip: this.selectAllText
                });
            }
            if(this.enableClear)
            {
                tools.push({
                    id: 'minus',
                    handler: this.onClearSelection,
                    scope: this,
                    qtip: this.clearSelectionText
                });
            }
            
            var buttons = null;
            if(this.enableNotIn)
            {
                this.notInButton = new Ext.Button({
                    text: this.notInText,
                    enableToggle: true,
                    scope: this,
                    toggleHandler: this.onNotInClick,
                    pressed: this.selectionMode === 'notin'
                });
                buttons = [this.notInButton];
            }
            
            this.selectorPanel = new Ext.Panel({
                layout: 'border',
                //autoWidth: true,
                height: this.selectorHeight,
                items: selItems,
                buttonAlign: 'center',
                buttons: buttons,
                tools: tools.length > 0 ? tools : null
            });
            
            //this.selectorPanel.mon('render',function(){this.fireEvent('selectorrender',this,this.selectorPanel);},this);
        },
        
        /**
         * Visualizza il pannello di selezione
         * @method showSelector
         */
        showSelector: function()
        {
            this.buildSelector();
            
            if(!this.selectorLayer)
            {
                this.selectorLayer = new Ext.Layer(
                {
                    constrain: false,
                    shadow: true,
                    cls: 'x-ux-msf-layer'
                });
                this.selectorLayer.swallowEvent('mousewheel');
                this.selectorPanel.render(this.selectorLayer.createChild());
            }
            
            
            var pad = this.selectorLayer.getFrameWidth('tb'),
                hpad = this.selectorLayer.getFrameWidth('lr'),
                ha = this.getPosition()[1] - Ext.getBody().getScroll().top,
                hb = Ext.lib.Dom.getViewHeight() - ha-this.getSize().height,
                space = Math.max(ha, hb, this.minHeight || 0) - this.selectorLayer.shadowOffset - pad - 5;
            
            var width = this.selectorWidth || Math.max(this.wrap.getWidth(), this.minSelectorWidth);
            var height = Math.min(space, this.selectorHeight);
    
    
            this.selectorDataView.setWidth(width - hpad);
            this.selectorLayer.show();
            this.selectorPanel.setSize(width - hpad, height - pad);
            
            var itemListHeight = this.selectorDataView.el ? this.selectorDataView.el.getComputedHeight() : 0;
            var freeBodyHeight = this.selectorPanel.body.getComputedHeight() - itemListHeight - 2;
            if(this.showSummary)
                freeBodyHeight -= this.summaryHeight;
    
    
            if(freeBodyHeight > 0)
            {
                height -= freeBodyHeight;
                this.selectorPanel.setSize(width - hpad, height - pad);
            }
            
            this.selectorLayer.setSize(width, height);
            
            this.selectorLayer.alignTo(this.el, "tl-bl?");
            
            
            Ext.getDoc().on({
                scope: this,
                mousewheel: this.hideSelectorIf,
                mousedown: this.hideSelectorIf
            });
            
            this.selectorShown = true;
            this.fireEvent('selectorshow',this, this.selectedValues, this.selectorPanel);
        },
        
        /**
         * Nasconde il pannello di selezione
         * @method hideSelector
         */
        hideSelector: function()
        {
            if(!this.selectorShown)
                return false;
            
            if(this.selectorLayer)
            {
                this.selectorLayer.hide();
            }
            
            Ext.getDoc().un('mousewheel', this.hideSelectorIf, this);
            Ext.getDoc().un('mousedown', this.hideSelectorIf, this);
            
            this.selectorShown = false;
            this.fireEvent('selectorhide',this, this.selectedValues, this.selectorPanel);
        },
        
        collapse : function(){
            if(this.hideSelector() !== false)
              this.fireEvent('collapse', this);
        },
    
    
        // private
        hideSelectorIf : function(e){
            if(!e.within(this.wrap) && !e.within(this.selectorLayer)){
                this.hideSelector();
            }
        },
        
        // private
        validateBlur : function(){
            return !this.selectorShown;
        },
        
        getCurrentSelection: function()
        {
            var res = [];
            this.store.each(function(r){
                if(r.get(this.itemSelectedField))
                    res.push(r.get(this.valueField));
            },this);
            return res;
        },
    
    
        onTriggerClick : function(e) 
        {
            Ext.ux.form.MultiSelectField.superclass.onTriggerClick.call(this, e);
    
    
            if (this.disabled) {
                return;
            }
    
    
            if(this.selectorShown)
            {
                this.hideSelector();
            }
            else
            {
                if(this.store.proxy && !this.store.lastOptions)
                {
                    this.store.load({
                        callback: function(){
                            this.showSelector();
                        },
                        scope: this
                    });
                }
                else
                    this.showSelector();
            }
        },
    
    
        /*onCheckChange: function(check, bChecked) 
        {
            var oldVals = this.selectedValues;
            this.changeSelection();//check, bChecked, this.selectedValues, oldVals);
            this.fireEvent("change", this, this.selectedValues, oldVals);
        },*/
    
    
        onDestroy : function() {
            Ext.destroy(
                    this.selectorDataView, 
                    this.selectorSummary, 
                    this.selectorPanel,
                    this.notInButton,
                    this.selectorLayer);
            Ext.ux.form.MultiSelectField.superclass.onDestroy.call(this);
            /*if(this.store)
                this.store.removeListener("load", this.buildMenu, this);*/
        },
        
        /**
         * @private
         * @return {String}
         */
        buildTextValue: function()
        {
            return (this.selectionMode === 'notin' && !Ext.isEmpty(this.selectedValues) ? '<>' : '')+this.selectedValues.join(this.separator);
        },
    
    
        changeSelection : function(selectedValues) {
            
            if(!selectedValues)
                selectedValues = this.getCurrentSelection();
            this.selectedValues = selectedValues;
            this.value = this.buildTextValue();
            if(this.hiddenField)
            {
                this.hiddenField.value = this.value;
            }
            this.updateSelectedLabels();
        },
        
        updateSelectedLabels: function()
        {
            var labels = [];
            var vals = this.selectedValues;
            this.store.each(function(r){
                var val = r.get(this.valueField);
                //Mi assicuro sempre che il valore sia una stringa prima di verificarne il matching
                if(Ext.isDefined(val))
                    val = '' + val;
                if(vals.indexOf(val) >= 0)
                {
                    labels.push(r.get(this.displayField));
                }
            },this);
            this.selectedLabels = labels;
            var rawVal = this.selectionMode === 'notin' && !Ext.isEmpty(this.selectedLabels) ? '<> ' : '';
            this.setRawValue(rawVal+this.selectedLabels.join(','));
            if(this.showTooltip && this.rendered)
                this.el.set({title: this.getRawValue()});
            if(this.selectorSummary)
                this.selectorSummary.setValue(rawVal+this.selectedLabels.join(', '));
        },
        
        updateSelection: function()
        {
            /*
             * Rimuovo dai valori attualmente selezionati quelli 
             * non compatibili con i valori previsti dallo store
             */
            if(!Ext.isEmpty(this.selectedValues))
            {
                var svf = [];
                for(var i=0; i < this.selectedValues.length; i++)
                {
                    var val = this.selectedValues[i];
                    var index = this.store.findBy(function(record){
                        var str = record.get(this.valueField);
                        if(Ext.isDefined(str))
                            str = ''+str;
                        return str == val;    
                    }, this);
                    if(index >= 0)
                        svf.push(val);
                }
                if(svf.length != this.selectedValues.length)
                {
                    this.selectedValues = svf;
                    this.value = this.buildTextValue();
                    if(this.hiddenField)
                    {
                        this.hiddenField.value = this.value;
                    }
                }
            }
            
            this.updateSelectedLabels();
            var sv = this.selectedValues;
            this.store.each(function(r){
                var val = r.get(this.valueField);
                //Mi assicuro sempre che il valore sia una stringa prima di verificarne il matching
                if(Ext.isDefined(val))
                    val = '' + val;
                r.set(this.itemSelectedField,sv.indexOf(val) >= 0);
            },this);
        },
        
        /**
         * Recupera il valore attuale. 
         * A seconda del valore del parametro di configurazione <code>selectionMode</code> il risultato sarà uguale alla concatenazione dei valori
         * selezionati (utilizzando il separatore <code>separator</code>) nel caso di selectionMode = <code>in</code>. Nel caso di selectionMode = <code>notin</code>
         * il valore selezionato sarà preceduto da <code>&lt;&gt;</code>.
         * @method getValue
         * @return {String} La stringa costruita facendo il join dei valori selezionati con il separatore <code>separator</code> ed eventualmente
         * preceduta da &lt;&gt; nel caso di selectionMode = <code>notin</code>
         */
        getValue : function() 
        {
            var prefix = this.selectionMode === 'notin' ? '<>' : '';
            return prefix + this.selectedValues.join(this.separator);
        },
        
        /**
         * Restituisce l'array dei valori selezionati
         * @method getValueArray
         * @return {Array} L'array dei valori selezionati
         */
        getValueArray: function()
        {
            return this.selectedValues;
        },
        
        setRawValue : function(v){
            return this.rendered && this.el ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
        },
        
        /**
         * @private
         * @param {String} v Il selectionMode
         */
        setSelectionMode: function(v)
        {
            if(this.notInButton)
            {
                this.notInButton.toggle(v === 'notin', true);
            }
            this.selectionMode = v;
            this.updateSelectedLabels();
        },
        
        /**
         * Restituisce la modalità di selezione corrente tra <code>in</code> e <code>notin</code>
         * @method getSelectionMode
         * @return {String} La modalità di selezione corrente
         */
        getSelectionMode: function()
        {
            return this.selectionMode;
        },
    
    
        /**
         * Setta la selezione corrente. Se il valore è una stringa e questa è preceduta da <code>&lt;&gt;</code>, verrà modificato
         * anche il <code>selectionMode</code> di questo controllo per passare a <code>notin</code>.
         * @method setValue
         * @param {String|Array} value I valori da selezionare separati da <code>separator</code>
         */
        setValue : function(sValue) {
    
    
            if(Ext.isArray(sValue))
            {
                this.selectedValues = [].concat(sValue);
                this.value = this.selectedValues.join(this.separator);
            }
            else
            {
                if (Ext.isEmpty(sValue))
                {
                    this.selectedValues = [];
                    this.value = '';
                }
                else
                {
                    var strval = Ext.ux.util.StringUtils.trim(''+sValue);
                    if(strval.indexOf('<>') == 0)
                    {
                        this.setSelectionMode('notin');
                        strval = strval.substring(2, strval.length);
                    }
                    else
                    {
                        this.setSelectionMode('in');
                    }
                    this.selectedValues = strval.split(this.separator);
                    this.value = this.buildTextValue();
                }
            }
            
            if(this.hiddenField)
            {
                this.hiddenField.value = this.value;
            }
            
            if(this.localStore)
            {
                this.updateSelection();
            } 
            else
            {
                if(this.storeLoaded)
                {
                    this.updateSelection();
                }
                else
                {
                    this.store.load({
                        callback : function(){
                            this.storeLoaded = true;
                            this.updateSelection();
                        },
                        scope : this
                    });
                }
            }
            return this;
        },
    
    
        /**
         * Resetta la selezione attuale
         * @method reset
         */
        reset : function() {
            this.setValue(null);
        },
        
        /**
         * Restituisce il numero di valori attualmente selezionati
         * @method numChecked
         * @return {int} Il numero di valori selezionati
         */
        numChecked : function() {
            return Ext.isArray(this.selectedValues) ? 0 : this.selectedValues.length;
        },
        
        getSelectedValues: function()
        {
            return this.selectedValues;
        },
        
        /**
         * Restituisce un array delle descrizioni dei valori selezionati
         * @method getSelectedLabels
         * @return {Array} Un array delle descrizioni dei valori selezionati
         */
        getSelectedLabels: function()
        {
            return this.selectedLabels;
        },
        
        onItemClick: function(dv, index, el, ev)
        {
            var record = this.store.getAt(index);
            var selected = record.get(this.itemSelectedField) ? true : false;
            record.set(this.itemSelectedField,!selected);
            this.fireEvent('select', this, record.get(this.valueField), record.get(this.displayField), record.get(this.itemSelectedField), record, this.store, this.selectionMode);
            var oldJoin = this.buildTextValue();
            this.changeSelection();
            var curJoin = this.buildTextValue();
            if( oldJoin != curJoin)
                this.fireEvent('change', this, curJoin, oldJoin);
        },
        
        onItemContainerClick: function(dv, ev)
        {
            this.fireEvent('itemcontainerclick', this, ev);
        },
        
        onItemContextMenu: function(dv, index, el, ev)
        {
            var record = this.store.getAt(index);
            if(record)
            {
                var selected = record.get(this.itemSelectedField) ? true : false;
                this.fireEvent('itemcontextmenu', this, selected, record, this.store, ev);
            }
        },
        
        getAllValues: function()
        {
            var res = [];
            this.store.each(function(r){
                res.push(r.get(this.valueField));
            },this);
            return res;
        },
        
        getValuesBy: function(selFn, scope)
        {
            var res = [];
            var records = this.store.queryBy(selFn, scope ? scope : this);
            records.each(function(record){
                res.push(record.get(this.valueField));
            }, this);
            return res;
        },
        
        selectAll: function()
        {
            var vals = this.getAllValues();
            this.setValue(vals);
        },
        
        /**
         * Seleziona tutti i valori i cui record sono ammessi
         * dalla funzione di selezione indicata. I valori individuati
         * vengono selezionati aggiungendoli alla selezione corrente
         * @method selectBy
         * @param {Function} selFn La funzione di selezione. Le vengono passati i singoli record dello store.
         * Deve restituire true o false per indicare se il record dev'essere selezionato
         * @param {Function} scope L'eventuale scope della funzione di ricerca. Se non indicato viene
         * usato come scope questo stesso oggetto
         */
        selectBy: function(selFn, scope)
        {
            var vals = this.getValuesBy(selFn, scope);
            if(vals.length == 0)
                return;
            var curVals = this.getValueArray();
            for(var i=0;i<vals.length;i++)
            {
                if(curVals.indexOf(vals[i]) < 0)
                    curVals.push(vals[i]);
            }
            this.setValue(curVals);
        },
        
        /**
         * Deseleziona tutti i valori i cui record sono ammessi
         * dalla funzione di selezione indicata. I valori individuati
         * vengono deselezionati rimuovendoli alla selezione corrente
         * @method unselectBy
         * @param {Function} selFn La funzione di selezione. Le vengono passati i singoli record dello store.
         * Deve restituire true o false per indicare se il record dev'essere deselezionato
         * @param {Function} scope L'eventuale scope della funzione di ricerca. Se non indicato viene
         * usato come scope questo stesso oggetto
         */
        unselectBy: function(selFn, scope)
        {
            var vals = this.getValuesBy(selFn, scope);
            if(vals.length == 0)
                return;
            var curVals = this.getValueArray();
            if(curVals.length == 0)
                return;
            var res = [];
            for(var i=0;i<curVals.length;i++)
            {
                if(vals.indexOf(curVals[i]) < 0)
                    res.push(curVals[i]);
            }
            this.setValue(res);
        },
        
        /**
         * Imposta il valore di selezione corrente selezionando solo i record
         * individuati come selezionabili dalla funzione indicata.
         * La selezione corrente viene completamente rimpiazzata dalla selezione
         * formulata secondo la funzione indicata.
         * @method setValueBy
         * @param {Function} selFn La funzione di selezione. Le viene passato ciascun
         * record dello store e deve restituire true o false per indicare se questo record
         * rientrerà nella nuova selezione
         * @param {Function} scope Lo scope per la funzione. Se non indicato viene utilizzato
         * lo scope corrente
         */
        setValueBy: function(selFn, scope)
        {
            var res = this.getValuesBy(selFn, scope);
            this.setValue(res);
        },
        
        onClearSelection: function()
        {
            var oldVals = this.selectedValues;
            this.setValue('');
            var oldJoin = oldVals.join(this.separator);
            var curJoin = this.selectedValues.join(this.separator);
            if(oldJoin != curJoin)
                this.fireEvent("change", this, curJoin, oldJoin);
        },
        
        onSelectAll: function()
        {
            var oldVals = this.selectedValues;
            this.setValue(this.getAllValues());
            var oldJoin = oldVals.join(this.separator);
            var curJoin = this.selectedValues.join(this.separator);
            if(oldJoin != curJoin)
                this.fireEvent("change", this, curJoin, oldJoin);
        },
        
        /**
         * Permette di accedere al pannello di selezione (se questo è già stato renderizzato)
         * @method getSelectorPanel
         * @return {Ext.Panel} Il pannello di selezione
         */
        getSelectorPanel: function()
        {
            return this.selectorPanel;
        }
    
    
    });
    
    
    Ext.reg('multiselectfield', Ext.ux.form.MultiSelectField);

  7. #7
    Ext User
    Join Date
    Jul 2008
    Posts
    4
    Vote Rating
    0
      0  

    Default

    Nice!But have some bugs,when the cols hidden,you need to resize the ColumnModel.My English is not good,Sorry

  8. #8
    Sencha User
    Join Date
    Dec 2007
    Posts
    96
    Vote Rating
    0
      0  

    Default need to show the filter field above the header label

    hi,
    Thank you very much for this great plugin. As per my requirement i need to show the filter field up above the header label instead of downside that is there in the plugin that you've provided. I tried to do this

    Ext.get(headerDiv).createChild({id: "col-"+colCfg.id+"-filter-div", tag: "div"},Ext.get(headerDiv).dom.childNodes[0])

    When i did this, the filter fields are getting rendered as per my requirement. But the problems that i've got are as follows
    1. I could not able to see the header menu's icon which will come beside the header when we mouseover on it.
    2. When i try to move the column by drag & drop of column label, I could not able to see the header label in the flyout that will come along with the mouse.

    Please help in solving this problem. Thanks in advance.

  9. #9
    Ext User
    Join Date
    Feb 2009
    Posts
    5
    Vote Rating
    0
      0  

    Default Example HTML

    Hello,

    I wanted to test the GridHeaderFilters example (on Ext 2.2.1) and got stuck (I'm a beginner...). Can you please provide a ready-to-run example? I'm getting the error "sp is undefined" in ext-base.js

    Thanks!

  10. #10
    Sencha Premium User d.zucconi's Avatar
    Join Date
    Jun 2008
    Location
    Piacenza (Italy)
    Posts
    96
    Vote Rating
    5
      0  

    Default

    Hi,

    I've updated code version on the first page of this post (1.0.0 12/1/09).
    I've removed the zip attachment and reported plugin code directly in the message.
    This is the latest version of my plugin and is the same version I'm currently using in my webapps.
    About your error is hard to say for me what could be the cause...
    A valid usage example is the grid code reported in the "Configuration example" on the first page... However you could post your code to check if I can help you directly with your problem

Page 1 of 3 123 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •