试过了不少能找到的多选控件,包括Ext官方坛子里的SuperBox啥的,不过越炫的bug越多,改起来越麻烦。算了吧,回归简单,从彭仁X(对不住了,那个字不会念所以也打不出来,惭愧...)老师的书《Ext JS源码分析与开发实例》中扒下来了一个,虽然简单,但是够用。当然由于是例子的关系功能还不是很完善,譬如对于放在Formpanel中数据加载的时候没有处理,所以加上了这一部分,并修复了一点小bug。
代码
Ext.ns(
'
Ext.ux
'
);
Ext.ux.MultiComBox = Ext.extend(Ext.form.ComboBox, {
splitSign : ' , ' ,
selections : [],
checks : [],
hiddenValue : '' ,
lastSelectionText : '' ,
initList : function () {
var cls = ' x-combo-list ' ;
this .tpl = '
'
;
Ext.ux.MultiComBox.superclass.initList.call( this );
this .view.updateIndexes = this .updateIndexes.createDelegate( this .view);
this .view.refresh = this .refresh.createDelegate( this .view, [ this ], 0 );
if ( this .view.store) {
this .view.setStore( this .view.store, true );
}
},
refresh : function (multi) {
this .clearSelections( false , true );
this .el.update( "" );
var records = this .store.getRange();
if (records.length < 1 ) {
if ( ! this .deferEmptyText || this .hasSkippedEmptyText) {
this .el.update( this .emptyText);
}
this .hasSkippedEmptyText = true ;
this .all.clear();
return ;
}
this .tpl.overwrite( this .el, this .collectData(records, 0 ));
this .all.fill(Ext.query( this .itemSelector, this .el.dom));
multi.createCheck();
this .updateIndexes( 0 );
},
updateIndexes : function (startIndex, endIndex) {
var ns = this .all.elements;
startIndex = startIndex || 0 ;
endIndex = endIndex || ((endIndex === 0 ) ? 0 : (ns.length - 1 ));
for ( var i = startIndex; i <= endIndex; i ++ ) {
ns[i].viewIndex = i;
if (ns[i].checkbox) {
ns[i].checkbox.index = i;
}
}
},
createCheck : function () {
this .checks = [];
for ( var i = 0 ; i < this .view.all.elements.length; i ++ ) {
var el = this .view.all.elements[i];
var check = new Ext.form.Checkbox({
width : 20 ,
renderTo : Ext.getBody()
});
check.initCheckEvents = Ext.emptyFn();
var m = {
index : el.viewIndex,
check : check,
node : el
};
el.checkbox = m;
this .checks.push(m);
Ext.fly(el).select( ' .check-box ' ).insertFirst(check.wrap);
}
},
findCheckBox : function (index) {
for ( var i = 0 ; i < this .checks.length; i ++ ) {
if ( this .checks[i].index == index)
return this .checks[i];
}
return null ;
},
onSelect : function (record, index, checked) {
if ( this .fireEvent( ' beforeselect ' , this , record, index) !== false ) {
if ( ! record)
return ;
var checkObj = this .findCheckBox(index);
var checkbox = checkObj && checkObj.check;
if ( ! checkbox)
return ;
if (checked == undefined)
checked = checkbox.checked;
this .toggleCheckBox(index, checked, record, checkbox);
this .select(index, false ); // 用来设定选择样式
this .fireEvent( ' select ' , this , record, index);
}
},
toggleCheckBox : function (index, checked, r, item) {
if (checked == false ) {
item.setValue( 1 );
if ( this .isExist(index) == true )
return ;
this .selections.push({
record : r,
index : index
});
} else {
item.setValue( 0 );
for ( var i = 0 ; i < this .selections.length; i ++ ) {
if (index == this .selections[i].index)
this .selections.remove( this .selections[i]);
}
}
this .setValue(r.data[ this .valueField || this .displayField], checked);
},
isExist : function (index) {
for ( var i = 0 ; i < this .selections.length; i ++ ) {
if ( this .selections[i].index == index)
return true ;
}
return false ;
},
setValue : function (v, checked) {
var text = this .lastSelectionText;
var hiddenValue = this .hiddenValue;
var values = v.toString().split( this .splitSign);
for (i = 0 , l = values.length; i < l; i ++ ) {
var r = this .findRecord( this .valueField, values[i]);
if (r) {
var name = r.data[ this .displayField], value = r.data[ this .valueField];
var split = Ext.escapeRe( this .splitSign);
var con = Ext.escapeRe(name.toString()), val = Ext
.escapeRe(value.toString());
var nemeRe = new RegExp( " (^ " + con + " [ " + split + " ]? " + " ) "
+ " |([ " + split + " ]? " + con + " ) " , ' g ' );
var valueRe = new RegExp( " (^ " + val + " [ " + split + " ]? " + " ) "
+ " |([ " + split + " ]? " + val + " ) " , ' g ' );
if (checked == false || typeof checked == " undefined " ) {
text = text.replace(nemeRe, "" );
hiddenValue = hiddenValue.replace(valueRe, "" );
var separate = ! text ? "" : this .splitSign;
text = text + separate + name;
hiddenValue = hiddenValue + separate + value;
} else {
text = text.replace(nemeRe, "" );
hiddenValue = hiddenValue.replace(valueRe, "" );
}
}
}
this .lastSelectionText = text;
Ext.form.ComboBox.superclass.setValue.call( this , text);
this .hiddenValue = hiddenValue || this .hiddenValue;
if ( this .hiddenField) {
this .hiddenField.value = this .hiddenValue;
}
this .value = this .hiddenValue;
},
getValue : function () {
return Ext.ux.MultiComBox.superclass.getValue.call( this );
},
selectByValue : function (v, scrollIntoView) {
var value = this .getRawValue().trim() || "" ;
var v1 = value.trim().split( this .splitSign);
for ( var i = 0 ; i < v1.length; i ++ ) {
var v = v1[i];
if (v) {
var r = this .findRecord( this .displayField, v);
this .onSelect(r, this .store.indexOf(r), false );
}
}
},
getRawValue : function (flag) {
var v = this .rendered ? this .el.getValue() : Ext.value( this .value, '' );
if (v === this .emptyText) {
v = '' ;
}
if (flag != true )
return v;
var v1 = v.trim().split( this .splitSign);
if (v1.length > 0 ) {
var v2 = "" ;
for ( var i = 0 ; i < v1.length; i ++ ) {
if (v1[i])
v2 = v2 + " ( " + v1[i] + " ) " + " | " ;
}
if (v2.length - 2 > 0 )
v2 = v2.substring( 0 , v2.length - 1 );
v = new RegExp(v2);
v.length = v2.length;
}
return v;
},
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();
this .selectByValue( this .value, true );
} else {
this .selectByValue( this .value, true )
}
} else {
this .onEmptyResults();
}
},
initQuery : function () {
this .doQuery( this .getRawValue( true ));
},
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( true ));
}
this .el.focus();
}
}
});
Ext.reg( ' multiCombox ' , Ext.ux.MultiComBox);
/*
* Ext.onReady(function() { var myData = [[3300, "彭仁夔"], [3301, "李明"], [3302,
* "王华"], [3303, "张三"], [3304, "李四"], [3305, "王五"], [3306, "彭小明"], [3307, "张华"],
* [3308, "李小"]];
*
* var store1 = new Ext.data.SimpleStore( { fields : [ { name : 'value', type :
* 'string' }, { name : 'name', type : 'string' }], data : myData });
*
* var test = new Ext.ux.MultiComBox( { store : store1, displayField : 'name',
* valueField : 'value', // typeAhead : true, triggerAction : 'all', mode :
* 'local', emptyText : '请选择...', selectOnFocus : true, loadingText :
* 'loading....' }) test.render(Ext.getBody()); });
*/
Ext.ux.MultiComBox = Ext.extend(Ext.form.ComboBox, {
splitSign : ' , ' ,
selections : [],
checks : [],
hiddenValue : '' ,
lastSelectionText : '' ,
initList : function () {
var cls = ' x-combo-list ' ;
this .tpl = '
'
+ cls
+ ' -item">
+ cls
+ ' -item">
{
'
+ this .displayField + ' } |
Ext.ux.MultiComBox.superclass.initList.call( this );
this .view.updateIndexes = this .updateIndexes.createDelegate( this .view);
this .view.refresh = this .refresh.createDelegate( this .view, [ this ], 0 );
if ( this .view.store) {
this .view.setStore( this .view.store, true );
}
},
refresh : function (multi) {
this .clearSelections( false , true );
this .el.update( "" );
var records = this .store.getRange();
if (records.length < 1 ) {
if ( ! this .deferEmptyText || this .hasSkippedEmptyText) {
this .el.update( this .emptyText);
}
this .hasSkippedEmptyText = true ;
this .all.clear();
return ;
}
this .tpl.overwrite( this .el, this .collectData(records, 0 ));
this .all.fill(Ext.query( this .itemSelector, this .el.dom));
multi.createCheck();
this .updateIndexes( 0 );
},
updateIndexes : function (startIndex, endIndex) {
var ns = this .all.elements;
startIndex = startIndex || 0 ;
endIndex = endIndex || ((endIndex === 0 ) ? 0 : (ns.length - 1 ));
for ( var i = startIndex; i <= endIndex; i ++ ) {
ns[i].viewIndex = i;
if (ns[i].checkbox) {
ns[i].checkbox.index = i;
}
}
},
createCheck : function () {
this .checks = [];
for ( var i = 0 ; i < this .view.all.elements.length; i ++ ) {
var el = this .view.all.elements[i];
var check = new Ext.form.Checkbox({
width : 20 ,
renderTo : Ext.getBody()
});
check.initCheckEvents = Ext.emptyFn();
var m = {
index : el.viewIndex,
check : check,
node : el
};
el.checkbox = m;
this .checks.push(m);
Ext.fly(el).select( ' .check-box ' ).insertFirst(check.wrap);
}
},
findCheckBox : function (index) {
for ( var i = 0 ; i < this .checks.length; i ++ ) {
if ( this .checks[i].index == index)
return this .checks[i];
}
return null ;
},
onSelect : function (record, index, checked) {
if ( this .fireEvent( ' beforeselect ' , this , record, index) !== false ) {
if ( ! record)
return ;
var checkObj = this .findCheckBox(index);
var checkbox = checkObj && checkObj.check;
if ( ! checkbox)
return ;
if (checked == undefined)
checked = checkbox.checked;
this .toggleCheckBox(index, checked, record, checkbox);
this .select(index, false ); // 用来设定选择样式
this .fireEvent( ' select ' , this , record, index);
}
},
toggleCheckBox : function (index, checked, r, item) {
if (checked == false ) {
item.setValue( 1 );
if ( this .isExist(index) == true )
return ;
this .selections.push({
record : r,
index : index
});
} else {
item.setValue( 0 );
for ( var i = 0 ; i < this .selections.length; i ++ ) {
if (index == this .selections[i].index)
this .selections.remove( this .selections[i]);
}
}
this .setValue(r.data[ this .valueField || this .displayField], checked);
},
isExist : function (index) {
for ( var i = 0 ; i < this .selections.length; i ++ ) {
if ( this .selections[i].index == index)
return true ;
}
return false ;
},
setValue : function (v, checked) {
var text = this .lastSelectionText;
var hiddenValue = this .hiddenValue;
var values = v.toString().split( this .splitSign);
for (i = 0 , l = values.length; i < l; i ++ ) {
var r = this .findRecord( this .valueField, values[i]);
if (r) {
var name = r.data[ this .displayField], value = r.data[ this .valueField];
var split = Ext.escapeRe( this .splitSign);
var con = Ext.escapeRe(name.toString()), val = Ext
.escapeRe(value.toString());
var nemeRe = new RegExp( " (^ " + con + " [ " + split + " ]? " + " ) "
+ " |([ " + split + " ]? " + con + " ) " , ' g ' );
var valueRe = new RegExp( " (^ " + val + " [ " + split + " ]? " + " ) "
+ " |([ " + split + " ]? " + val + " ) " , ' g ' );
if (checked == false || typeof checked == " undefined " ) {
text = text.replace(nemeRe, "" );
hiddenValue = hiddenValue.replace(valueRe, "" );
var separate = ! text ? "" : this .splitSign;
text = text + separate + name;
hiddenValue = hiddenValue + separate + value;
} else {
text = text.replace(nemeRe, "" );
hiddenValue = hiddenValue.replace(valueRe, "" );
}
}
}
this .lastSelectionText = text;
Ext.form.ComboBox.superclass.setValue.call( this , text);
this .hiddenValue = hiddenValue || this .hiddenValue;
if ( this .hiddenField) {
this .hiddenField.value = this .hiddenValue;
}
this .value = this .hiddenValue;
},
getValue : function () {
return Ext.ux.MultiComBox.superclass.getValue.call( this );
},
selectByValue : function (v, scrollIntoView) {
var value = this .getRawValue().trim() || "" ;
var v1 = value.trim().split( this .splitSign);
for ( var i = 0 ; i < v1.length; i ++ ) {
var v = v1[i];
if (v) {
var r = this .findRecord( this .displayField, v);
this .onSelect(r, this .store.indexOf(r), false );
}
}
},
getRawValue : function (flag) {
var v = this .rendered ? this .el.getValue() : Ext.value( this .value, '' );
if (v === this .emptyText) {
v = '' ;
}
if (flag != true )
return v;
var v1 = v.trim().split( this .splitSign);
if (v1.length > 0 ) {
var v2 = "" ;
for ( var i = 0 ; i < v1.length; i ++ ) {
if (v1[i])
v2 = v2 + " ( " + v1[i] + " ) " + " | " ;
}
if (v2.length - 2 > 0 )
v2 = v2.substring( 0 , v2.length - 1 );
v = new RegExp(v2);
v.length = v2.length;
}
return v;
},
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();
this .selectByValue( this .value, true );
} else {
this .selectByValue( this .value, true )
}
} else {
this .onEmptyResults();
}
},
initQuery : function () {
this .doQuery( this .getRawValue( true ));
},
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( true ));
}
this .el.focus();
}
}
});
Ext.reg( ' multiCombox ' , Ext.ux.MultiComBox);
/*
* Ext.onReady(function() { var myData = [[3300, "彭仁夔"], [3301, "李明"], [3302,
* "王华"], [3303, "张三"], [3304, "李四"], [3305, "王五"], [3306, "彭小明"], [3307, "张华"],
* [3308, "李小"]];
*
* var store1 = new Ext.data.SimpleStore( { fields : [ { name : 'value', type :
* 'string' }, { name : 'name', type : 'string' }], data : myData });
*
* var test = new Ext.ux.MultiComBox( { store : store1, displayField : 'name',
* valueField : 'value', // typeAhead : true, triggerAction : 'all', mode :
* 'local', emptyText : '请选择...', selectOnFocus : true, loadingText :
* 'loading....' }) test.render(Ext.getBody()); });
*/
没时间详细解释...万一真有人看,有疑问可以看原书或回帖。