前言
在
[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 */
实现效果