1. #1
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,506
    Vote Rating
    54
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default Flexible, multi-line Grid/Dataview/whatever filtering

    Flexible, multi-line Grid/Dataview/whatever filtering


    This is a Panel subclass which offers condition-based filtering of any Store, and so may be applied to a Grid or a DataView, or any Store-backed control.

    It infers test types and value input types from the Store's field definitions, and allows complex, multi-condition filters to be created, with each condition being enablable/disablable.

    Being a Panel subclass, the presentation method is up to the developer. It could be a collapsible border Panel as in the demo, or an item of a layout:'fit' popup Window, or it whatever you choose.

    The file below will run anywhere, and pull Ext files off cachefly. It offers filtering of the standard array-grid model.

    The code is based on the 3.0 code branch, so for 2.0, use "SimpleStore" instead of "ArrayStore"

    Code:
    <html>
    <head>
    <title>Array Grid Example With Filter</title>
    <link type="text/css" rel="stylesheet" href="http://extjs.cachefly.net/ext-3.2.0/resources/css/ext-all.css"/> 
    <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.2.0/adapter/ext/ext-base.js"></script> 
    <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.2.0/ext-all-debug.js"></script> 
    <style type="text/css">
    .x-filter-condition td {
        padding: 4px;
    }
    
    .add-button {
        background: url(../shared/icons/fam/add.gif)!important;
    }
    
    .delete-button {
        background: url(../shared/icons/fam/delete.gif)!important;
    }
    
    .condition-row-disabled {
        background: url(../shared/icons/fam/cross.gif)!important;
    }
    
    .condition-row-enabled {
        background: url(../shared/extjs/images/checked.gif)!important;
    }
    </style>
    <script type="text/javascript">
    Ext.BLANK_IMAGE_URL = 'http://extjs.cachefly.net/ext-3.2.0/resources/images/default/s.gif'; 
    Ext.override(Ext.Element, {
        getDistanceTo: function(x, y) {
            var b = this.getBox();
            if (y > b.bottom) {
                y = b.y - (y - b.bottom);
            }
            if (x > b.right) {
                x = b.x - (x - b.right);
            }
            if (x > b.x) {
                return b.y - y;
            }
            if (y > b.y) {
                return b.x - x;
            }
            return Math.round(Math.sqrt(Math.pow(b.y - y, 2) + Math.pow(b.x - x, 2)));
        }
    });
    
    /**
     * @class Ext.ux.ProximityFader
     * Manages visibility of a Component based on the proximity of the mouse to a configured trigger Element:<pre><code>
    new Ext.ux.ProximityFader({
        threshold: 100,                    // When within 100 pixels of
        trigger: proximityTriggerEl,    // this Element,
        component: myFloatingPanel        // Begin fading in this Component.
    });
     */
    Ext.ux.ProximityFader = Ext.extend(Object, {
        constructor: function(config) {
            Ext.apply(this, config);
            if (this.component) {
                this.init(this.component);
            }
        },
    
        init: function(component) {
            this.component = component;
            if (component.rendered) {
                this.onComponentRender(component);
            } else {
                component.on({
                    render: this.onComponentRender,
                    single: true,
                    scope: this,
                    delay: 1
                });
            }
            component.on({
                beforemove: this.onBeforeComponentMove,
                move: this.onComponentMove,
                scope: this
            });
    
    //      If we have not been configured with a trigger
            if (!this.trigger) {
                component.on({
                    show: this.onShow,
                    hide: this.onHide,
                    scope: this
                });
            }
        },
    
        onBeforeComponentMove: function() {
            if (this.locked) {
                this.ignoreMove = true;
            } else {
                this.lock();
            }
        },
    
        onComponentMove: function() {
            if (this.ignoreMove) {
                delete this.ignoreMove;
            } else {
                this.unlock();
            }
        },
    
        lock: function() {
            this.locked = true;
               this.component.show();
            this.el.setOpacity(1);
        },
    
        unlock: function() {
            this.locked = false;
        },
    
        onMouseMove: function(e) {
    
            if (this.locked) return;
    
            var o = 1, d = this.el.getDistanceTo.apply(this.trigger, e.getXY());
            if (d > this.threshold) {
                this.component.hide();
            } else if (d > 0) {
    
    //          Mouse is within range of the trigger, so show the Component if its not already visible
                if (this.trigger && !this.component.isVisible()) {
                    this.component.show();
                }
                var o = 1 - (d / this.threshold);
            }
            this.el.setOpacity(o);
            if (this.shadow) {
                this.shadow.setOpacity(o);
            }
        },
    
        onComponentRender: function(c) {
            if (!this.trigger) {
                this.trigger = c.el;
            }
            this.el = c.el;
            if (this.el.shadow) {
                this.shadow = this.el.shadow.el;
            }
        },
    
        onShow: function() {
            if (this.locked) return;
            Ext.getDoc().on('mousemove', this.onMouseMove, this);
        },
    
        onHide: function() {
            if (this.locked) return;
            Ext.getDoc().un('mousemove', this.onMouseMove, this);
        }
    });
    
    Ext.override(String, {
        startsWith: function(prefix) {
            return this.substr(0, prefix.length) == prefix;
        },
        
        endsWith: function(suffix) {
            var start = this.length - suffix.length;
            return (start > -1) && (this.substr(start) == suffix);
        }
    });
    
    Ext.override(Ext.Window.DD, {
        startDrag : Ext.Window.DD.prototype.startDrag.createInterceptor(function(){
            this.win.fireEvent("beforemove", this.win);
        })
    });
    
    Ext.ux.FilterCondition = Ext.extend(Ext.Container, {
        layout: {
            type: 'hbox'
        },
        defaults: {
            margins: '0 2 4 2'
        },
     
        cls: 'x-filter-condition',
     
        Field: Ext.data.Record.create(['name', 'type']),
    
        filterTestStore: function(testRec) {
        
            var types = Ext.data.Types,
                f = this.fieldCombo,
                idx = f.store.find(f.valueField, f.getValue());
    
    //      A Field is selected, so we can filter the test types available
            if (idx != -1) {
                var fieldType = f.store.getAt(idx).data.type,
                    inc = testRec.data.include,
                    exc = testRec.data.exclude;
    
                if (fieldType == types.AUTO) {
                    return true;
                }
    
    //          Explicitly including data types mean *ONLY* include those types
                if (inc) {
                    return inc.hasOwnProperty(fieldType.type);
                }
    
    //          If a type is excluded, return false
                if (exc && exc.hasOwnProperty(fieldType.type)) {
                    return false;
                }
                
    //          Default to including a test.
                return true;
            }
        },
     
        initComponent: function() {
            var tests = [
                [ '<', null, {boolean: true} ],
                [ '<=', null, {boolean: true} ],
                [ '=', null, null ],
                [ '!=', null, null ],
                [ '>=', null, {boolean: true} ],
                [ '>', null, {boolean: true} ],
                [ 'Starts with', {string: true}, null ],
                [ 'Ends with', {string: true}, null ],
                [ 'Contains', {string: true}, null ],
                [ 'Between', null, null ]
            ];
            this.testStore = new Ext.data.ArrayStore({
                idIndex: 0,
                fields: ['test', 'include', 'exclude'],
                data: tests,
                autoDestroy: true,
                filterFn: this.filterTestStore,
                filterScope: this
            });
    
    //      Bit flag to indicate when all fields have actualy been set to something so that autoApply filters
    //      Don't get applied before the user has actually set them up.
            this.fieldsChanged = 0;
    
            this.fields = this.store.reader.recordType.prototype.fields;
            this.fieldStore = new Ext.data.Store();
     
    //      Create a Store containing the field names and types
    //      in the passed Store.
            this.fields.each(function(f) {
                this.fieldStore.add(new this.Field(f))
            }, this);
     
    //      Create a Combo which allows selection of a field
            this.fieldCombo = new Ext.form.ComboBox({
                triggerAction: 'all',
                store: this.fieldStore,
                valueField: 'name',
                displayField: 'name',
                editable: false,
                forceSelection: true,
                mode: 'local',
                listeners: {
                    select: this.onFieldSelect,
                    scope: this
                }
            });
     
    //      Create a Combo which allows selection of a test
            this.testCombo = new Ext.form.ComboBox({
                width: 100,
                editable: false,
                forceSelection: true,
                valueField: 'test',
                displayField: 'test',
                mode: 'local',
                store: this.testStore,
                doQuery: Ext.form.ComboBox.prototype.onLoad,
                listeners: {
                    select: this.onTestSelect,
                    scope: this
                }
            });
    
    //      Inputs for each type of field. Hidden and shown as necessary
            this.booleanInput = new Ext.form.Checkbox({
                hidden: true,
                testFilter: function(rec) {
                    var t = rec.text;
                    return (t == '=') || (t == '!=');
                },
                listeners: {
                    check: this.onTestValueChange,
                    scope: this
                }
            });
            this.intInput = new Ext.form.NumberField({
                allowDecimals: false,
                hidden: true,
                listeners: {
                    valid: this.onTestValueChange,
                    scope: this
                }
            });
            this.floatInput = new Ext.form.NumberField({
                hidden: true,
                listeners: {
                    valid: this.onTestValueChange,
                    scope: this
                }
            });
            this.textInput = new Ext.form.TextField({
                hidden: true,
                enableKeyEvents: true,
                listeners: {
                    keypress: {
                        fn: this.onTestValueChange,
                        buffer: 50
                    },
                    change: this.onTestValueChange,
                    scope: this
                }
            });
            this.dateInput = new Ext.form.DateField({
                hidden: true,
                convertValue: function(d) {
                    return d.valueOf();
                },
                listeners: {
                    select: this.onTestValueChange,
                    valid: this.onTestValueChange,
                    scope: this
                }
            });
     
            this.cls = 'x-filter-condition';
            this.items = [{
                xtype: 'button',
                margins: '0 2 0 0',
                iconCls: 'delete-button',
                handler: this.removeSelf,
                scope: this,
                tooltip: 'Remove this condition'
            }, {
                xtype: 'button',
                handler: this.toggleEnabled,
                scope: this,
                iconCls: 'condition-row-enabled',
                tooltip: 'Enable/disable this condition'
            }, this.fieldCombo, this.testCombo, this.booleanInput, this.intInput, this.floatInput, this.textInput, this.dateInput];
            Ext.ux.FilterCondition.superclass.initComponent.apply(this, arguments);
        },
    
        removeSelf: function() {
            var o = this.ownerCt;
            o.remove(this, true);
            o.doLayout();
        },
    
        toggleEnabled: function(b) {
            b = Ext.get(b.el.query(b.buttonSelector));
            if (this.disabled) {
                b.removeClass('condition-row-disabled');
                b.addClass('condition-row-enabled');
                this.enable();
            } else {
                b.removeClass('condition-row-enabled');
                b.addClass('condition-row-disabled');
                this.disable();
            }
        },
    
        focus: function() {
            this.fieldCombo.focus();
        },
    
        onDisable: function() {
            for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
                if (!(it[i] instanceof Ext.Button)) {
                    it[i].disable();
                }
            }
            this.disabled = true;
            this.fireChangeEvent();
        },
     
        onEnable: function() {
            for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
                it[i].enable();
            }
            this.disabled = false;
            this.fireChangeEvent();
        },
    
        onFieldSelect: function(combo, rec, index) {
    //      Refresh the tests dropdown
            this.testStore.filterBy(this.filterTestStore, this);
            this.testStore.fireEvent('datachanged', this.fieldStore);
    
            var types = Ext.data.Types;
            this.booleanInput.hide();
            this.intInput.hide();
            this.floatInput.hide();
            this.textInput.hide();
            this.dateInput.hide();
            var t = rec.get('type');
            if (t == types.BOOLEAN) {
                this.booleanInput.show();
                this.valueInput = this.booleanInput;
            } else if (t == types.INT) {
                this.intInput.show();
                this.valueInput = this.intInput;
            } else if (t == types.FLOAT) {
                this.floatInput.show();
                this.valueInput = this.floatInput;
            } else if (t == types.DATE) {
                this.dateInput.show();
                this.valueInput = this.dateInput;
            } else {
                this.textInput.show();
                this.valueInput = this.textInput;
            }
            this.doLayout();
            this.fieldsChanged |= 1;
            this.fireChangeEvent();
        },
     
        onTestSelect: function(combo, rec, index) {
            this.fieldsChanged |= 2;
            this.fireChangeEvent();
            if (rec.get("test") == "Between") {
            	if (this.valueInput) {
    	            this.secondValueInput = this.valueInput.cloneConfig();
    	            this.add(this.secondValueInput);
    	            this.secondValueInput.show();
    	            this.doLayout();
    	        }
            } else {
                if (this.secondValueInput) {
                    this.remove(this.secondValueInput);
                    this.doLayout();
                    delete this.secondValueInput;
                }
            }
        },
    
        onTestValueChange: function() {
            this.fieldsChanged |= 4;
            this.fireChangeEvent();
        },
    
    //  Only fire the change event if it's an actually applied filter.
    //  During first run through, the change event should not fire.
        fireChangeEvent: function() {
            if (this.fieldsChanged == 7) {
                this.fireEvent("change", this);
            }
        },
    
        getValue: function() {
            return {
                field: this.fieldCombo.getValue(),
                test: this.testCombo.getValue(),
                value: this.valueInput.getValue()
            };
        },
    
        getXml: function() {
            if (!this.testCombo || !this.testCombo.getValue() || !this.valueInput) {
                return '';
            }
            return '<condition test="' + this.testCombo.getValue() + '">\n' +
                '  <field>' + this.fieldCombo.getValue() + '</field>\n' +
                '  <value>' + this.valueInput.getRawValue() + '</value>\n' +
            '</condition>';
        },
    
        getFilterFunction: function() {
            if (!this.filterFunction) {
                this.filterFunction = this.filterFunctionImpl.createDelegate(this);
            }
            return this.filterFunction;
        },
    
        filterFunctionImpl: function(rec) {
            var fieldValue = rec.get(this.fieldCombo.getValue()),
                v = this.valueInput.getValue(),
                v1 = this.secondValueInput ? this.secondValueInput.getValue() : null;
    
    //      If the field knows how to preprocess...
            if (this.valueInput.convertValue) {
                fieldValue = this.valueInput.convertValue(fieldValue);
                v = this.valueInput.convertValue(v);
                v1 = this.valueInput.convertValue(v1);
            }
    
            switch (this.testCombo.getValue()) {
                case '<':
                    return fieldValue < v;
    
                case '<=':
                    return fieldValue <= v;
    
                case '=':
                    return fieldValue == v;
    
                case '!=':
                    return fieldValue != v;
    
                case '>=':
                    return fieldValue >= v;
    
                case '>':
                    return fieldValue > v;
                    
                case 'Starts with':
                    return (Ext.isString(fieldValue) && fieldValue.startsWith(v));
    
                case 'Ends with':
                    return (Ext.isString(fieldValue) && fieldValue.endsWith(v));
    
                case 'Contains':
                    return (Ext.isString(fieldValue) && (fieldValue.indexOf(v) !== -1));
            
                case 'Between':
                    return (fieldValue >= v) && (fieldValue <= v1);
            
            }
        },
    
        isEmpty: function() {
            return ((this.fieldCombo.getValue.length == 0) && (this.testCombo.getValue().length == 0)) || !this.valueInput;
        }
    });
    
    Ext.ux.StoreFilter = Ext.extend(Ext.Panel, {
        constructor: function(config) {
            config = Ext.apply({}, {
                layout: 'anchor',
                bodyStyle: {
                    padding: '10px 0px 10px 10px',
                    overflow: 'auto'
                },
                defaults: {
                    xtype: 'container',
                    autoEl: {}
                },
                items: [{
                    cls: 'x-condition-header',
                    anchor: '-25',
                    layout: 'column',
                    style: {
                        'text-decoration': 'underline',
                        'font': 'bold small verdana',
                        'margin-bottom': '5px'
                    },
                    defaults: {
                        xtype: 'box',
                        style: {
                            'padding-left': '5px'
                        }
                    },
                    items: [{
                        style: {
                            'padding-left': '65px'
                        },
                        width: 235,
                        autoEl: {html: 'Field to test'}
                    }, {
                        width: 95,
                        autoEl: {html: 'Test type'}
                    }, {
                        autoEl: {html: 'Test value'}
                    }]
                }, this.addConditionButton = new Ext.Button({
                    iconCls: 'add-button',
                    handler: this.onAddConditionButtonClick,
                    scope: this,
                    tooltip: 'Add condition'
                })],
                bbar: new Ext.Toolbar([
                    this.filterButton = new Ext.Button({
                        text: "Filter",
                        tooltip: 'Filter grid',
                        handler: this.doFilter,
                        scope: this
                    }),
                    this.clearFilterButton = new Ext.Button({
                        text: "Clear Filter",
                        tooltip: 'Clear filters',
                        handler: this.clearFilter,
                        scope: this
                    })
                ])
            }, config);
            Ext.ux.StoreFilter.superclass.constructor.call(this, config);
        },
    
        onAddConditionButtonClick: function() {
            var c, j = this.items.getCount();
            if (j > 2) {
                c = this.items.items[j - 2];
                if (c.isEmpty()) {
                    return;
                }
            }
            c = new Ext.ux.FilterCondition({store: this.store});
            if (this.autoApply) {
                c.on({
                    change: this.doFilter,
                    destroy: this.doFilter,
                    scope: this
                })
            }
            this.insert(this.items.getCount() - 1, c);
            this.doLayout();
            this.addConditionButton.getEl().scrollIntoView(this.body);
            c.focus();
        },
    
        doFilter: function() {
            this.store.filterBy(this.getFilterFunction());
        },
    
        clearFilter: function() {
            this.store.clearFilter();
        },
    
        getFilterFunction: function() {
            if (!this.filterFunction) {
                this.filterFunction = this.filterFunctionImpl.createDelegate(this);
            }
            return this.filterFunction;
        },
    
        filterFunctionImpl: function(rec) {
            for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
                var c = it[i];
                if ((c instanceof Ext.ux.FilterCondition) && (!c.isEmpty()) && (!c.disabled)) {
                    var fn = c.getFilterFunction();
                    if (!fn(rec)) {
                        return false;
                    }
                }
            }
            return true;
        }
    });
    
    Ext.onReady(function(){
    
        Ext.QuickTips.init();
    
        var myData = [
            ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
            ['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
            ['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
            ['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
            ['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
            ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
            ['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
            ['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
            ['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
            ['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
            ['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
            ['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
            ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
            ['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
            ['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
            ['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
            ['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
            ['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
            ['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
            ['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
            ['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
            ['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
            ['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
            ['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
            ['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
            ['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
            ['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],            
            ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am'],
            ['3m Co',71.20,0.02,0.03,'9/1 12:00am']
        ];
    
        // example of custom renderer function
        function change(val){
            if(val > 0){
                return '<span style="color:green;">' + val + '</span>';
            }else if(val < 0){
                return '<span style="color:red;">' + val + '</span>';
            }
            return val;
        }
    
        // example of custom renderer function
        function pctChange(val){
            if(val > 0){
                return '<span style="color:green;">' + val + '%</span>';
            }else if(val < 0){
                return '<span style="color:red;">' + val + '%</span>';
            }
            return val;
        }
    
        // create the data store
        var store = new Ext.data.ArrayStore({
            fields: [
               {name: 'company'},
               {name: 'price', type: 'float'},
               {name: 'change', type: 'float'},
               {name: 'pctChange', type: 'float'},
               {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
            ],
            comparator: function(r1, r2){
                var v1 = r1.data[this.sortInfo.field], v2 = r2.data[this.sortInfo.field];
                return -(v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
            }
        });
        store.loadData(myData);
    
        var filter = new Ext.ux.StoreFilter({
            store: store,
            autoApply: true,
            border: false
        });
        var filterPanel = new Ext.Window({
            title: "Filters",
            closable: true,
            closeAction: 'hide',
            constrain: true,
            shadow: false,
            layout: 'fit',
            items: filter,
            width: 630,
            height: 200,
            renderTo: document.body,
            tools: [{
                id: 'unpin',
                handler: function(e, tool, panel, tc) {
                    if (tool.hasClass('x-tool-pin')) {
                        tool.addClass('x-tool-unpin');
                        tool.removeClass('x-tool-pin');
                        panel.plugins.unlock();
                    } else {
                        tool.addClass('x-tool-pin');
                        tool.removeClass('x-tool-unpin');
                        panel.plugins.lock();
                    }
                }
            }],
            plugins: new Ext.ux.ProximityFader({
                threshold: 100
            }),
            hidden: true
        });
        filterPanel.tools.close.hide();
    
        // create the Grid
        grid = new Ext.grid.GridPanel({
            id: 'static-grid',
            store: store,
            columns: [
                {id:'company',header: "Company", width: 160, sortable: true, dataIndex: 'company'},
                {header: "Price", width: 75, sortable: true, renderer: 'usMoney', dataIndex: 'price'},
                {header: "Change", width: 75, sortable: true, renderer: change, dataIndex: 'change'},
                {header: "% Change", width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
                {header: "Last Updated", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
            ],
            viewConfig: {
                emptyText: 'No matching data'
            },
            stripeRows: true,
            autoExpandColumn: 'company',
            title:'Array Grid',
            region: 'center',
            margins: '0 5 5 5',
            tbar: [{
                text: 'Filters...',
                handler: function(b, e) {
                    filterPanel.alignTo(grid.el, 't-t');
                    filterPanel.show();
                    filterPanel.el.slideIn('t');
                    filterPanel.tools.unpin.hide();
                    filterPanel.tools.close.show();
                    filterPanel.plugins.lock();
                }
            }],
            listeners: {
                render: function(g) {
                    g.el.on({
                        contextmenu: function(e) {
                            e.stopEvent();
                            filterPanel.setPagePosition(e.getXY());
                            filterPanel.show();
                            Ext.Window.prototype.doConstrain.call(filterPanel);
                        }
                    });
                },
                single: true
            }
        });
         
        new Ext.Viewport({
            layout:'fit',
            items: grid
        });
    });
    </script>
    </head>
    <body></body>
    </html>

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

      0  

    Default


    I have a error in line 553:

    PHP Code:
           comparator: function(r1r2){554            var v1 r1.data[this.sortInfo.field], v2 r2.data[this.sortInfo.field];555            return -(v1 v2 : (v1 v2 ? -0));556        
    Ext.data.ArrayStore is not a constructor

    Gretings,


    P.D: Please attach the example in a zip file and I will upload to my site for live example

  3. #3
    Sencha User
    Join Date
    Mar 2007
    Posts
    7,854
    Vote Rating
    4
    tryanDLS is on a distinguished road

      0  

    Default


    Did you read that line about switching to SimpleStore if you're using 2.x?

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

      0  

    Default


    No. But in code put "simplestore". Which is the change?

    Thanks,

  5. #5
    Sencha User
    Join Date
    Mar 2007
    Posts
    7,854
    Vote Rating
    4
    tryanDLS is on a distinguished road

      0  

    Default


    You have to replace the reference in the sample code if you're running 2.x.

  6. #6
    Sencha - Architect Dev Team aconran's Avatar
    Join Date
    Mar 2007
    Posts
    9,308
    Vote Rating
    125
    aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold aconran is a splendid one to behold

      0  

    Default


    Pretty Cool Nige.

    I wrote something very similar a ways back for another project. I'd suggest adding like/begins with/contains operators for the string fields.
    Aaron Conran
    @aconran
    Sencha Architect Development Team

  7. #7
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,506
    Vote Rating
    54
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    Yes, I was thinking of adding that. It's a very quick and dirty implementation in its current state.

    In our live implementation, all HQL (Hibernate Query Language) tests applicable to the field type are available, and the entire filter set is passed back to the server in XML format and transformed into a Hibername query.

  8. #8
    Sencha User
    Join Date
    Jun 2008
    Posts
    5
    Vote Rating
    0
    cerori is on a distinguished road

      0  

    Default


    I tested the file and found out that the images and stylesheet were broken.
    So I want to get the images and css. Can you tell me how can I get those?
    And Just let me know if It's possible to use Json reader for those.

    Thank you.

  9. #9
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,506
    Vote Rating
    54
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    First post updated to incorporate extra tests for String values as Aaron suggested.

    Plus serialization of the filter to XML for sending to a server.


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

      0  

    Default


    Hi Animal,

    Excellent work.

    One suggestion: Would be great use grid to add / remove filters instead of panel. In my opinion this feature would improve the interface and make it more "compact".

    I attach a image composition for clarify the idea.



    Thanks in advance,