EXT2.0 checkbox树的扩展(支持单选,级联多选,只选叶子等)
转载自http://www.iteye.com/topic/164426
在ext1.x里,树是没有checkbox的, 幸好在2.X版本里提供了这个功能, 在许多应用里, 带有checkbox的树使用还是很常见的
Ext2.X提供了简单的checkbox实现,但对于一些复杂的需求,如: 级联多选(选中父结点,自选中其所有子结点和所有父结点) , 单选等等, Ext2.X并没有帮我们实现
还有最难解决的情况, 当树是异步的时候, 要想级联多选, 实现起来有些难度
在此, 通过对Ext.tree.TreeNodeUI进行扩展,这些问题都得到很好的解决
1. /**
2. * @class Ext.tree.TreeCheckNodeUI
3. * @extends Ext.tree.TreeNodeUI
4. *
5. * 对 Ext.tree.TreeNodeUI 进行checkbox功能的扩展,后台返回的结点信息不用非要包含checked属性
6. *
7. * 扩展的功能点有:
8. * 一、支持只对树的叶子进行选择
9. * 只有当返回的树结点属性leaf = true 时,结点才有checkbox可选
10. * 使用时,只需在声明树时,加上属性 onlyLeafCheckable: true 既可,默认是false
11. *
12. * 二、支持对树的单选
13. * 只允许选择一个结点
14. * 使用时,只需在声明树时,加上属性 checkModel: "single" 既可
15. *
16. * 二、支持对树的级联多选
17. * 当选择结点时,自动选择该结点下的所有子结点,以及该结点的所有父结点(根结点除外),特别是支持异步,当子结点还没显示时,会从后台取得子结点,然后将其选中/取消选中
18. * 使用时,只需在声明树时,加上属性 checkModel: "cascade" 既可
19. *
20. * 三、添加"check"事件
21. * 该事件会在树结点的checkbox发生改变时触发
22. * 使用时,只需给树注册事件,如:
23. * tree.on("check",function(node,checked){...});
24. *
25. * 默认情况下,checkModel为'multiple',也就是多选,onlyLeafCheckable为false,所有结点都可选
26. *
27. * 使用方法:在loader里加上 baseAttrs:{uiProvider:Ext.tree.TreeCheckNodeUI} 既可.
28. * 例如:
29. * var tree = new Ext.tree.TreePanel({
30. * el:'tree-ct',
31. * width:568,
32. * height:300,
33. * checkModel: 'cascade', //对树的级联多选
34. * onlyLeafCheckable: false,//对树所有结点都可选
35. * animate: false,
36. * rootVisible: false,
37. * autoScroll:true,
38. * loader: new Ext.tree.DWRTreeLoader({
39. * dwrCall:Tmplt.getTmpltTree,
40. * baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI } //添加 uiProvider 属性
41. * }),
42. * root: new Ext.tree.AsyncTreeNode({ id:'0' })
43. * });
44. * tree.on("check",function(node,checked){alert(node.text+" = "+checked)}); //注册"check"事件
45. * tree.render();
46. *
47. */
48.
49. Ext.tree.TreeCheckNodeUI = function() {
50. //'multiple':多选; 'single':单选; 'cascade':级联多选
51. this.checkModel = 'multiple';
52.
53. //only leaf can checked
54. this.onlyLeafCheckable = false;
55.
56. Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments);
57. };
58.
59. Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI, {
60.
61. renderElements : function(n, a, targetNode, bulkRender){
62. var tree = n.getOwnerTree();
63. this.checkModel = tree.checkModel || this.checkModel;
64. this.onlyLeafCheckable = tree.onlyLeafCheckable || false;
65.
66. // add some indent caching, this helps performance when rendering a large tree
67. this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
68.
69. var cb = (!this.onlyLeafCheckable || a.leaf);
70. var href = a.href ? a.href : Ext.isGecko ? "" : "#";
71. 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">',
72. '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
73. '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
74. '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
75. cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
76. '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
77. a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
78. '<ul class="x-tree-node-ct" style="display:none;"></ul>',
79. "</li>"].join('');
80.
81. var nel;
82. if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
83. this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
84. }else{
85. this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
86. }
87.
88. this.elNode = this.wrap.childNodes[0];
89. this.ctNode = this.wrap.childNodes[1];
90. var cs = this.elNode.childNodes;
91. this.indentNode = cs[0];
92. this.ecNode = cs[1];
93. this.iconNode = cs[2];
94. var index = 3;
95. if(cb){
96. this.checkbox = cs[3];
97. Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null]));
98. index++;
99. }
100. this.anchor = cs[index];
101. this.textNode = cs[index].firstChild;
102. },
103.
104. // private
105. check : function(checked){
106. var n = this.node;
107. var tree = n.getOwnerTree();
108. this.checkModel = tree.checkModel || this.checkModel;
109.
110. if( checked === null ) {
111. checked = this.checkbox.checked;
112. } else {
113. this.checkbox.checked = checked;
114. }
115.
116. n.attributes.checked = checked;
117. tree.fireEvent('check', n, checked);
118.
119. if(!this.onlyLeafCheckable && this.checkModel == 'cascade'){
120. var parentNode = n.parentNode;
121. if(parentNode !== null) {
122. this.parentCheck(parentNode,checked);
123. }
124. if( !n.expanded && !n.childrenRendered ) {
125. n.expand(false,false,this.childCheck);
126. }
127. else {
128. this.childCheck(n);
129. }
130. }else if(this.checkModel == 'single'){
131. var checkedNodes = tree.getChecked();
132. for(var i=0;i<checkedNodes.length;i++){
133. var node = checkedNodes[i];
134. if(node.id != n.id){
135. node.getUI().checkbox.checked = false;
136. node.attributes.checked = false;
137. tree.fireEvent('check', node, false);
138. }
139. }
140. }
141.
142. },
143.
144. // private
145. childCheck : function(node){
146. var a = node.attributes;
147. if(!a.leaf) {
148. var cs = node.childNodes;
149. var csui;
150. for(var i = 0; i < cs.length; i++) {
151. csui = cs[i].getUI();
152. if(csui.checkbox.checked ^ a.checked)
153. csui.check(a.checked);
154. }
155. }
156. },
157.
158. // private
159. parentCheck : function(node ,checked){
160. var checkbox = node.getUI().checkbox;
161. if(typeof checkbox == 'undefined')return ;
162. if(!(checked ^ checkbox.checked))return;
163. if(!checked && this.childHasChecked(node))return;
164. checkbox.checked = checked;
165. node.attributes.checked = checked;
166. node.getOwnerTree().fireEvent('check', node, checked);
167.
168. var parentNode = node.parentNode;
169. if( parentNode !== null){
170. this.parentCheck(parentNode,checked);
171. }
172. },
173.
174. // private
175. childHasChecked : function(node){
176. var childNodes = node.childNodes;
177. if(childNodes || childNodes.length>0){
178. for(var i=0;i<childNodes.length;i++){
179. if(childNodes[i].getUI().checkbox.checked)
180. return true;
181. }
182. }
183. return false;
184. },
185.
186. toggleCheck : function(value){
187. var cb = this.checkbox;
188. if(cb){
189. var checked = (value === undefined ? !cb.checked : value);
190. this.check(checked);
191. }
192. }
193. });
使用方法都在注释里了,应该已经很详细了,我就不多说了
需要注意的是, 使用例子里的Tree 使用了Ext.tree.DWRTreeLoader这个扩展类,用来加载后台树结点,这和使用其它的loader没有区别的,
如果您使用其它的loader, 同样加上baseAttrs: { uiProvider: Ext.tree.TreeCheckNodeUI }就行了