在ext1.x里,树是没有checkbox的, 幸好在2.X版本里提供了这个功能, 在许多应用里, 带有checkbox的树使用还是很常见的
Ext2.X提供了简单的checkbox实现,但对于一些复杂的需求,如: 级联多选(选中父结点,自选中其所有子结点和所有父结点) , 单选等等, Ext2.X并没有帮我们实现
还有最难解决的情况, 当树是异步的时候, 要想级联多选, 实现起来有些难度
在此, 通过对Ext.tree.TreeNodeUI进行扩展,这些问题都得到很好的解决
/** * @class Ext.tree.TreeCheckNodeUI * @extends Ext.tree.TreeNodeUI * * 对 Ext.tree.TreeNodeUI 进行checkbox功能的扩展,后台返回的结点信息不用非要包含checked属性 * * 扩展的功能点有: * 一、支持只对树的叶子进行选择 * 只有当返回的树结点属性leaf = true 时,结点才有checkbox可选 * 使用时,只需在声明树时,加上属性 onlyLeafCheckable: true 既可,默认是false * * 二、支持对树的单选 * 只允许选择一个结点 * 使用时,只需在声明树时,加上属性 checkModel: "single" 既可 * * 二、支持对树的级联多选 * 当选择结点时,自动选择该结点下的所有子结点,以及该结点的所有父结点(根结点除外),特别是支持异步,当子结点还没显示时,会从后台取得子结点,然后将其选中/取消选中 * 使用时,只需在声明树时,加上属性 checkModel: "cascade" 既可 * * 三、添加"check"事件 * 该事件会在树结点的checkbox发生改变时触发 * 使用时,只需给树注册事件,如: * tree.on("check",function(node,checked){...}); * * 默认情况下,checkModel为'multiple',也就是多选,onlyLeafCheckable为false,所有结点都可选 * * 使用方法:在loader里加上 baseAttrs:{uiProvider:Ext.tree.TreeCheckNodeUI} 既可. * 例如: * var tree = new Ext.tree.TreePanel({ * el:'tree-ct', * width:568, * height:300, * checkModel: 'cascade', //对树的级联多选 * onlyLeafCheckable: false,//对树所有结点都可选 * animate: false, * rootVisible: false, * autoScroll:true, * loader: new Ext.tree.DWRTreeLoader({ * dwrCall:Tmplt.getTmpltTree, * baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI } //添加 uiProvider 属性 * }), * root: new Ext.tree.AsyncTreeNode({ id:'0' }) * }); * tree.on("check",function(node,checked){alert(node.text+" = "+checked)}); //注册"check"事件 * tree.render(); * */ Ext.tree.TreeCheckNodeUI = function() { //'multiple':多选; 'single':单选; 'cascade':级联多选 this.checkModel = 'multiple'; //only leaf can checked this.onlyLeafCheckable = false; Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments); }; Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI, { renderElements : function(n, a, targetNode, bulkRender){ var tree = n.getOwnerTree(); this.checkModel = tree.checkModel || this.checkModel; this.onlyLeafCheckable = tree.onlyLeafCheckable || false; // add some indent caching, this helps performance when rendering a large tree this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; var cb = (!this.onlyLeafCheckable || a.leaf); var href = a.href ? a.href : Ext.isGecko ? "" : "#"; var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">', '<span class="x-tree-node-indent">',this.indentMarkup,"</span>", '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />', '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />', cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '', '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ', a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>", '<ul class="x-tree-node-ct" style="display:none;"></ul>', "</li>"].join(''); var nel; if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){ this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf); }else{ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf); } this.elNode = this.wrap.childNodes[0]; this.ctNode = this.wrap.childNodes[1]; var cs = this.elNode.childNodes; this.indentNode = cs[0]; this.ecNode = cs[1]; this.iconNode = cs[2]; var index = 3; if(cb){ this.checkbox = cs[3]; Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null])); index++; } this.anchor = cs[index]; this.textNode = cs[index].firstChild; }, // private check : function(checked){ var n = this.node; var tree = n.getOwnerTree(); this.checkModel = tree.checkModel || this.checkModel; if( checked === null ) { checked = this.checkbox.checked; } else { this.checkbox.checked = checked; } n.attributes.checked = checked; tree.fireEvent('check', n, checked); if(!this.onlyLeafCheckable && this.checkModel == 'cascade'){ var parentNode = n.parentNode; if(parentNode !== null) { this.parentCheck(parentNode,checked); } if( !n.expanded && !n.childrenRendered ) { n.expand(false,false,this.childCheck); } else { this.childCheck(n); } }else if(this.checkModel == 'single'){ var checkedNodes = tree.getChecked(); for(var i=0;i<checkedNodes.length;i++){ var node = checkedNodes[i]; if(node.id != n.id){ node.getUI().checkbox.checked = false; node.attributes.checked = false; tree.fireEvent('check', node, false); } } } }, // private childCheck : function(node){ var a = node.attributes; if(!a.leaf) { var cs = node.childNodes; var csui; for(var i = 0; i < cs.length; i++) { csui = cs[i].getUI(); if(csui.checkbox.checked ^ a.checked) csui.check(a.checked); } } }, // private parentCheck : function(node ,checked){ var checkbox = node.getUI().checkbox; if(typeof checkbox == 'undefined')return ; if(!(checked ^ checkbox.checked))return; if(!checked && this.childHasChecked(node))return; checkbox.checked = checked; node.attributes.checked = checked; node.getOwnerTree().fireEvent('check', node, checked); var parentNode = node.parentNode; if( parentNode !== null){ this.parentCheck(parentNode,checked); } }, // private childHasChecked : function(node){ var childNodes = node.childNodes; if(childNodes || childNodes.length>0){ for(var i=0;i<childNodes.length;i++){ if(childNodes[i].getUI().checkbox.checked) return true; } } return false; }, toggleCheck : function(value){ var cb = this.checkbox; if(cb){ var checked = (value === undefined ? !cb.checked : value); this.check(checked); } } });
使用方法都在注释里了,应该已经很详细了,我就不多说了
需要注意的是, 使用例子里的Tree 使用了Ext.tree.DWRTreeLoader这个扩展类,用来加载后台树结点,这和使用其它的loader没有区别的,
如果您使用其它的loader, 同样加上baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI }就行了