View Poll Results: Do You Like This???

Voters
2. You may not vote on this poll
  • Yes

    1 50.00%
  • No

    1 50.00%
  1. #1
    Ext JS Premium Member Artistan's Avatar
    Join Date
    Apr 2007
    Location
    MN
    Posts
    144
    Vote Rating
    0
    Artistan is on a distinguished road

      0  

    Default BinaryMultiSelect - int value to select multiple binary items

    BinaryMultiSelect - int value to select multiple binary items


    I recently started using Ext in a project and wanted to share this.
    Due to a friend's lack of fore-thought, I have to use a binary field for options in many places. Ext does not have a great interface for this that I know of (multi-select with binary values). I found the MultiSelect.js extension and decided to modify it for binary values.
    This allows for my Direct api to auto load the selected values and submit the sum back to the server. PERFECT!

    Here is an example of the form item I am using...

    [update Sun Sept 12,2009] - modified the get and set functions so they will save the setValue before being rendered (tabs within form).
    It will then render with the correct selections without reloading the data source.
    [update Mon Sept 14,2009] - updated for better selection of values and updated a few parts that needed to use the valueField to do its work.
    Now it will work with my DirectStore functions to load dynamic lists and preselect them. WooHoo!

    Basic example...
    Code:
    {
        xtype: 'binarymultiselect',
        fieldLabel: 'Trading Regions',
        name: 'trading_regions',
        width: 250,
        height: 200,
        allowBlank:true,
        store: [
    	    [1 ,'North America' ],
    	    [2 ,'South America' ],
    	    [4 ,'Africa'        ],
    	    [8 ,'Middle East'   ],
    	    [16,'Europe'        ],
    	    [32,'Oceania'       ],
    	    [64,'Asia'          ],
    	],
        tbar:[{
    	text: 'CLEAR',
    	handler: function(x_object,x_action){
    	    x_object.getForm().findField('multiselect').reset();
    	}
        }]
    }
    DirectStore example...
    Code:
                        {
                            xtype: 'binarymultiselect',
                            fieldLabel: 'Categories',
                            name: 'categories',
                            width: 250,
                            height: 185,
                            allowBlank:true,
                            displayField:'name',
                            valueField:'flag',
                            store: {
                                xtype: 'directstore',
                                autoDestroy: true,
                                autoLoad: true,
                                directFn: Ext.bb.Common.categories,
                                totalProperty: 'totalCount',
                                root: 'items',
                                paramOrder: ['grouping_id'],
                                baseParams: {grouping_id:0},
                                paramsAsHash:false,
                                fields: [
                                   {name: 'flag', type: 'int'},
                                   {name: 'name'},
                                   {name: 'default', type: 'int'},
                                   {name: 'group', type: 'int'},
                                   {name: 'order'}
                                ]
                            }
                        }
    And the extension code....
    Code:
    /*!
     * Ext JS Library 3.0.0
     * Copyright(c) 2006-2009 Ext JS, LLC
     * licensing@extjs.com
     * http://www.extjs.com/license
     */
    Ext.ns('Ext.ux.form');
    
    /**
     * @class Ext.ux.form.BinaryMultiSelect
     * @extends Ext.form.Field
     * A control that allows selection and form submission of multiple binary items added together
     *
     *  @history
     *    2009-09-10 Charles Peterson - Reimplemented MultiSelect for binary values, Modified code for Binary Selection
     *    2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)
     *    2008-06-19 bpm Docs and demo code clean up
     *
     * @constructor
     * Create a new BinaryMultiSelect
     * @param {Object} config Configuration options
     * @xtype BinaryMultiSelect
     */
    Ext.ux.form.BinaryMultiSelect = Ext.extend(Ext.form.Field,  {
        /**
         * @cfg {String} legend Wraps the object with a fieldset and specified legend.
         */
        /**
         * @cfg {Ext.ListView} view The {@link Ext.ListView} used to render the BinaryMultiSelect list.
         */
        view:false,
        /**
         * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a
         * toolbar config, or an array of buttons/button configs to be added to the toolbar.
         */
        /**
         * @cfg {Number} width Width in pixels of the control (defaults to 100).
         */
        width:100,
        /**
         * @cfg {Number} height Height in pixels of the control (defaults to 100).
         */
        height:100,
        /**
         * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0).
         */
        displayField:0,
        /**
         * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1).
         */
        valueField:1,
        /**
         * @cfg {Boolean} allowBlank False to require at least one item in the list to be selected, true to allow no
         * selection (defaults to true).
         */
        allowBlank:true,
        /**
         * @cfg {Number} minSelections Minimum number of selections allowed (defaults to 0).
         */
        minSelections:0,
        /**
         * @cfg {Number} maxSelections Maximum number of selections allowed (defaults to Number.MAX_VALUE).
         */
        maxSelections:Number.MAX_VALUE,
        /**
         * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as
         * {@link Ext.form.TextField#blankText}.
         */
        blankText:Ext.form.TextField.prototype.blankText,
        /**
         * @cfg {String} minSelectionsText Validation message displayed when {@link #minSelections} is not met (defaults to 'Minimum {0}
         * item(s) required').  The {0} token will be replaced by the value of {@link #minSelections}.
         */
        minSelectionsText:'Minimum {0} item(s) required',
        /**
         * @cfg {String} maxSelectionsText Validation message displayed when {@link #maxSelections} is not met (defaults to 'Maximum {0}
         * item(s) allowed').  The {0} token will be replaced by the value of {@link #maxSelections}.
         */
        maxSelectionsText:'Maximum {0} item(s) allowed',
        /**
         * @cfg {Ext.data.Store/Array} store The data source to which this BinaryMultiSelect is bound (defaults to <tt>undefined</tt>).
         * Acceptable values for this property are:
         * <div class="mdetail-params"><ul>
         * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
         * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.
         * <div class="mdetail-params"><ul>
         * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
         * A 1-dimensional array will automatically be expanded (each array item will be the combo
         * {@link #valueField value} and {@link #displayField text})</div></li>
         * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
         * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
         * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.
         * </div></li></ul></div></li></ul></div>
         */
    
        // private
        defaultAutoCreate : {tag: "div"},
    
        // private
        initComponent: function(){
            console.log('BINARY SELECT: initComponent');
            Ext.ux.form.BinaryMultiSelect.superclass.initComponent.call(this);
    
            if(Ext.isArray(this.store)){
                if (Ext.isArray(this.store[0])){
                    this.store = new Ext.data.ArrayStore({
                        fields: ['value','text'],
                        data: this.store
                    });
                    this.valueField = 'value';
                }else{
                    this.store = new Ext.data.ArrayStore({
                        fields: ['text'],
                        data: this.store,
                        expandData: true
                    });
                    this.valueField = 'text';
                }
                this.displayField = 'text';
            } else {
                this.store = Ext.StoreMgr.lookup(this.store);
            }
            this.addEvents({
                'dblclick' : true,
                'click' : true,
                'change' : true
            });
            console.log('BINARY SELECT: /initComponent');
            console.log('BINARY SELECT: this',this);
        },
    
        // private
        onRender: function(ct, position){
            console.log('BINARY SELECT: onRender');
            Ext.ux.form.BinaryMultiSelect.superclass.onRender.call(this, ct, position);
    
            var fs = this.fs = new Ext.form.FieldSet({
                renderTo: this.el,
                title: this.legend,
                height: this.height,
                width: this.width,
                style: "padding:0;",
                tbar: this.tbar,
                bodyStyle: 'overflow: auto;'
            });
    
            this.view = new Ext.ListView({
                multiSelect: true,
                simpleSelect: true,
                store: this.store,
                columns: [{ header: 'Value', width: 1, dataIndex: this.displayField }],
                hideHeaders: true
            });
    
            fs.add(this.view);
    
            this.view.on('click', this.onViewClick, this);
            this.view.on('beforeclick', this.onViewBeforeClick, this);
            this.view.on('dblclick', this.onViewDblClick, this);
    
            this.hiddenName = this.name || Ext.id();
            var hiddenTag = { tag: "input", type: "hidden", value: "", name: this.hiddenName };
            this.hiddenField = this.el.createChild(hiddenTag);
            this.hiddenField.dom.disabled = this.hiddenName != this.name;
            fs.doLayout();
            console.log('BINARY SELECT: /onRender');
        },
    
        // private
        afterRender: function(){
            console.log('BINARY SELECT: afterRender');
            Ext.ux.form.BinaryMultiSelect.superclass.afterRender.call(this);
            // add after render stuff...
            console.log('BINARY SELECT: afterRender2');
        },
    
        // private
        onViewClick: function(vw, index, node, e) {
            console.log('BINARY SELECT: onViewClick',this.getValue());
            var result = this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
            console.log('BINARY SELECT: onViewClick, result1',result);
            result = this.hiddenField.dom.value = this.getValue();
            console.log('BINARY SELECT: onViewClick, result2',result);
            result = this.fireEvent('click', this, e);
            console.log('BINARY SELECT: onViewClick, result3',result);
            result = this.validate();
        },
    
        // private
        onViewBeforeClick: function(vw, index, node, e) {
            if (this.disabled) {return false;}
        },
    
        // private
        onViewDblClick : function(vw, index, node, e) {
            return this.fireEvent('dblclick', vw, index, node, e);
        },
    
        /**
         * Returns an sum value of data values for the selected items in the list.
         * @return {Int} value Sum of all data values
         */
        getValue: function(valueField){
            try{
                valueField = (valueField != null) ? valueField : this.valueField;
                console.log('BINARY SELECT: getValue,valueField',valueField);
    
                var returnInt = 0;
                if(this.rendered){
                    console.log('BINARY SELECT: this.rendered');
                    var selectionsArray = this.view.getSelectedIndexes();
                    if (selectionsArray.length == 0) {
                        if(this.value){
                            returnInt = this.value;
                        } else {
                            returnInt = '';
                        }
                    } else {
                        for (var i=0; i<selectionsArray.length; i++) {
                            returnInt += this.store.getAt(selectionsArray[i]).get(valueField);
                        }
                    }
                } else {
                    console.log('BINARY SELECT: NOT this.rendered');
                    returnInt = this.value;
                }
                console.log('BINARY SELECT: getValue',returnInt);
                return returnInt;
            } catch(e) {
                console.log('BINARY SELECT: getValue:ERROR',e)
            }
        },
    
        /**
         * Sets an binary value or array of data values into the list.
         * @param {Int/Array} values The values to set
         */
        setValue: function(values) {
            console.log('BINARY SELECT: setValue',values);
    
            if (!values || (values == '')) { return; }
    
            if (!Ext.isArray(values)) {
                // create a array of the binary values
                // check each field for match against value
                var tmpVal = values;
                var total=1;
                var totalBinary = this.store.sum(this.valueField);
                var currentVal='';
                values = [];
                while( total <= totalBinary ){
                    item = this.store.query(this.valueField,new RegExp('^' + total + '$', "i")).itemAt(0);
                    if(typeof item != "undefined"){
                        currentVal = item.data[this.valueField];
                        if(tmpVal & currentVal){
                            // if value of current item matches binary Values selected...
                            values.push( currentVal );
                        }
                    }
                    total = total * 2;
                }
    
            }
            console.log('BINARY SELECT: setValue2',values);
    
            var sum = 0;
            for (i=0; i<values.length; i++){
                sum = sum + values[i];
            }
            this.value=sum;
    
            if(this.view == false){
                return;
            }
            var index;
            var selections = [];
            this.view.clearSelections();
            this.hiddenField.dom.value = '';
    
            for (var i=0; i<values.length; i++) {
                index = this.store.indexOf(this.store.query(this.valueField,new RegExp('^' + values[i] + '$', "i")).itemAt(0));
                selections.push(index);
            }
            this.view.select(selections);
            this.hiddenField.dom.value = this.getValue();
            this.validate();
        },
    
        // inherit docs
        reset : function() {
            console.log('BINARY SELECT: reset');
            this.setValue('');
        },
    
        // inherit docs
        getRawValue: function(valueField) {
            valueField = (valueField != null) ? valueField : this.valueField;
    
            var tmpVal = this.getValue(valueField);
            if(!this.rendered){
                tmpVal=this.value;
            }
            // create a array of the binary values
            // check each field for match against value
            var total=1;
            var totalBinary = this.store.sum(valueField);
            var currentVal='';
            var values = [];
            while( total <= totalBinary ){
                item = this.store.query(this.valueField,new RegExp('^' + total + '$', "i")).itemAt(0);
                if(typeof item != "undefined"){
                    currentVal = item.data[this.valueField];
                    if(tmpVal & currentVal){
                        // if value of current item matches binary Values selected...
                        values.push( currentVal );
                    }
                }
                total = total * 2;
            }
            console.log('BINARY SELECT: getRawValue',values);
            // return the array of individual binary values...
            return values;
        },
    
        // inherit docs
        setRawValue: function(values){
            console.log('BINARY SELECT: setRawValue',values);
            this.setValue(values);
        },
    
        // inherit docs
        validateValue : function(value){
            console.log('BINARY SELECT: validateValue',value);
            if (value.length < 1) { // if it has no value
                 if (this.allowBlank) {
                     this.clearInvalid();
                     return true;
                 } else {
                     this.markInvalid(this.blankText);
                     return false;
                 }
            }
            if (value.length < this.minSelections) {
                this.markInvalid(String.format(this.minSelectionsText, this.minSelections));
                return false;
            }
            if (value.length > this.maxSelections) {
                this.markInvalid(String.format(this.maxSelectionsText, this.maxSelections));
                return false;
            }
            return true;
        },
    
        // inherit docs
        disable: function(){
            console.log('BINARY SELECT: disable');
            this.disabled = true;
            this.hiddenField.dom.disabled = true;
            this.fs.disable();
        },
    
        // inherit docs
        enable: function(){
            console.log('BINARY SELECT: enable');
            this.disabled = false;
            this.hiddenField.dom.disabled = false;
            this.fs.enable();
        },
    
        // inherit docs
        destroy: function(){
            console.log('BINARY SELECT: destroy');
            // destroy stuff...
            Ext.ux.form.BinaryMultiSelect.superclass.destroy.call(this);
        }
    });
    
    
    Ext.reg('binarymultiselect', Ext.ux.form.BinaryMultiSelect);

  2. #2
    Sencha Premium Member ajaxvador's Avatar
    Join Date
    Nov 2007
    Location
    PARIS, FRANCE
    Posts
    206
    Vote Rating
    0
    ajaxvador is on a distinguished road

      0  

    Default


    demo ?
    Vador

  3. #3
    Ext JS Premium Member Artistan's Avatar
    Join Date
    Apr 2007
    Location
    MN
    Posts
    144
    Vote Rating
    0
    Artistan is on a distinguished road

      0  

    Default


    Temp Ext.ux.form.BinaryMultiSelect Demo
    This uses an Ext.Direct api to load and save a binary value a BinaryMultiSelect object.
    Should be able to use any type of Ext.Store to populate the select object.

  4. #4
    Sencha User
    Join Date
    Apr 2008
    Posts
    99
    Vote Rating
    0
    Bulle Bas is on a distinguished road

      0  

    Default


    Your example is broken

  5. #5
    Ext JS Premium Member Artistan's Avatar
    Join Date
    Apr 2007
    Location
    MN
    Posts
    144
    Vote Rating
    0
    Artistan is on a distinguished road

      0  

    Default


    Works fine in firefox, have not done testing in other browsers yet.
    [update]
    I added the appropriate
    Code:
    if(typeof(console) == 'undefined')
    so now everything works in IE and FF without console.

  6. #6

  7. #7
    Ext JS Premium Member Artistan's Avatar
    Join Date
    Apr 2007
    Location
    MN
    Posts
    144
    Vote Rating
    0
    Artistan is on a distinguished road

      0  

    Default


    superboxselect is excellent for multiselect, but it does not do what I need, which is to accept a single value (binary sum) to select multiple choices and have an updated single value submited back to the server for storage. Thanks for pointing out that excellent ux though!