之前使用过xtree和dojo中的tree,感觉都是怪怪的,界面简陋,功能也不好上手,待看到ext里的树形真是眼前一亮,在此之前,动态增添,修改删除节点,拖拽和右键菜单,我一直认为是不可能实现的任务,而在ext上却轻松实现了,而且界面和动画效果相当完美。真是让人爱不释手啊。
树形是非常典型的一种数据结构,多级菜单,部门组织结构,省市县三级这种金字塔结构都可以用树形表示,要表示一个老爸有一帮孩子的情况真是非树形莫属啊,做好了这部分,绝对是个亮点。
树形世界的万物之初是一个TreePanel。
var tree = new Ext.tree.TreePanel('tree');
这里的参数'tree',表示渲染的dom的id。html写着个<div id="tree"></div>做呼应呢,最后这棵树就出现在这个div的位置上。
现在我们获得了树形面板,既然是树就必须有一个根,有了根才能在上边加枝子,放叶子,最后装饰的像一棵树似的。嗯,所以根是必要的,我们就研究研究这个根是怎么咕哝出来的。
var root = new Ext.tree.TreeNode({text:'偶是根'});
看到了吧,它自己都说它自己是根了,所以它就肯定是根没错。再看下面。
tree.setRootNode(root); tree.render();
首先,我们把这个根root,放到tree里,用了setRootNode()方法,就是告诉tree,这是一个根,你可得把它放好啊。
立刻对tree进行渲染,让它出现在id="tree"的地方,这个id可是在上面指定的,如果有疑问,请翻回去继续研究,我们就不等你,继续了。
当当,我非常荣幸的向您宣布,咱们的第一棵树出来了。这是它的照片。
靠,这是树吗?
嗯,现在的确不太像树,因为它只有一个根。
靠,我们要的是树,你就搞出一个根来,有鸟用啊。
嗯,理论上等它自己发芽长成树,可能性是比较小,不如咱们偷偷插上几个枝子,说不定看起来就更像树了。
注意
虽然只有一个根,不过也算是棵树,1.x的例子在lingo-sample/1.1.1/03-01.html。
上回书说道,我们要偷偷插上几个杈子,让这个本来就是树的树,更像一棵树。
var root = new Ext.tree.TreeNode({text:'偶是根'}); var node1 = new Ext.tree.TreeNode({text:'偶是根的第一个枝子'}); var node2 = new Ext.tree.TreeNode({text:'偶是根的第一个枝子的第一个叶子'}); var node3 = new Ext.tree.TreeNode({text:'偶是根的第一个叶子'}); node1.appendChild(node2); root.appendChild(node1); root.appendChild(node3);
我们从外边拿来三个TreeNode,不管不顾就把node2插到node1上,node1,node2一起插到root上,这下好了,咱们的树立刻就像是一棵树了。
咦?它怎么还是这么点儿东西?客官莫急,待小二儿给你整治一番,即看庐山真面目。
嗯,现在的确有点儿意思了,不过它开始的时候就那么缩在一团,看着很不爽,每次都要点这么几下才能看到底下的东西,咱们有没有办法让它每次渲染好就自己展开呢?
方法当然有咯,请上眼。
root.expand(true, true);
这一下就能解您燃眉之急,第一个参数是说,是否递归展开所有子节点,如果是false,就只展开第一级子节点,子节点下面还是折叠着的,。第二个参数是说是否有动画效果,true的话,你可以明显看出来那些节点是一点儿点儿展开的,否则就是刷拉一下子就出来了.
为了方便,咱们的例子里直接就可以展开了,省的再去点啊。1.x的例子在lingo-sample/1.1.1/03-02.html。
第一个区别就是TreePanel的定义,原来的id要放到{}中,对应的名字是el。像这样改:
var tree = new Ext.tree.TreePanel({ el:'tree' });
即使这样改完了,还是什么都看不见,我们错过了什么?用findbug看了一下dom,height竟然是0,当然啥也看不见了,2.0里的树为啥不会自动伸缩呢,只好咱们给它设置一个初始高度,在html里设置个300px的高度,就可以显示出来了。
<div id="tree" style="height:300px;"></div>
另一个也如法炮制。我们就可以看到2.0比1.x多了鼠标移到树节点上时的高亮显示。
好了,看了这些例子,应该对树型有些认识了,虽然这里只有TreeNode,却能表示枝杈或者叶子,原理很简单,如果这个TreeNode下有其他节点,它就是一个枝杈,如果没有,它就是一个叶子,从它前头的图标就很容易看出来。嘿嘿,根其实就是一个没有上级节点的枝杈了。实际上,他们都是TreeNode而已,属性不同而已。
2.0的例子在lingo-sample/2.0/目录下,分别是03-01.html和03-02.html。
如此刀耕火种不但麻烦,而且容易犯错,有没有更简便一些的方法吗?答案是利用Ext.tree.TreeLoader和后台进行数据交换,我们在只提供数据,让TreeLoader帮咱们做数据转换和装配节点的操作。
啦啦啦,json和ajax要登场了,不过你是否还记得我说过,一旦涉及到ajax就需要配合服务器了,ajax是无法从本地文件系统直接取得数据的。
首先,让我们为TreePanel加上TreeLoader
var tree = new Ext.tree.TreePanel('tree', { loader: new Ext.tree.TreeLoader({dataUrl: '03-03.txt'}) });
在此,TreeLoader仅包含一个参数dataUrl: '03-03.txt',这个dataUrl表示,在渲染后要去哪里读取数据,为了方便模拟,我们写了一个txt文本文件提供数据,直接打开03-03.txt可以看到里边的内容。
[ {text:'not leaf'}, {text:'is leaf',leaf:true} ]
里边是一个包含了两个节点定义的数组,可能你会发觉那个多出来的属性leaf:true,它的效果很神奇,这一点我们马上就可以看到。
如果你现在就去匆匆忙忙的刷新页面,想看一下咱们的成果,那一定会失望而归,页面上没有像你期待的那样,从03-03.txt读取数据,显示到页面上,你依然只能看到那个孤零零的根。这是因为TreeNode是不支持ajax的,我们需要把根节点换成AsyncTreeNode,它可以实现咱们的愿望。
var root = new Ext.tree.AsyncTreeNode({text:'偶是根'});
估计谁第一次看到这场面都一定吓傻了。我们不是只定义了两个节点吗?怎么一下子跑出这么多东西来?先别着急,让我们先把root.expand(true, true)改成root.expand(),避免节点无限展开下去,然后慢慢研究这个情况。
现在场面被控制住了,取消了递归展开,只展开根节点的第一层节点,我们得到的确实是与03-03.txt文件里相对应的两个节点,不过这两个节点有些不同,not leaf节点的图标赫然是枝杈的图标,如果点击它前面的加号,便又成了上面的场景。Why?
原因就来自AsyncTreeNode,这个东西会继承根节点TreeLoader中dataUrl,你点展开的时候,会执行这个节点的expand()方法,ajax会跑到dataUrl指定的地址去取数据,用firebug可以看到当前节点id会作为参数传递给dataUrl指定的地址,这样我们的后台就可以通过这个节点的id计算出该返回什么数据,得到了数据TreeLoader去解析数据并装配成子节点,然后显示出来。
哈哈,现在就是关键部分了。因为咱们使用的03-03.txt提供的数据,不会判断当前节点的id,所以每次返回的数据都是一样的,这就是树会无限循环下去的原因了。
那么为啥只有第一个节点会无限循环下去呢?第二个节点就没有那个小加号,呵呵~因为第二个节点不是AsyncTreeNode ,TreeLoader在生成节点的时候会判断数据里的leaf属性,如果是leaf:true,那么就会生成TreeNode而不是AsyncTreeNode,TreeNode可不会自动去用ajax取值,自然就不会无限循环展开了。
现实中,异步读取属性的节点是很爽的一件事情,因为你可能要保存成千上万条节点记录。一次性全部装载的话,无论读取和渲染的速度都会很慢。使用异步读取的方式,只有点击某一节点的时候,才去获得子节点属性,并进行渲染,极大的提高了用户体验。而且ext的树形本身有缓存机制,打开一次,再点击也不会去重复读取了,提升了响应速度。
html例子,1.x版本在lingo-sample/1.1.1/03-03.html,2.0版本在lingo-sample/2.0/03-03.html。
为了巩固学习效果,咱们再写一个json获得数据的例子,这次的json稍微写复杂一点儿。
这次对应的json数据文件是03-04.txt。
[ {text:'01',children:[ {text:'01-01',leaf:true}, {text:'01-02',children:[ {text:'01-02-01',leaf:true}, {text:'01-02-02',leaf:true} ]}, {text:'01-03',leaf:true} ]}, {text:'02',leaf:true} ]
这也可以看作是在数据不多的情况下,一次加载所有数据的途径,只要确保所有叶子节点上都加上leaf:true的属性,就不会出现循环展开的问题了。
html例子:1.x在lingo-sample/1.1.1/03-04.html,2.0在lingo-sample/2.0/03-04.html。