[Ext JS 4] 实战之多选下拉单 (带checkbox) 续 - 带ALL 选项

前言

[Ext JS 4] 实战之多选下拉单 (带checkbox)

这一篇中有介绍如何开发带有checkbox 的多选菜单。

但是实际项目开发过程中, 用户的需求也是不断精进的。

使用淘宝或是其他网站购物车功能的用户对全选就特别习惯, 所以他们也希望在下拉单中也能有  "ALL" 这样的选项。

但是Extjs 本身提供的多选下拉单,功能比较有限。

之前有扩充过带 checkbox, 现在又要多扩充一个 "ALL" 选项了。

要求是:

1. 选中"ALL", 其他选项要自动勾选

2. 反选"ALL", 其他选项要自动反选

3. 所有非"ALL"选项都选了, 要自动把 "ALL"勾选

3. 有一个非"ALL"选项选项反选了, 要自动反选 "ALL"


思考思路

要扩充,首先得要有一个思路。

思路1 : 在store 中加入一个ALL 的数据,

很简单的一个store

	var store1 = Ext.create('Ext.data.Store', {
	    fields: ['abbr', 'name'],
	    data : [
	        {"abbr":"ALL", "name":"All"},    
	        {"abbr":"AL", "name":"Alabama"},
	        {"abbr":"AK", "name":"Alaska"},
	        {"abbr":"AZ", "name":"Arizona"}
	    ]

这样的确有一个 ALL 选项, 但是这个ALL 选项和其他选项的作用是一样的。

但是 "ALL" 本身的意义有不一样, 无法达到上面提到的要求。


思路2: 能否在下拉单中增加一个选项, 但是这个选项又和一般的选项不一样, 也就是说它不属于Extjs 的combobox 的选项, 只是添加一个 html 的input 元素, 设置他的onclik 事件, 并与其他的选项进行联动。

Extjs combox 的结构基本上是

1. 显示的input 框和 trigger 的按钮

2. BoundList , 类似于grid 的view , 用来显示下拉单。(可以想象成下拉单是只有一个栏位的 grid)

BoundList 的配置可以在combobox 的listConfig 配置选项中进行配置, 这样的话, 就可以配置显示的template 了

配置类似:

 tpl:	'<div class="mt-boundlist-item" onclick="clickAllOptionDiv(\''+thisid+'\')"><input onclick="clickAllOptionInput(this)" type="checkbox" id="'+allOptId+'">ALL</div>
<tpl for="."><div class="x-boundlist-item"><input type=checkbox>{'+this.displayField+'}</div></tpl>',

这里配置了整个BoundList的显示格式:

1. 有一个 ALL 选项, 并有相应的 onclick 时间

2. 配置了每个选项的显示效果(带有一个checkbox)

显示部分解决之后, 接下来就是, 联动的处理,

在BoundList 的 onItemSelect 和 onItemDeselect 去处理 ALL 的选项是否要选取。

需要注意的是,

1.因为这里有给ALL  选项的id 为 combobox 的id 加上一些字符, 所以在创建combobox 的时候, 需要指定id

.2. ALL选项的div class 设置成 mt-boundlist-item 目的是为了和其他选项的显示效果一致。

这个Class 的定义如下:

<style>
.mt-boundlist-item {
  padding: 0 6px;
  line-height: 22px;
  cursor: pointer;
  cursor: hand;
  position: relative;
  /*allow hover in IE on empty items*/
  zoom: 1;
  border-width: 1px;
  border-style: dotted;
  border-color: white;
}
</style>
<script>

这个内容和Extjs 本身的 x-boundlist-Item是一样的, 只是ALL选项的Class 不能用那个Class(因为用了, 就会被当成一般的选项)

代码

/*********************************
 * @author: Oscar999
 * @Description: New Widgets Extend from Ext
 * @verion: V1.0
 **********************************/

/**
 * begin for multi Select combobox
 */
Ext.define('Ext.ux.MultiComboBox', {
    extend: 'Ext.form.ComboBox',
    alias: 'widget.multicombobox',
    xtype: 'multicombobox',
    validate:function() {
	    var errs =[]; 
	    var val = this.getRawValue();
	    if (this.store.data && this.store.data.items.length>0){
	        if (this.xtype=="multicombobox"){
	        	var ssRawValues=[];
	        	if (val){
		        	if ((this.id==="projectList")||val.indexOf(", ")>0){
		        		ssRawValues=val.split(", ");
		        	}
		        	else{
		        		ssRawValues=val.split(",");
		        	}
	        	}
	        	for(var ii=0;ii<ssRawValues.length;ii++){
	        		var selectedValue=ssRawValues[ii];
	        		if (ssRawValues[ii].trim){
	        			selectedValue=ssRawValues[ii].trim();
	        		}else if (trim){
	        			selectedValue=trim(selectedValue);
	        		}
		        	var rec = this.findRecord(this.displayField, selectedValue);
		        	if(!rec)
		                errs.push("Invalid Selection ["+ ssRawValues[ii]+"]"); 
	        	}
	        	
	        }
	        else{
	        	var rec = this.findRecord(this.displayField, val);
	        	if(!rec)
	                errs.push("Invalid Selection"); 
	        }
	    }
	    if(errs && errs.length > 0)
		{
			var error = errs[0];
			this.markInvalid(error);
	        return false;
		}else if(!this.allowBlank && !val){
			this.markInvalid(this.getErrors());
			return false;
		}    
		else{
			this.clearInvalid();
	        return true;
		} 
    },
    clearValue:function(){   	
   	 //var coboxhtml = this.getEl().getHTML();
   	 try{
   	     var coboxhtml = this.getEl().dom;
   	   	 if(coboxhtml!=null)
   	     {	    		
   	   		 var checkboxs = this.picker.listEl.el.dom.children;
   	   		 if(checkboxs!=null)
   	   		 {
   	       		 for(var i=0;i<checkboxs.length;i++)
   	           	 {
   	               	 var checkbox = checkboxs[i];
   	               	 
   	               	 checkbox.children[0].checked = false;
   	           	 }
   	   		 }
   	      } 
   	 }catch(e)
   	 {
   		if(typeof(console)!="undefined"&&console!=null)
   		{
   			console.log(e.toString());
   		}
   	 }
      this.setValue([]);
   },
    initComponent: function(){
    	var valueField = this.valueField;
    	this.multiSelect = true;
    	var me = this;   
    	var thisid = this.id;
    	var allOptId = thisid+"_allOpt";
        this.allOptId = allOptId;
    	this.listConfig = {   	 
    	     tpl:	'<div class="mt-boundlist-item" onclick="clickAllOptionDiv(\''+thisid+'\')"><input onclick="clickAllOptionInput(this)" type="checkbox" id="'+allOptId+'">ALL</div><tpl for="."><div class="x-boundlist-item"><input type=checkbox>{'+this.displayField+'}</div></tpl>',
	  	     onItemSelect: function(record) {    
	  	    	  var node = this.getNode(record);
    	          if (node) {
    	             Ext.fly(node).addCls(this.selectedItemCls);
    	             
    	             var checkboxs = node.getElementsByTagName("input");
    	             if(checkboxs!=null)
    	             {
    	            	 var checkbox = checkboxs[0];
  	    				 checkbox.checked = true;
    	             }
    	          }
    			 var isAllSelected = true;
    			 var store = this.getStore();
    			 if(store!=null&&store.getTotalCount()>0)
    			 {
  	    			 for(var i=0;i<store.getTotalCount();i++)
  	    			 {
  	    				 var recordTemp = store.getAt(i);
  	    				 var itemTemp = this.getNode(recordTemp);
  	    				 var isSelectedTemp = this.isSelected(itemTemp);
  	    				 if(!isSelectedTemp){
   	    					isAllSelected = false; 	  
  	    					break; 					
  	    				 }
  	    			 }
    			 }else{
    				isAllSelected = false;
    			 }

    			 if(isAllSelected)
    			 {
    				me.selectAllOpt();
    			 }    	            	             	                 	              	             	          
	  	      },
	  	    onItemDeselect: function(record) {
	  	    	var node = this.getNode(record);
  	            if (node) {
  	            	 Ext.fly(node).removeCls(this.selectedItemCls);
  	             
  	             var checkboxs = node.getElementsByTagName("input");
  	             if(checkboxs!=null)
  	             {
  	            	 var checkbox = checkboxs[0];
	    				 checkbox.checked = false;
	    			 me.deselectAllOpt();
	    				 
  	             }
  	            }
	  	    },
	  	      listeners:{
  	    		  itemclick:function(view, record, item, index, e, eOpts ){
  	    			  var isSelected = view.isSelected(item);
  	    			  var checkboxs = item.getElementsByTagName("input");
  	    			  if(checkboxs!=null)
  	    			  {
  	    				  var checkbox = checkboxs[0];
  	    				  if(!isSelected)
  	    				  {
  	    					  checkbox.checked = true;
  	    				  }else{
  	    					  checkbox.checked = false;
  	    				  }
  	    			  }  	    			   	    			 
  	    		  }
	  	      }    	  
	  	}   	
    	this.callParent();
    },
    
    deselectAllOpt:function()
    {
    	var allOptInput = document.getElementById(this.allOptId);
    	if(allOptInput!=null)
    	{
    		allOptInput.checked =false;
    	}
    },
    selectAllOpt:function()
    {
    	var allOptInput = document.getElementById(this.allOptId);
    	if(allOptInput!=null)
    	{
    		allOptInput.checked =true;
    	}
    }    
    
    
    
});

function clickAllOptionDiv(comboxId)
{	
	if(comboxId!=null&&comboxId.length>0)
	{
		var allOptInputId = comboxId+"_allOpt";
		var allOptInput = document.getElementById(allOptInputId);
		clickAllOptionInput(allOptInput);		
	}
}

function clickAllOptionInput(allOptInput)
{
	if(allOptInput!=null)
	{
		var allOptInputId = allOptInput.id;
		var allOptInputIdArray = allOptInputId.split("_allOpt");
		var comboxId = allOptInputIdArray[0];
		var isChecked = allOptInput.checked;
		var combobox = Ext.getCmp(comboxId);
		var boundList = combobox.getPicker(); 
		if(boundList!=null)
		{	
			var selModel = boundList.getSelectionModel();
			selModel.deselectOnContainerClick=false;
		}
		var allValueArray = getAllStoreValueArryByAtt(combobox.getStore(),combobox.valueField);
		if(isChecked)
		{
			allOptInput.checked=false;			
			if(combobox!=null)
			{
				combobox.setValue([]);
			}
		}else{
			allOptInput.checked=true;
			if(combobox!=null)
			{
				combobox.setValue(allValueArray);									
			}
		}
	}	
}


function getAllStoreValueArryByAtt(store,key)
{
	var valueArray = [];
	if(store!=null)
	{
		for(var i=0;i<store.getTotalCount();i++)
		{
			var record = store.getAt(i);
			if(record.get(key)!=null)
			{
				valueArray.push(record.get(key));
			}
		}
	}
	return valueArray;
}


/**
 * end for multi Select combobox
 */

贴一下全部的代码。

调用的方式如下:

	var store1 = Ext.create('Ext.data.Store', {
	    fields: ['abbr', 'name'],
	    data : [
	        //{"abbr":"ALL", "name":"All"},    
	        {"abbr":"AL", "name":"Alabama"},
	        {"abbr":"AK", "name":"Alaska"},
	        {"abbr":"AZ", "name":"Arizona"}
	        //...
	    ]
	});
	
	Ext.create('Ext.ux.MultiComboBox',{
		fieldLabel:'Multi Combox 1',
		id:'nulticombox1', //must
		renderTo:'combox',
		//queryMode:'local',
		width:600,
		displayField:'name',
		valueField:'abbr',
		value:['AL'],
		store:store1
	});

实现的效果如下:



继续

其实,问题到以上基本上以及介绍了。

但是正如上面看到的。

有的用户认为, 全选的话, 应该显示成ALL,而不是其他的所有选项。

这样的需求也算合理, 改进一般, 直接上源码

/*********************************
 * @author: Oscar999
 * @Description: New Widgets Extend from Ext
 * @verion: V1.0
 **********************************/

/**
 * begin for multi Select combobox
 */
Ext.define('Ext.ux.MultiComboBox', {
    extend: 'Ext.form.ComboBox',
    alias: 'widget.multicombobox',
    xtype: 'multicombobox',
    validate:function() {
	    var errs =[]; 
	    var val = this.getRawValue();
	    if (this.store.data && this.store.data.items.length>0){
	        if (this.xtype=="multicombobox"){
	        	var ssRawValues=[];
	        	if (val){
		        	if ((this.id==="projectList")||val.indexOf(", ")>0){
		        		ssRawValues=val.split(", ");
		        	}
		        	else{
		        		ssRawValues=val.split(",");
		        	}
	        	}
	        	for(var ii=0;ii<ssRawValues.length;ii++){
	        		var selectedValue=ssRawValues[ii];
	        		if (ssRawValues[ii].trim){
	        			selectedValue=ssRawValues[ii].trim();
	        		}else if (trim){
	        			selectedValue=trim(selectedValue);
	        		}
	        		if(selectedValue!="ALL")
	        		{
			        	var rec = this.findRecord(this.displayField, selectedValue);
			        	if(!rec)
			                errs.push("Invalid Selection ["+ ssRawValues[ii]+"]"); 
	        		}
	        	}
	        	
	        }
	        else{
	        	var rec = this.findRecord(this.displayField, val);
	        	if(!rec)
	                errs.push("Invalid Selection"); 
	        }
	    }
	    if(errs && errs.length > 0)
		{
			var error = errs[0];
			this.markInvalid(error);
	        return false;
		}else if(!this.allowBlank && !val){
			this.markInvalid(this.getErrors());
			return false;
		}    
		else{
			this.clearInvalid();
	        return true;
		} 
    },
    clearValue:function(){   	
   	 //var coboxhtml = this.getEl().getHTML();
   	 try{
   	     var coboxhtml = this.getEl().dom;
   	   	 if(coboxhtml!=null)
   	     {	    		
   	   		 var checkboxs = this.picker.listEl.el.dom.children;
   	   		 if(checkboxs!=null)
   	   		 {
   	       		 for(var i=0;i<checkboxs.length;i++)
   	           	 {
   	               	 var checkbox = checkboxs[i];
   	               	 
   	               	 checkbox.children[0].checked = false;
   	           	 }
   	   		 }
   	      } 
   	 }catch(e)
   	 {
   		if(typeof(console)!="undefined"&&console!=null)
   		{
   			console.log(e.toString());
   		}
   	 }
      this.setValue([]);
   },
    initComponent: function(){
    	var valueField = this.valueField;
    	this.multiSelect = true;
    	this.queryMode = 'local';
    	var me = this;   
    	var thisid = this.id;
    	var allOptId = thisid+"_allOpt";
        this.allOptId = allOptId;
        this.setRawValue =function(value) {
            var me = this;
            value = Ext.value(me.transformRawValue(value), '');
            me.rawValue = value;

            var isAllValue = false;
            var allOptInput = document.getElementById(this.allOptId);
        	if(allOptInput!=null)
        	{
        		if(allOptInput.checked)
        		{
        			isAllValue = true;
        		}
        	}
        	var disvalue = value;
        	//this.setRawValue("ALL");
            // Some Field subclasses may not render an inputEl
            if (me.inputEl) {
            	
                if(isAllValue)
                {
                	disvalue = "ALL";
                	this.suspendCheckChange++;
                	allOptInput.checked = true;
                }
                me.inputEl.dom.value = disvalue;
            }
            return disvalue;
        }, 
    	this.listConfig = {   	 
    	     tpl:	'<div class="mt-boundlist-item" onclick="clickAllOptionDiv(\''+thisid+'\')"><input onclick="clickAllOptionInput(this)" type="checkbox" id="'+allOptId+'">ALL</div><tpl for="."><div class="x-boundlist-item"><input type=checkbox>{'+this.displayField+'}</div></tpl>',
	  	     onItemSelect: function(record) {    
	  	    	  var node = this.getNode(record);
    	          if (node) {
    	             Ext.fly(node).addCls(this.selectedItemCls);
    	             
    	             var checkboxs = node.getElementsByTagName("input");
    	             if(checkboxs!=null)
    	             {
    	            	 var checkbox = checkboxs[0];
  	    				 checkbox.checked = true;
    	             }
    	          }
    			 var isAllSelected = true;
    			 var store = this.getStore();
    			 if(store!=null&&store.getTotalCount()>0)
    			 {
  	    			 for(var i=0;i<store.getTotalCount();i++)
  	    			 {
  	    				 var recordTemp = store.getAt(i);
  	    				 var itemTemp = this.getNode(recordTemp);
  	    				 var isSelectedTemp = this.isSelected(itemTemp);
  	    				 if(!isSelectedTemp){
   	    					isAllSelected = false; 	  
  	    					break; 					
  	    				 }
  	    			 }
    			 }else{
    				isAllSelected = false;
    			 }

    			 if(isAllSelected)
    			 {
    				me.selectAllOpt();
    			 }    	            	             	                 	              	             	          
	  	      },
	  	    onItemDeselect: function(record) {
	  	    	var node = this.getNode(record);
  	            if (node) {
  	            	 Ext.fly(node).removeCls(this.selectedItemCls);
  	             
  	             var checkboxs = node.getElementsByTagName("input");
  	             if(checkboxs!=null)
  	             {
  	            	 var checkbox = checkboxs[0];
	    				 checkbox.checked = false;
	    			 me.deselectAllOpt();
	    				 
  	             }
  	            }
	  	    },
	  	      listeners:{
  	    		  itemclick:function(view, record, item, index, e, eOpts ){
  	    			  var isSelected = view.isSelected(item);
  	    			  var checkboxs = item.getElementsByTagName("input");
  	    			  if(checkboxs!=null)
  	    			  {
  	    				  var checkbox = checkboxs[0];
  	    				  if(!isSelected)
  	    				  {
  	    					  checkbox.checked = true;
  	    				  }else{
  	    					  checkbox.checked = false;
  	    				  }
  	    			  }  	    			   	    			 
  	    		  },show:function(view, eOpts ){
  	    			  if(me.getRawValue()=="ALL")
  	    			  {
  	    				 view.getSelectionModel().selectAll(view.getStore());
  	    				 //me.selectAllOpt();
  	    			  }
  	    		  }
	  	      }    	  
	  	}   	
    	this.callParent();
    },
    
    deselectAllOpt:function()
    {
    	var allOptInput = document.getElementById(this.allOptId);
    	if(allOptInput!=null)
    	{
    		allOptInput.checked =false;   		
    	}
    },
    selectAllOpt:function()
    {
    	var allOptInput = document.getElementById(this.allOptId);
    	if(allOptInput!=null)
    	{
    		allOptInput.checked =true;
    	}
    	//this.setRawValue("ALL");
    }    
    
    
    
});

function clickAllOptionDiv(comboxId)
{	
	if(comboxId!=null&&comboxId.length>0)
	{
		var allOptInputId = comboxId+"_allOpt";
		var allOptInput = document.getElementById(allOptInputId);
		clickAllOptionInput(allOptInput);		
	}
}

function clickAllOptionInput(allOptInput)
{
	if(allOptInput!=null)
	{
		var allOptInputId = allOptInput.id;
		var allOptInputIdArray = allOptInputId.split("_allOpt");
		var comboxId = allOptInputIdArray[0];
		var isChecked = allOptInput.checked;
		var combobox = Ext.getCmp(comboxId);
		var boundList = combobox.getPicker(); 
		if(boundList!=null)
		{	
			var selModel = boundList.getSelectionModel();
			selModel.deselectOnContainerClick=false;
		}
		var allValueArray = getAllStoreValueArryByAtt(combobox.getStore(),combobox.valueField);
		if(isChecked)
		{
			allOptInput.checked=false;			
			if(combobox!=null)
			{
				combobox.setValue([]);
			}
		}else{
			allOptInput.checked=true;
			if(combobox!=null)
			{
				combobox.setValue(allValueArray);									
			}
		}
		if(combobox.allOptClickFun!=null)
			combobox.allOptClickFun();
	}	
}


function getAllStoreValueArryByAtt(store,key)
{
	var valueArray = [];
	if(store!=null)
	{
		for(var i=0;i<store.getTotalCount();i++)
		{
			var record = store.getAt(i);
			if(record.get(key)!=null)
			{
				valueArray.push(record.get(key));
			}
		}
	}
	return valueArray;
}


/**
 * end for multi Select combobox
 */

实现效果





你可能感兴趣的:([Ext JS 4] 实战之多选下拉单 (带checkbox) 续 - 带ALL 选项)