Ext2.0下拉多选菜单(MultiComboBox)
由于Ext2.0的ComboBox不支持菜单多选,本人花了点时间通过扩展ComboBox功能支持下拉多选功能的MultiComboBox。下面介绍MultiComboBox的使用方式,大家先看看下面的代码:
由上面代码可以看到用法大致和ComboBox一样,不相同的地方是:
由于添加了多选CheckBox图标,所以需要在 ext -all.css文件最后添加两行支持样式:
MultiComboBox的源代码:
- Ext .onReady(function(){
- var formPanel = new Ext .FormPanel({
- height : 100 , // 表单面板的高度
- width : 400 , // 表单面板的宽度
- labelWidth : 120 , // 字段标签宽度
- labelAlign : "right" , // 字段标签对齐方式
- fileUpload: true , //支持文件上传
- defaults : {// 默认form元素类型为textfield
- xtype : "textfield" , // 默认类型为textfield
- width : 150 // 默认宽度
- },
- items : [{
- xtype:'multicombo' ,
- width:250 ,
- store: new Ext .data.SimpleStore({
- fields: ["name" , "value" ],
- data:[['测试菜单1' , 1 ],[ '测试菜单2' , 2 ],[ '测试菜单3' , 3 ],[ '测试菜单4' , 4 ]]}),
- valueField :"value" ,
- displayField: "name" ,
- labelSeparator:':' ,
- displaySeparator:';' ,
- valueSeparator:',' ,
- mode: 'local' ,
- value:'1,2' ,
- forceSelection: true ,
- hiddenName:'test' ,
- editable: true ,
- triggerAction: 'all' ,
- allowBlank:false ,
- emptyText:'请选择' ,
- fieldLabel: '多选下拉ComBo'
- }],
- buttons : [{
- text : '提交' ,
- type : 'submit' ,
- handler : function() {
- }
- }]
- });
- formPanel.render("multicombo-div" );
- });
Ext
.onReady(function(){
var formPanel = new Ext
.FormPanel({
height : 100,// 表单面板的高度
width : 400,// 表单面板的宽度
labelWidth : 120,// 字段标签宽度
labelAlign : "right",// 字段标签对齐方式
fileUpload: true,//支持文件上传
defaults : {// 默认form元素类型为textfield
xtype : "textfield",// 默认类型为textfield
width : 150 // 默认宽度
},
items : [{
xtype:'multicombo',
width:250,
store: new Ext
.data.SimpleStore({
fields: ["name","value"],
data:[['测试菜单1',1],['测试菜单2',2],['测试菜单3',3],['测试菜单4',4]]}),
valueField :"value",
displayField: "name",
labelSeparator:':',
displaySeparator:';',
valueSeparator:',',
mode: 'local',
value:'1,2',
forceSelection: true,
hiddenName:'test',
editable: true,
triggerAction: 'all',
allowBlank:false,
emptyText:'请选择',
fieldLabel: '多选下拉ComBo'
}],
buttons : [{
text : '提交',
type : 'submit',
handler : function() {
}
}]
});
formPanel.render("multicombo-div");
});
由上面代码可以看到用法大致和ComboBox一样,不相同的地方是:
- xtype: 'multicombo' , //MultiComboBox注册类型名称
- displaySeparator:';' , //多选显示分隔字符
- valueSeparator:',' , //多选提交到后台的值分隔符
- value:'1,2' , // 多值通过","分隔,与valueSeparator相对应,表示默认选择了"测试菜单1'"和"测试菜单2"
xtype:'multicombo',//MultiComboBox注册类型名称
displaySeparator:';',//多选显示分隔字符
valueSeparator:',',//多选提交到后台的值分隔符
value:'1,2',// 多值通过","分隔,与valueSeparator相对应,表示默认选择了"测试菜单1'"和"测试菜单2"
由于添加了多选CheckBox图标,所以需要在 ext -all.css文件最后添加两行支持样式:
- .checked{background-image:url(../images/ default /menu/checked.gif)}
- .unchecked{background-image:url(../images/default /menu/unchecked.gif)}
.checked{background-image:url(../images/default/menu/checked.gif)}
.unchecked{background-image:url(../images/default/menu/unchecked.gif)}
MultiComboBox的源代码:
- Ext .form.MultiComboBox = Ext .extend(Ext .form.TriggerField, {
- defaultAutoCreate : {tag: "input" , type: "text" , size: "24" , autocomplete: "off" },
- listClass: '' ,
- selectedClass: 'x-combo-selected' ,
- triggerClass : 'x-form-arrow-trigger' ,
- shadow:'sides' ,
- listAlign: 'tl-bl?' ,
- maxHeight: 300 ,
- triggerAction: 'query' ,
- minChars : 4 ,
- typeAhead: false ,
- queryDelay: 500 ,
- pageSize: 0 ,
- selectOnFocus:false ,
- queryParam: 'query' ,
- loadingText: 'Loading...' ,
- resizable: false ,
- handleHeight : 8 ,
- editable: true ,
- allQuery: '' ,
- mode: 'remote' ,
- minListWidth : 70 ,
- forceSelection:false ,
- typeAheadDelay : 250 ,
- displaySeparator:';' ,
- valueSeparator:',' ,
- lazyInit : true ,
- initComponent : function(){
- Ext .form.ComboBox.superclass.initComponent.call(this );
- this .addEvents(
- 'expand' ,
- 'collapse' ,
- 'beforeselect' ,
- 'select' ,
- 'beforequery'
- );
- if ( this .transform){
- this .allowDomMove = false ;
- var s = Ext .getDom(this .transform);
- if (! this .hiddenName){
- this .hiddenName = s.name;
- }
- if (! this .store){
- this .mode = 'local' ;
- var d = [], opts = s.options;
- for (var i = 0 , len = opts.length;i < len; i++){
- var o = opts[i];
- var value = (Ext .isIE ? o.getAttributeNode('value' ).specified : o.hasAttribute( 'value' )) ? o.value : o.text;
- if (o.selected) {
- this .value = value;
- }
- d.push([value, o.text]);
- }
- this .store = new Ext .data.SimpleStore({
- 'id' : 0 ,
- fields: ['value' , 'text' ],
- data : d
- });
- this .valueField = 'value' ;
- this .displayField = 'text' ;
- }
- s.name = Ext .id(); // wipe out the name in case somewhere else they have a reference
- if (! this .lazyRender){
- this .target = true ;
- this .el = Ext .DomHelper.insertBefore(s, this .autoCreate || this .defaultAutoCreate);
- Ext .removeNode(s); // remove it
- this .render( this .el.parentNode);
- }else {
- Ext .removeNode(s); // remove it
- }
- }
- this .selectedIndex = - 1 ;
- if ( this .mode == 'local' ){
- if ( this .initialConfig.queryDelay === undefined){
- this .queryDelay = 10 ;
- }
- if ( this .initialConfig.minChars === undefined){
- this .minChars = 0 ;
- }
- }
- },
- // private
- onRender : function(ct, position){
- Ext .form.ComboBox.superclass.onRender.call(this , ct, position);
- var disValue="" ;
- if ( this .hiddenName){
- this .hiddenField = this .el.insertSibling({tag: 'input' , type: 'hidden' , name: this .hiddenName, id: ( this .hiddenId|| this .hiddenName)},
- 'before' , true );
- var hvalue=this .hiddenValue !== undefined ? this .hiddenValue :
- this .value !== undefined ? this .value : '' ;
- var hvalueArray=hvalue.split(this .valueSeparator);
- for (var i= 0 ;i< this .store.data.length;i++){
- var r = this .store.getAt(i);
- var newValue = r.data[this .displayField];
- var v=r.data[this .valueField];
- for (var j= 0 ;j<hvalueArray.length;j++){
- if (hvalueArray[j]==v){
- disValue+=newValue+this .displaySeparator;
- }
- }
- }
- this .hiddenField.value = this .hiddenValue !== undefined ? this .hiddenValue :
- this .value !== undefined ? this .value : '' ;
- this .el.dom.removeAttribute( 'name' );
- }
- if (Ext .isGecko){
- this .el.dom.setAttribute( 'autocomplete' , 'off' );
- }
- if (! this .lazyInit){
- this .initList();
- }else {
- this .on( 'focus' , this .initList, this , {single: true });
- }
- if (! this .editable){
- this .editable = true ;
- this .setEditable( false );
- }
- this .setValue(disValue);
- },
- initList : function(){
- if (! this .list){
- var cls = 'x-combo-list' ;
- this .list = new Ext .Layer({
- shadow: this .shadow, cls: [cls, this .listClass].join( ' ' ), constrain: false
- });
- var lw = this .listWidth || Math.max( this .wrap.getWidth(), this .minListWidth);
- this .list.setWidth(lw);
- this .list.swallowEvent( 'mousewheel' );
- this .assetHeight = 0 ;
- if ( this .title){
- this .header = this .list.createChild({cls:cls+ '-hd' , html: this .title});
- this .assetHeight += this .header.getHeight();
- }
- this .innerList = this .list.createChild({cls:cls+ '-inner' });
- this .innerList.on( 'mouseover' , this .onViewOver, this );
- this .innerList.on( 'mousemove' , this .onViewMove, this );
- this .innerList.setWidth(lw - this .list.getFrameWidth( 'lr' ))
- if ( this .pageSize){
- this .footer = this .list.createChild({cls:cls+ '-ft' });
- this .pageTb = new Ext .PagingToolbar({
- store:this .store,
- pageSize: this .pageSize,
- renderTo:this .footer
- });
- this .assetHeight += this .footer.getHeight();
- }
- if (! this .tpl){
- //alert(cls);
- //x-combo-list-item
- this .tpl = '<tpl for="."><div class="' +cls+ '-item"><span class="unchecked" id="checkBox_{' + this .displayField + '}"+ width="20"> </span>{' + this .displayField + '}</div></tpl>' ;
- }
- this .view = new Ext .DataView({
- applyTo: this .innerList,
- tpl: this .tpl,
- singleSelect: true ,
- selectedClass: this .selectedClass,
- itemSelector: this .itemSelector || '.' + cls + '-item'
- });
- this .view.on( 'click' , this .onViewClick, this );
- this .bindStore( this .store, true );
- if ( this .resizable){
- this .resizer = new Ext .Resizable( this .list, {
- pinned:true , handles: 'se'
- });
- this .resizer.on( 'resize' , function(r, w, h){
- this .maxHeight = h- this .handleHeight- this .list.getFrameWidth( 'tb' )- this .assetHeight;
- this .listWidth = w;
- this .innerList.setWidth(w - this .list.getFrameWidth( 'lr' ));
- this .restrictHeight();
- }, this );
- this [ this .pageSize? 'footer' : 'innerList' ].setStyle( 'margin-bottom' , this .handleHeight+ 'px' );
- }
- }
- },
- bindStore : function(store, initial){
- if ( this .store && !initial){
- this .store.un( 'beforeload' , this .onBeforeLoad, this );
- this .store.un( 'load' , this .onLoad, this );
- this .store.un( 'loadexception' , this .collapse, this );
- if (!store){
- this .store = null ;
- if ( this .view){
- this .view.setStore( null );
- }
- }
- }
- if (store){
- this .store = Ext .StoreMgr.lookup(store);
- this .store.on( 'beforeload' , this .onBeforeLoad, this );
- this .store.on( 'load' , this .onLoad, this );
- this .store.on( 'loadexception' , this .collapse, this );
- if ( this .view){
- this .view.setStore(store);
- }
- }
- },
- // private
- initEvents : function(){
- Ext .form.ComboBox.superclass.initEvents.call(this );
- this .keyNav = new Ext .KeyNav( this .el, {
- "up" : function(e){
- this .inKeyMode = true ;
- this .selectPrev();
- },
- "down" : function(e){
- if (! this .isExpanded()){
- this .onTriggerClick();
- }else {
- this .inKeyMode = true ;
- this .selectNext();
- }
- },
- "enter" : function(e){
- this .onViewClick();
- //return true;
- },
- "esc" : function(e){
- this .collapse();
- },
- "tab" : function(e){
- this .onViewClick( false );
- return true ;
- },
- scope : this ,
- doRelay : function(foo, bar, hname){
- if (hname == 'down' || this .scope.isExpanded()){
- return Ext .KeyNav.prototype.doRelay.apply( this , arguments);
- }
- return true ;
- },
- forceKeyDown : true
- });
- this .queryDelay = Math.max( this .queryDelay || 10 ,
- this .mode == 'local' ? 10 : 250 );
- this .dqTask = new Ext .util.DelayedTask( this .initQuery, this );
- if ( this .typeAhead){
- this .taTask = new Ext .util.DelayedTask( this .onTypeAhead, this );
- }
- if ( this .editable !== false ){
- this .el.on( "keyup" , this .onKeyUp, this );
- }
- if ( this .forceSelection){
- this .on( 'blur' , this .doForce, this );
- }
- },
- onDestroy : function(){
- if ( this .view){
- this .view.el.removeAllListeners();
- this .view.el.remove();
- this .view.purgeListeners();
- }
- if ( this .list){
- this .list.destroy();
- }
- this .bindStore( null );
- Ext .form.ComboBox.superclass.onDestroy.call(this );
- },
- // private
- fireKey : function(e){
- if (e.isNavKeyPress() && ! this .list.isVisible()){
- this .fireEvent( "specialkey" , this , e);
- }
- },
- // private
- onResize: function(w, h){
- Ext .form.ComboBox.superclass.onResize.apply(this , arguments);
- if ( this .list && this .listWidth === undefined){
- var lw = Math.max(w, this .minListWidth);
- this .list.setWidth(lw);
- this .innerList.setWidth(lw - this .list.getFrameWidth( 'lr' ));
- }
- },
- // private
- onDisable: function(){
- Ext .form.ComboBox.superclass.onDisable.apply(this , arguments);
- if ( this .hiddenField){
- this .hiddenField.disabled = this .disabled;
- }
- },
- setEditable : function(value){
- if (value == this .editable){
- return ;
- }
- this .editable = value;
- if (!value){
- this .el.dom.setAttribute( 'readOnly' , true );
- this .el.on( 'mousedown' , this .onTriggerClick, this );
- this .el.addClass( 'x-combo-noedit' );
- }else {
- this .el.dom.setAttribute( 'readOnly' , false );
- this .el.un( 'mousedown' , this .onTriggerClick, this );
- this .el.removeClass( 'x-combo-noedit' );
- }
- },
- // private
- onBeforeLoad : function(){
- if (! this .hasFocus){
- return ;
- }
- this .innerList.update( this .loadingText ?
- '<div class="loading-indicator">' + this .loadingText+ '</div>' : '' );
- this .restrictHeight();
- this .selectedIndex = - 1 ;
- },
- // private
- onLoad : function(){
- if (! this .hasFocus){
- return ;
- }
- if ( this .store.getCount() > 0 ){
- this .expand();
- this .restrictHeight();
- if ( this .lastQuery == this .allQuery){
- if ( this .editable){
- this .el.dom.select();
- }
- if (! this .selectByValue( this .value, true )){
- this .select( 0 , true );
- }
- }else {
- this .selectNext();
- if ( this .typeAhead && this .lastKey != Ext .EventObject.BACKSPACE && this .lastKey != Ext .EventObject.DELETE){
- this .taTask.delay( this .typeAheadDelay);
- }
- }
- }else {
- this .onEmptyResults();
- }
- },
- // private
- onTypeAhead : function(){
- if ( this .store.getCount() > 0 ){
- var r = this .store.getAt( 0 );
- var newValue = r.data[this .displayField];
- var len = newValue.length;
- var selStart = this .getRawValue().length;
- if (selStart != len){
- this .setRawValue(newValue);
- this .selectText(selStart, newValue.length);
- }
- }
- },
- // private
- onSelect : function(record, index){
- if ( this .fireEvent( 'beforeselect' , this , record, index) !== false ){
- var r = this .store.getAt(index);
- var newValue = r.data[this .displayField];
- var check=document.getElementById("checkBox_" +newValue);
- if (check.className== "checked" ){
- check.className="unchecked"
- }else {
- check.className="checked"
- }
- var value="" ;
- var hiddenValue="" ;
- for (var i= 0 ;i< this .store.data.length;i++){
- var r = this .store.getAt(i);
- newValue = r.data[this .displayField];
- check=document.getElementById("checkBox_" +newValue);
- if (check.className== "checked" ){
- value+= r.data[this .displayField]+ this .displaySeparator;
- hiddenValue+= r.data[this .valueField]+ this .valueSeparator;
- }
- }
- if (value.length> 1 ){
- value=value.substring(0 ,value.length- this .displaySeparator.length);
- }
- if (hiddenValue.length> 1 ){
- hiddenValue=hiddenValue.substring(0 ,value.length- this .valueSeparator.length);
- }
- this .setValue(value);
- this .hiddenField.value=hiddenValue;
- this .fireEvent( 'select' , this , record, index);
- }
- },
- getValue : function(){
- if ( this .valueField){
- return typeof this .value != 'undefined' ? this .value : '' ;
- }else {
- return Ext .form.ComboBox.superclass.getValue.call( this );
- }
- },
- /**
- * Clears any text/value currently set in the field
- */
- clearValue : function(){
- if ( this .hiddenField){
- this .hiddenField.value = '' ;
- }
- this .setRawValue( '' );
- this .lastSelectionText = '' ;
- this .applyEmptyText();
- },
- setValue : function(v){
- var text = v;
- if ( this .valueField){
- var r = this .findRecord( this .valueField, v);
- if (r){
- text = r.data[this .displayField];
- }else if ( this .valueNotFoundText !== undefined){
- text = this .valueNotFoundText;
- }
- }
- this .lastSelectionText = text;
- Ext .form.ComboBox.superclass.setValue.call(this , text);
- this .value = v;
- },
- // private
- findRecord : function(prop, value){
- var record;
- if ( this .store.getCount() > 0 ){
- this .store.each(function(r){
- if (r.data[prop] == value){
- record = r;
- return false ;
- }
- });
- }
- return record;
- },
- // private
- onViewMove : function(e, t){
- this .inKeyMode = false ;
- },
- // private
- onViewOver : function(e, t){
- if ( this .inKeyMode){ // prevent key nav and mouse over conflicts
- return ;
- }
- var item = this .view.findItemFromChild(t);
- if (item){
- var index = this .view.indexOf(item);
- this .select(index, false );
- }
- },
- // private
- onViewClick : function(doFocus){
- var index = this .view.getSelectedIndexes()[ 0 ];
- var r = this .store.getAt(index);
- if (r){
- this .onSelect(r, index);
- }
- if (doFocus !== false ){
- this .el.focus();
- }
- },
- // private
- restrictHeight : function(){
- this .innerList.dom.style.height = '' ;
- var inner = this .innerList.dom;
- var fw = this .list.getFrameWidth( 'tb' );
- var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
- this .innerList.setHeight(h < this .maxHeight ? 'auto' : this .maxHeight);
- this .list.beginUpdate();
- this .list.setHeight( this .innerList.getHeight()+fw+( this .resizable? this .handleHeight: 0 )+ this .assetHeight);
- this .list.alignTo( this .el, this .listAlign);
- this .list.endUpdate();
- },
- // private
- onEmptyResults : function(){
- this .collapse();
- },
- /**
- * Returns true if the dropdown list is expanded, else false.
- */
- isExpanded : function(){
- return this .list && this .list.isVisible();
- },
- selectByValue : function(v, scrollIntoView){
- if (v !== undefined && v !== null ){
- var r = this .findRecord( this .valueField || this .displayField, v);
- if (r){
- this .select( this .store.indexOf(r), scrollIntoView);
- return true ;
- }
- }
- return false ;
- },
- select : function(index, scrollIntoView){
- this .selectedIndex = index;
- this .view.select(index);
- if (scrollIntoView !== false ){
- var el = this .view.getNode(index);
- if (el){
- this .innerList.scrollChildIntoView(el, false );
- }
- }
- },
- // private
- selectNext : function(){
- var ct = this .store.getCount();
- if (ct > 0 ){
- if ( this .selectedIndex == - 1 ){
- this .select( 0 );
- }else if ( this .selectedIndex < ct- 1 ){
- this .select( this .selectedIndex+ 1 );
- }
- }
- },
- // private
- selectPrev : function(){
- var ct = this .store.getCount();
- if (ct > 0 ){
- if ( this .selectedIndex == - 1 ){
- this .select( 0 );
- }else if ( this .selectedIndex != 0 ){
- this .select( this .selectedIndex- 1 );
- }
- }
- },
- // private
- onKeyUp : function(e){
- if ( this .editable !== false && !e.isSpecialKey()){
- this .lastKey = e.getKey();
- this .dqTask.delay( this .queryDelay);
- }
- },
- // private
- validateBlur : function(){
- return ! this .list || ! this .list.isVisible();
- },
- // private
- initQuery : function(){
- this .doQuery( this .getRawValue());
- },
- // private
- doForce : function(){
- if ( this .el.dom.value.length > 0 ){
- this .el.dom.value =
- this .lastSelectionText === undefined ? '' : this .lastSelectionText;
- this .applyEmptyText();
- }
- },
- doQuery : function(q, forceAll){
- if (q === undefined || q === null ){
- q = '' ;
- }
- var qe = {
- query: q,
- forceAll: forceAll,
- combo: this ,
- cancel:false
- };
- if ( this .fireEvent( 'beforequery' , qe)=== false || qe.cancel){
- return false ;
- }
- q = qe.query;
- forceAll = qe.forceAll;
- if (forceAll === true || (q.length >= this .minChars)){
- if ( this .lastQuery !== q){
- this .lastQuery = q;
- if ( this .mode == 'local' ){
- this .selectedIndex = - 1 ;
- if (forceAll){
- this .store.clearFilter();
- }else {
- this .store.filter( this .displayField, q);
- }
- this .onLoad();
- }else {
- this .store.baseParams[ this .queryParam] = q;
- this .store.load({
- params: this .getParams(q)
- });
- this .expand();
- }
- }else {
- this .selectedIndex = - 1 ;
- this .onLoad();
- }
- }
- },
- // private
- getParams : function(q){
- var p = {};
- //p[this.queryParam] = q;
- if ( this .pageSize){
- p.start = 0 ;
- p.limit = this .pageSize;
- }
- return p;
- },
- /**
- * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
- */
- collapse : function(){
- if (! this .isExpanded()){
- return ;
- }
- this .list.hide();
- Ext .getDoc().un('mousewheel' , this .collapseIf, this );
- Ext .getDoc().un('mousedown' , this .collapseIf, this );
- this .fireEvent( 'collapse' , this );
- },
- // private
- collapseIf : function(e){
- if (!e.within( this .wrap) && !e.within( this .list)){
- this .collapse();
- }
- },
- /**
- * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
- */
- expand : function(){
- if ( this .isExpanded() || ! this .hasFocus){
- return ;
- }
- this .list.alignTo( this .wrap, this .listAlign);
- var hvalueArray=this .hiddenField.value.split( this .valueSeparator);
- for (var i= 0 ;i< this .store.data.length;i++){
- var r = this .store.getAt(i);
- var newValue = r.data[this .displayField];
- var v=r.data[this .valueField];
- for (var j= 0 ;j<hvalueArray.length;j++){
- if (hvalueArray[j]==v){
- document.getElementById("checkBox_" +newValue).className= "checked" ;
- }
- }
- }
- this .list.show();
- Ext .getDoc().on('mousewheel' , this .collapseIf, this );
- Ext .getDoc().on('mousedown' , this .collapseIf, this );
- this .fireEvent( 'expand' , this );
- },
- // private
- // Implements the default empty TriggerField.onTriggerClick function
- onTriggerClick : function(){
- if ( this .disabled){
- return ;
- }
- if ( this .isExpanded()){
- this .collapse();
- this .el.focus();
- }else {
- this .onFocus({});
- if ( this .triggerAction == 'all' ) {
- this .doQuery( this .allQuery, true );
- } else {
- this .doQuery( this .getRawValue());
- }
- this .el.focus();
- }
- }
- });
- Ext .reg('multicombo' , Ext .form.MultiComboBox);
Ext
.form.MultiComboBox = Ext
.extend(Ext
.form.TriggerField, {
defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
listClass: '',
selectedClass: 'x-combo-selected',
triggerClass : 'x-form-arrow-trigger',
shadow:'sides',
listAlign: 'tl-bl?',
maxHeight: 300,
triggerAction: 'query',
minChars : 4,
typeAhead: false,
queryDelay: 500,
pageSize: 0,
selectOnFocus:false,
queryParam: 'query',
loadingText: 'Loading...',
resizable: false,
handleHeight : 8,
editable: true,
allQuery: '',
mode: 'remote',
minListWidth : 70,
forceSelection:false,
typeAheadDelay : 250,
displaySeparator:';',
valueSeparator:',',
lazyInit : true,
initComponent : function(){
Ext
.form.ComboBox.superclass.initComponent.call(this);
this.addEvents(
'expand',
'collapse',
'beforeselect',
'select',
'beforequery'
);
if(this.transform){
this.allowDomMove = false;
var s = Ext
.getDom(this.transform);
if(!this.hiddenName){
this.hiddenName = s.name;
}
if(!this.store){
this.mode = 'local';
var d = [], opts = s.options;
for(var i = 0, len = opts.length;i < len; i++){
var o = opts[i];
var value = (Ext
.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
if(o.selected) {
this.value = value;
}
d.push([value, o.text]);
}
this.store = new Ext
.data.SimpleStore({
'id': 0,
fields: ['value', 'text'],
data : d
});
this.valueField = 'value';
this.displayField = 'text';
}
s.name = Ext
.id(); // wipe out the name in case somewhere else they have a reference
if(!this.lazyRender){
this.target = true;
this.el = Ext
.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
Ext
.removeNode(s); // remove it
this.render(this.el.parentNode);
}else{
Ext
.removeNode(s); // remove it
}
}
this.selectedIndex = -1;
if(this.mode == 'local'){
if(this.initialConfig.queryDelay === undefined){
this.queryDelay = 10;
}
if(this.initialConfig.minChars === undefined){
this.minChars = 0;
}
}
},
// private
onRender : function(ct, position){
Ext
.form.ComboBox.superclass.onRender.call(this, ct, position);
var disValue="";
if(this.hiddenName){
this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
'before', true);
var hvalue=this.hiddenValue !== undefined ? this.hiddenValue :
this.value !== undefined ? this.value : '';
var hvalueArray=hvalue.split(this.valueSeparator);
for(var i=0;i<this.store.data.length;i++){
var r = this.store.getAt(i);
var newValue = r.data[this.displayField];
var v=r.data[this.valueField];
for(var j=0;j<hvalueArray.length;j++){
if(hvalueArray[j]==v){
disValue+=newValue+this.displaySeparator;
}
}
}
this.hiddenField.value =this.hiddenValue !== undefined ? this.hiddenValue :
this.value !== undefined ? this.value : '';
this.el.dom.removeAttribute('name');
}
if(Ext
.isGecko){
this.el.dom.setAttribute('autocomplete', 'off');
}
if(!this.lazyInit){
this.initList();
}else{
this.on('focus', this.initList, this, {single: true});
}
if(!this.editable){
this.editable = true;
this.setEditable(false);
}
this.setValue(disValue);
},
initList : function(){
if(!this.list){
var cls = 'x-combo-list';
this.list = new Ext
.Layer({
shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
});
var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
this.list.setWidth(lw);
this.list.swallowEvent('mousewheel');
this.assetHeight = 0;
if(this.title){
this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
this.assetHeight += this.header.getHeight();
}
this.innerList = this.list.createChild({cls:cls+'-inner'});
this.innerList.on('mouseover', this.onViewOver, this);
this.innerList.on('mousemove', this.onViewMove, this);
this.innerList.setWidth(lw - this.list.getFrameWidth('lr'))
if(this.pageSize){
this.footer = this.list.createChild({cls:cls+'-ft'});
this.pageTb = new Ext
.PagingToolbar({
store:this.store,
pageSize: this.pageSize,
renderTo:this.footer
});
this.assetHeight += this.footer.getHeight();
}
if(!this.tpl){
//alert(cls);
//x-combo-list-item
this.tpl = '<tpl for="."><div class="'+cls+'-item"><span class="unchecked" id="checkBox_{' + this.displayField + '}"+ width="20"> </span>{' + this.displayField + '}</div></tpl>';
}
this.view = new Ext
.DataView({
applyTo: this.innerList,
tpl: this.tpl,
singleSelect: true,
selectedClass: this.selectedClass,
itemSelector: this.itemSelector || '.' + cls + '-item'
});
this.view.on('click', this.onViewClick, this);
this.bindStore(this.store, true);
if(this.resizable){
this.resizer = new Ext
.Resizable(this.list, {
pinned:true, handles:'se'
});
this.resizer.on('resize', function(r, w, h){
this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
this.listWidth = w;
this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
this.restrictHeight();
}, this);
this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
}
}
},
bindStore : function(store, initial){
if(this.store && !initial){
this.store.un('beforeload', this.onBeforeLoad, this);
this.store.un('load', this.onLoad, this);
this.store.un('loadexception', this.collapse, this);
if(!store){
this.store = null;
if(this.view){
this.view.setStore(null);
}
}
}
if(store){
this.store = Ext
.StoreMgr.lookup(store);
this.store.on('beforeload', this.onBeforeLoad, this);
this.store.on('load', this.onLoad, this);
this.store.on('loadexception', this.collapse, this);
if(this.view){
this.view.setStore(store);
}
}
},
// private
initEvents : function(){
Ext
.form.ComboBox.superclass.initEvents.call(this);
this.keyNav = new Ext
.KeyNav(this.el, {
"up" : function(e){
this.inKeyMode = true;
this.selectPrev();
},
"down" : function(e){
if(!this.isExpanded()){
this.onTriggerClick();
}else{
this.inKeyMode = true;
this.selectNext();
}
},
"enter" : function(e){
this.onViewClick();
//return true;
},
"esc" : function(e){
this.collapse();
},
"tab" : function(e){
this.onViewClick(false);
return true;
},
scope : this,
doRelay : function(foo, bar, hname){
if(hname == 'down' || this.scope.isExpanded()){
return Ext
.KeyNav.prototype.doRelay.apply(this, arguments);
}
return true;
},
forceKeyDown : true
});
this.queryDelay = Math.max(this.queryDelay || 10,
this.mode == 'local' ? 10 : 250);
this.dqTask = new Ext
.util.DelayedTask(this.initQuery, this);
if(this.typeAhead){
this.taTask = new Ext
.util.DelayedTask(this.onTypeAhead, this);
}
if(this.editable !== false){
this.el.on("keyup", this.onKeyUp, this);
}
if(this.forceSelection){
this.on('blur', this.doForce, this);
}
},
onDestroy : function(){
if(this.view){
this.view.el.removeAllListeners();
this.view.el.remove();
this.view.purgeListeners();
}
if(this.list){
this.list.destroy();
}
this.bindStore(null);
Ext
.form.ComboBox.superclass.onDestroy.call(this);
},
// private
fireKey : function(e){
if(e.isNavKeyPress() && !this.list.isVisible()){
this.fireEvent("specialkey", this, e);
}
},
// private
onResize: function(w, h){
Ext
.form.ComboBox.superclass.onResize.apply(this, arguments);
if(this.list && this.listWidth === undefined){
var lw = Math.max(w, this.minListWidth);
this.list.setWidth(lw);
this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
}
},
// private
onDisable: function(){
Ext
.form.ComboBox.superclass.onDisable.apply(this, arguments);
if(this.hiddenField){
this.hiddenField.disabled = this.disabled;
}
},
setEditable : function(value){
if(value == this.editable){
return;
}
this.editable = value;
if(!value){
this.el.dom.setAttribute('readOnly', true);
this.el.on('mousedown', this.onTriggerClick, this);
this.el.addClass('x-combo-noedit');
}else{
this.el.dom.setAttribute('readOnly', false);
this.el.un('mousedown', this.onTriggerClick, this);
this.el.removeClass('x-combo-noedit');
}
},
// private
onBeforeLoad : function(){
if(!this.hasFocus){
return;
}
this.innerList.update(this.loadingText ?
'<div class="loading-indicator">'+this.loadingText+'</div>' : '');
this.restrictHeight();
this.selectedIndex = -1;
},
// private
onLoad : function(){
if(!this.hasFocus){
return;
}
if(this.store.getCount() > 0){
this.expand();
this.restrictHeight();
if(this.lastQuery == this.allQuery){
if(this.editable){
this.el.dom.select();
}
if(!this.selectByValue(this.value, true)){
this.select(0, true);
}
}else{
this.selectNext();
if(this.typeAhead && this.lastKey != Ext
.EventObject.BACKSPACE && this.lastKey != Ext
.EventObject.DELETE){
this.taTask.delay(this.typeAheadDelay);
}
}
}else{
this.onEmptyResults();
}
},
// private
onTypeAhead : function(){
if(this.store.getCount() > 0){
var r = this.store.getAt(0);
var newValue = r.data[this.displayField];
var len = newValue.length;
var selStart = this.getRawValue().length;
if(selStart != len){
this.setRawValue(newValue);
this.selectText(selStart, newValue.length);
}
}
},
// private
onSelect : function(record, index){
if(this.fireEvent('beforeselect', this, record, index) !== false){
var r = this.store.getAt(index);
var newValue = r.data[this.displayField];
var check=document.getElementById("checkBox_"+newValue);
if(check.className=="checked"){
check.className="unchecked"
}else{
check.className="checked"
}
var value="";
var hiddenValue="";
for(var i=0;i<this.store.data.length;i++){
var r = this.store.getAt(i);
newValue = r.data[this.displayField];
check=document.getElementById("checkBox_"+newValue);
if(check.className=="checked"){
value+= r.data[this.displayField]+this.displaySeparator;
hiddenValue+= r.data[this.valueField]+this.valueSeparator;
}
}
if(value.length>1){
value=value.substring(0,value.length-this.displaySeparator.length);
}
if(hiddenValue.length>1){
hiddenValue=hiddenValue.substring(0,value.length-this.valueSeparator.length);
}
this.setValue(value);
this.hiddenField.value=hiddenValue;
this.fireEvent('select', this, record, index);
}
},
getValue : function(){
if(this.valueField){
return typeof this.value != 'undefined' ? this.value : '';
}else{
return Ext
.form.ComboBox.superclass.getValue.call(this);
}
},
/**
* Clears any text/value currently set in the field
*/
clearValue : function(){
if(this.hiddenField){
this.hiddenField.value = '';
}
this.setRawValue('');
this.lastSelectionText = '';
this.applyEmptyText();
},
setValue : function(v){
var text = v;
if(this.valueField){
var r = this.findRecord(this.valueField, v);
if(r){
text = r.data[this.displayField];
}else if(this.valueNotFoundText !== undefined){
text = this.valueNotFoundText;
}
}
this.lastSelectionText = text;
Ext
.form.ComboBox.superclass.setValue.call(this, text);
this.value = v;
},
// private
findRecord : function(prop, value){
var record;
if(this.store.getCount() > 0){
this.store.each(function(r){
if(r.data[prop] == value){
record = r;
return false;
}
});
}
return record;
},
// private
onViewMove : function(e, t){
this.inKeyMode = false;
},
// private
onViewOver : function(e, t){
if(this.inKeyMode){ // prevent key nav and mouse over conflicts
return;
}
var item = this.view.findItemFromChild(t);
if(item){
var index = this.view.indexOf(item);
this.select(index, false);
}
},
// private
onViewClick : function(doFocus){
var index = this.view.getSelectedIndexes()[0];
var r = this.store.getAt(index);
if(r){
this.onSelect(r, index);
}
if(doFocus !== false){
this.el.focus();
}
},
// private
restrictHeight : function(){
this.innerList.dom.style.height = '';
var inner = this.innerList.dom;
var fw = this.list.getFrameWidth('tb');
var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
this.list.beginUpdate();
this.list.setHeight(this.innerList.getHeight()+fw+(this.resizable?this.handleHeight:0)+this.assetHeight);
this.list.alignTo(this.el, this.listAlign);
this.list.endUpdate();
},
// private
onEmptyResults : function(){
this.collapse();
},
/**
* Returns true if the dropdown list is expanded, else false.
*/
isExpanded : function(){
return this.list && this.list.isVisible();
},
selectByValue : function(v, scrollIntoView){
if(v !== undefined && v !== null){
var r = this.findRecord(this.valueField || this.displayField, v);
if(r){
this.select(this.store.indexOf(r), scrollIntoView);
return true;
}
}
return false;
},
select : function(index, scrollIntoView){
this.selectedIndex = index;
this.view.select(index);
if(scrollIntoView !== false){
var el = this.view.getNode(index);
if(el){
this.innerList.scrollChildIntoView(el, false);
}
}
},
// private
selectNext : function(){
var ct = this.store.getCount();
if(ct > 0){
if(this.selectedIndex == -1){
this.select(0);
}else if(this.selectedIndex < ct-1){
this.select(this.selectedIndex+1);
}
}
},
// private
selectPrev : function(){
var ct = this.store.getCount();
if(ct > 0){
if(this.selectedIndex == -1){
this.select(0);
}else if(this.selectedIndex != 0){
this.select(this.selectedIndex-1);
}
}
},
// private
onKeyUp : function(e){
if(this.editable !== false && !e.isSpecialKey()){
this.lastKey = e.getKey();
this.dqTask.delay(this.queryDelay);
}
},
// private
validateBlur : function(){
return !this.list || !this.list.isVisible();
},
// private
initQuery : function(){
this.doQuery(this.getRawValue());
},
// private
doForce : function(){
if(this.el.dom.value.length > 0){
this.el.dom.value =
this.lastSelectionText === undefined ? '' : this.lastSelectionText;
this.applyEmptyText();
}
},
doQuery : function(q, forceAll){
if(q === undefined || q === null){
q = '';
}
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars)){
if(this.lastQuery !== q){
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
this.store.filter(this.displayField, q);
}
this.onLoad();
}else{
this.store.baseParams[this.queryParam] = q;
this.store.load({
params: this.getParams(q)
});
this.expand();
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
},
// private
getParams : function(q){
var p = {};
//p[this.queryParam] = q;
if(this.pageSize){
p.start = 0;
p.limit = this.pageSize;
}
return p;
},
/**
* Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
*/
collapse : function(){
if(!this.isExpanded()){
return;
}
this.list.hide();
Ext
.getDoc().un('mousewheel', this.collapseIf, this);
Ext
.getDoc().un('mousedown', this.collapseIf, this);
this.fireEvent('collapse', this);
},
// private
collapseIf : function(e){
if(!e.within(this.wrap) && !e.within(this.list)){
this.collapse();
}
},
/**
* Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
*/
expand : function(){
if(this.isExpanded() || !this.hasFocus){
return;
}
this.list.alignTo(this.wrap, this.listAlign);
var hvalueArray=this.hiddenField.value.split(this.valueSeparator);
for(var i=0;i<this.store.data.length;i++){
var r = this.store.getAt(i);
var newValue = r.data[this.displayField];
var v=r.data[this.valueField];
for(var j=0;j<hvalueArray.length;j++){
if(hvalueArray[j]==v){
document.getElementById("checkBox_"+newValue).className="checked";
}
}
}
this.list.show();
Ext
.getDoc().on('mousewheel', this.collapseIf, this);
Ext
.getDoc().on('mousedown', this.collapseIf, this);
this.fireEvent('expand', this);
},
// private
// Implements the default empty TriggerField.onTriggerClick function
onTriggerClick : function(){
if(this.disabled){
return;
}
if(this.isExpanded()){
this.collapse();
this.el.focus();
}else {
this.onFocus({});
if(this.triggerAction == 'all') {
this.doQuery(this.allQuery, true);
} else {
this.doQuery(this.getRawValue());
}
this.el.focus();
}
}
});
Ext
.reg('multicombo', Ext
.form.MultiComboBox);
<script type="text/javascript"><!-- google_ad_client = "pub-4348265167276910"; /* 468x60, 个人博客 */ google_ad_slot = "2046406163"; google_ad_width = 468; google_ad_height = 60; //--> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/expansion_embed.js"></script> <script src="http://googleads.g.doubleclick.net/pagead/test_domain.js"></script> <script>google_protectAndRun("ads_core.google_render_ad", google_handleError, google_render_ad);</script>
前面已提到:
由于添加了多选CheckBox图标,所以需要在ext -all.css文件最后添加两行支持样式:
- .checked{background-image:url(../images/ default /menu/checked.gif)}
- .unchecked{background-image:url(../images/default /menu/unchecked.gif)}