有效.
点开根节点,显示如下:
-----------------------
| +-Servers
-----|-----------------
| +-ExchangServer
-----|-----------------
| +-Notes Server
-----------------------
即使Servers前没有图象,它
他们并没有真的错开!原因是间相互依赖.解决的办法是解除依赖,即把每个节点都放入单独的表格.代码如下:
点开根节点,显示如下:
---|------------------
| + Servers
---|------------------
| | + ExchangServer
| |
| + Notes Server
----------------------
调整展开操作:
从刚才显示的结果看出,节点始终可以被展开,展开操作的图象始终存在,即使没有这两个子节点,为什么一个没有子节点的节点要有展开操作?我们只要那些有子节点的节点才能展开!我们希望所有这类节点将只显示空格而非展开操作的图标.所以我们必须修改 的标签属性把替换成,现在标签体只对那些有子节点的节点有效.代码如下:
|
"> src="../images/collapsedMidNode.gif" border="0">
|
|
|
浏览器显示如下:
---|------------------
| + Servers
| |
| | ExchangServer
|
| Notes Server
----------------------
这样,不切实际的展开操作就不会发生.但是缩进又被破坏了!子节点名字被显示被显示在根节点的名字下面咯!这是因为我们不能给没有子节点的(没有展开操作的)节点成功插入空格!下面的代码也能形成上面的屏幕显示:
上述行只能放在插入展开操作的 ...元素的后面 整体代码如下:
|
"> src="../images/collapsedMidNode.gif" border="0">
|
|
|
屏幕显示如下:
---|------------------
| + Servers
| |
| | ExchangServer
|
| Notes Server
----------------------
浏览器中收起的显示如下:
---|------------------
| + Servers
---|------------------
这些才是我们想要的.若加上一些其它的子节点,效果同样很棒:
---|---------------------
| -- Servers
| |
| -- ExchangServer
| | Peter Johnson
| |
| -- Notes Server
| | Peter Johnson
| Jakob Jenkov
-------------------------
注意一下缩进中ExchangServer和Notes Server之间的垂直线并不对齐~!这是空格引起的.在表的单元格中含有如下缩进代码:
|
空格和"/"或线破坏了 与 | 的匹配导致了垂直线不对齐.在树型的结构中图象间类似空格和"/"或线等符号都会导致垂直线错位.
使垂直方向能成线,并增加易读性.应该这样显示更好:
---|---------------------
| -- Servers
| |
| -- ExchangServer
| | Peter Johnson
| |
| -- Notes Server
| | Peter Johnson
| Jakob Jenkov
-------------------------
其实hasChildren="false"是一般不会做expanded="true"展开操作,所以hasChildren与expanded两属性,
浏览器显示如下:
---|--------------------
| -- Servers
| |
| -- ExchangServer
| | Peter Johnson
| |
| -- Notes Server
| Peter Johnson
| Jakob Jenkov
------------------------
到此似乎接近完美咯,汇总一下代码,JSP显示如下:
<%-- 树定义 --%>
<%@ page
import="com.jenkov.prizetags.tree.itf.ITree,
com.jenkov.prizetags.tree.impl.Tree,
com.jenkov.prizetags.tree.itf.ITreeNode,
com.jenkov.prizetags.tree.impl.TreeNode"
%>
<%@ taglib uri="/WEB-INF/treetag.tld" prefix="tree"%>
<%
if (session.getAttribute("tree.model") == null) {
ITree tree = new Tree();
//(node's id , node's name , node's type)
ITreeNode root = new TreeNode("rootId", "Servers", "root");
ITreeNode server1 = new TreeNode("server1Id","Exchange Server", "server");
ITreeNode server2 = new TreeNode("server2Id", "Notes Server","server");
ITreeNode user1 = new TreeNode("user1Id", "Peter Johnson","user");
ITreeNode user2 = new TreeNode("user1Id", "Jakob Jenkov","user");
root.addChild(server1);
root.addChild(server2);
server1.addChild(user1);
server2.addChild(user1);
server2.addChild(user2);
tree.setRoot(root);
session.setAttribute("tree.model", tree);
}
%>
第二章:树模型详解
树模型由两国主要的接口和他们的实现类组成。
这些接口是:
com.jenkov.prizetags.tree.itf.ITree
com.jenkov.prizetags.tree.itf.ITreeNode
实现类是:
com.jenkov.prizetags.tree.impl.Tree
com.jenkov.prizetags.tree.impl.TreeNode
关于tree类
类tree是跟踪记录哪些节点被展开或选择的。这些记录并不存放在树节点自身。这样就使存在不同的树实例共享相同的节点成为可能。而不会相互影响他们之间的展开选择等操作。这个特征被开发者用来实现不同的用户访问相同的缓存过来的树数据,而不互相影响状态。这个特征的缺点是如果你从一棵树中移除了一个被展开或被选择的节点,你必须从自己的ITree实例中将这个节点的ID移除。这些同样页适合那些没有展开或被选择的节点。
展开和收起方法:
你可以通过实现接口ITree的getExpandedNodes方法得到被展开节点的集合。
public Set getExpandedNodes();
只有被展开的节点ID被保存,以便跟踪记录树的展开状态。你可以通过以下ITree的方法实现展开或收起一个节点:
public void expand (String treeNodeId);
public void collapse (String treeNodeId);
通过ITree的isExpanded方法可以知道指定的节点是否被展开
public boolean isExpanded(String treeNodeId);
Select / Unselect 方法
和获得展开的节点一样,通过ITree的getSelectedNodes方法可以获得被选择节点的集合:
public Set getSelectedNodes();
只有被选择的节点ID被保存,以便跟踪记录树的选择状态。你可以通过以下ITree的方法实现选择或释放一个节点:
public void select (String treeNodeId);
public void unSelect (String treeNodeId);
public void unSelectAll();
通过ITree的isSelected方法可以知道指定的节点是否被选中:
public boolean isSelected(String treeNodeId);
可以把树设置成单选模式,意味着一次只能选择一个节点。一旦你选择了一个节点,以前被选择的节点将自动被释放。在重新载入另外一个含节点信息的窗口时,你不希望在你点击下一个节点后仍然保存着以前被选择的节点。
树默认情况下不是单选的。按你的需要可以定制树的选择模型。有时也是需要同时选择很多节点的,比如所复选框等。
服务端事件侦听(event listeners):
你可以给ITree实例添加event listeners,你可以侦听展开,收起,选中,或释放选中4种事件。
当页面刷新或节点被展开或被选择,这个侦听器将会察觉。
在服务端添加事件侦听方法:
public void addExpandListener (IExpandListener expandListener);
public void removeExpandListener (IExpandListener expandListener);
public void addCollapseListener (ICollapseListener collapseListener);
public void removeCollapseListener (ICollapseListener collapseListener);
public void addSelectListener (ISelectListener selectListener);
public void removeSelectListener (ISelectListener selectListener);
public void addUnselectListener (IUnselectListener unselectListener);
public void removeUnselectListener (IUnselectListener unselectListener);
服务端事件侦听将在下一章详述~
模型抑或视图?
你也许会断言道接口ITree与类Tree不是真正的模型类而是视图类型或视图帮助器。除去对根节点的引用,他们只含有在JSP种绘画树的相关信息。我们不在此争论,但是他们确实与纯树模型和树的节点有密切的关系。
现在详细描述一下:
ITree 接口
下面是全部的接口,不含注释.
public interface ITree {
public ITreeNode getRoot();
public void setRoot(ITreeNode node);
public ITreeNode findNode (String treeNodeId);
public Set findNodes(Set treeNodeIds);
public boolean isExpanded(String treeNodeId);
public void expand (String treeNodeId);
public void collapse (String treeNodeId);
public Set getExpandedNodes();
public void addExpandListener (IExpandListener expandListener);
public void removeExpandListener (IExpandListener expandListener);
public void addCollapseListener (ICollapseListener collapseListener);
public void removeCollapseListener (ICollapseListener collapseListener);
public boolean isSelected(String treeNodeId);
public void select (String treeNodeId);
public void unSelect (String treeNodeId);
public void unSelectAll();
public Set getSelectedNodes();
public void addSelectListener (ISelectListener selectListener);
public void removeSelectListener (ISelectListener selectListener);
public void addUnselectListener (IUnselectListener unselectListener);
public void removeUnselectListener (IUnselectListener unselectListener);
public void setSingleSelectionMode(boolean mode);
public boolean isSingleSelectionMode();
public Iterator iterator(boolean includeRootNode);
}
树节点:
树节点接口 ITreeNode 并且 类 Tree实现该接口。
节点 Id唯一
节点id被用来跟踪哪个节点被展开或选择,所以每个节点要用树中唯一的ID来标识。 若节点ID重复,展开或选定节点时将出错。
在生成节点时,节点的ID是TreeNodeThe构造器提供的。 节点的ID可以通过ITreeNode的方法被访问:
public String getId();
public void setId(String id);
节点ID是String而不是Object型的,似乎看起来象个低级的错误,其实背后是有道理的.树的标签类要通过.equals()方法比较ID..譬如说,当判断一个节点是否展开或被选中,或要在树中找到需要的节点.如果节点的ID是一个Object型的实例,开发人员就不能把节点的ID作为 String, Integer, Long, BigDecimal 还有自己定制的类随机使用咯~在唯一性上来讲,只要所有的实例都是唯一的,则他们就是合法的.不幸的(抑或幸运),.equals()方法能指出两个不相关类的对象不相等(equal).因此,即使使用 String "123"去发现一个节点是否被选中,所得到的结果将是错误的.纵然这个节点的ID是一个 Integer 类型的对象123.这些都会潜在的导致开发者困惑.尤其是一个树是混合节点时:比如所从不同的表中,不同的JNDI trees,不同的文件,工程中定义的常量节点等得到的.所以为了安全起见,我们还是给ID赋予String类型吧!实际上,既然很多对象都可以与 String 互相转换,也在无形中消除了一些障碍.
添加子节点和设置父节点:
每个添加删除子节点,设置父节点的方法都有两个版本:
public void addChild(ITreeNode node);
public void addChildOnly(ITreeNode node);
public void removeChild(ITreeNode node);
public void removeChildOnly(ITreeNode node);
public void setParent(ITreeNode parent);
public void setParentOnly(ITreeNode parent);
其中第二种方法是用来避免在添加子节点或设置父节点时,父子节点之间出现死递归.为了能保证树型结构的牢固连贯,一个节点添加子节点的方法是child.setParentOnly(this);该方法不需要回访父节点.同样地,如果你给一个节点增加父节点时child.setParent(node).
子节点的 setParent()方法体中将调用 parent.addChildOnly(this)方法.而addChildOnly方法并不进一步的调用增加子节点方法:
parent.addChild(child) ---> child.setParentOnly(this) ---> no more calls.
child.setParent(child) ---> parent.addChildOnly(this) ---> no more calls.
child.setParent(null) ---> parent.removeChildOnly(this) ---> no more calls.
parent.removeChild(child) ---> child.setParentOnly(null) ---> no more calls.
如果每个方法只有一个版本,势必造成死递归:
parent.addChild(child) ---> child.setParent(this)---> parent.addChild(this) ---> child.setParent(this)---> ...
parent child
|------addChild---> |
|<-----setParent--- |
|------addChild---> |
|<-----setParent--- |
我们消除了递归,但是把保持树模型完整性的责任推给了使用者.
每一个对parent.addChild(child)方法的调用后,尽接着必须跟上一个child.setParent(parent)方法. 不尽人意的代码撒. 我们可以采用只对父节点所用addChild()方法的方式产生递归而不采用对子节点所用的setParent()方法.这种做法可能在其它地方被使用的. 所以现在要看情况决定,是使用递归版的方法还是使用非递归版的方法.
给树节点配置属性:
给树节点配属一个对象是很有必要的,这个对象可用来定制外观和树节点的文本.相应的方法:
public Object getObject();
public void setObject(Object object);
这章包含怎么配属,分离和定制外观.
接口ITreeNode
下面是全部的接口,不含注释.
public interface ITreeNode {
public String getName();
public void setName(String name);
public String getId();
public void setId(String id);
public String getType();
public void setType(String type);
public Object getObject();
public void setObject(Object object);
public void addChild(ITreeNode node);
public void addChildOnly(ITreeNode node);
public void removeChild(ITreeNode node);
public void removeChildOnly(ITreeNode node);
public void removeAllChildren();
public List getChildren();
public boolean hasChildren();
public ITreeNode getParent();
public void setParent(ITreeNode parent);
public void setParentOnly(ITreeNode parent);
}
在哪里产生树模型?
树模型是被作为 session属性集合存放的, 一边JSP中标签能取到数据.这并不意味这你也必须在JSP页面中生成树模型.可以在 Servlet, Struts action, 或另一个JSP页面中生成树模型. 只要最后树模型被存入session属性中可以被标签使用即可.
匹配树节点:
Tree标签中最强大的一个子标签是 标签,它的反义的标签是.这些匹配标签只有在匹配或不匹配给定的标准是才去取标签体内的值.这个在你想定制个别节点外观时很有用.例如:一个头的图标代表用户,两个头的图标代表用户组.或一个文件夹图标代表一个目录!这个匹配标签能匹配树节点的不同属性.之前你也看过了标签时如何匹配是否有儿子,是否展开等节点.再看个例子:
标签体中只有当前节点的类型是"usergroup"的子标签才会被取到.如果被取到的标签体被以HTML形式得到,代码 将会最终再HTML文档中显示!然后将显示一个代表用户组的图片.
相反的,
将匹配那些所有不是用户组类型的节点
这两种匹配标签属性列表:
Attribute Purpose Allowed Values Examples
--------------------------------------------------------------------------
type Matches the type Any string value. type="usergroup"
of the current node, type="folder"
as returned by the type="movies.thriller.*"
ITreeNode.getType()
method.
--------------------------------------------------------------------------- name Matches the name of the Any string value name="Peter Johnson"
current ITreeNode, as name="*Ltd"
returned by the
ITreeNode.getName()
method.
---------------------------------------------------------------------------
id Matches the id of the Any string value. id="server1"
current node, as id="server*"
returned by the
ITreeNode.getId()
method.
---------------------------------------------------------------------------
expanded Matches if the current true|false expanded="true"
node is expanded. expanded="false"
---------------------------------------------------------------------------
selected Matches if the current true|false selected="true"
node is selected selected="false"
---------------------------------------------------------------------------
hasChildren Matches if the true|false hasChildren="true"
current node hasChildren="false"
has any children
---------------------------------------------------------------------------
isFirstChild Matches if the true|false isFirstChild="true"
current node is isFirstChild="false"
the first child of
it's parent.
---------------------------------------------------------------------------
isLastChild Matches if the true|false isLastChild="true"
current node is isLastChild="false"
the last child of
it's parent
---------------------------------------------------------------------------
如果你在一个匹配标签中使用多个属性,必须保证这些属性同时都匹配,子标签体才会被获取.例子:
这个标签体在只有当前标签有子节点并且是父节点的结束节点时才被获取.
通配符
属性中支持通配符*,例如:type="movies.*"将匹配所有类型以"movies."开头的节点.name="*Johnson" 将匹配所有名称以"Johnson"为结尾的节点.type="movies.*.thriller"将匹配所有类型以"movies."开头,以".thriller"结尾的所有节点.在使用匹配中它只支持单通配符!结合标签,你可以匹配更多的状态.所以这一切对用户来说足够满足他们的需求了. ("... 对每个人来说,640K足够啦 ."). 不然的话,写信给我们,我们会加上你所需要的匹配子标签.其实 就是这么来滴.
节点匹配的嵌套:
你可以在匹配中嵌套节点匹配.匹配标签体被声明为JSP,我们当然就可以在该标签体中插入任何合法的JSP碎片.包括匹配标签~,例子:
这个例子首先匹配所有以"folder."为开始的节点.然后对匹配成功的节点再过滤匹配.最后剩下的是类型以".user" 或".userGroup".为结尾的节点.
添加节点图标:
有时,给节点添加图标,节点将看起来外观更舒服,类型更鲜明.
例如,一个目录或一个文件夹的前面紧贴一个图标.图标会因为节点状态的不同而变换,比如按是否展开或被选中.一个展开的节点会有一个展开的图标.添加这些类型图标其实使用上一章的节点匹配标签就可以咯.首先,在展开操作前添加一个单独的表格单元格.
......
|
"> src="../images/collapsedMidNode.gif" border="0">
"> src="../images/expandedMidNode.gif" border="0">
|
|
<%-- cell reserved for node type / state icon --%>
|
|
知道了它的位置后,下一步是使用 或匹配标签选择正确的图标.下面是一个把type 为"folder"的节点都显示"folder"图标:
![](folder.gif) |
注意下如何嵌套 and | . 下个例子将展示给一个folder配2个图标.展开的和收起的!
|
|
例子代码中的,与上述代码略有差别,他们并不检查节点 type,它只匹配有子节点得节点,象这样:
|
|
|
其实这个例子也给其他节点(没有目录得节点)同样指定了图标,而不仅限于这一个目录,这是最后一个匹配得作用。浏览器中得效果:
---|--------------------
| --★Servers
| |
| --★ExchangServer
| | ■Peter Johnson
| |
| --★Notes Server
| ■Peter Johnson
| ■Jakob Jenkov
------------------------
也许你希望一个节点能用线与其子节点关联起来。再展开操作后,那些自身也有子节点得节点兄弟间已经可以通过线相关联了。
但是那些没有子节点得节点之间似乎没有没有相互关联。 象没有展开和收起操作得节点"Peter Johnson"和"Jakob Jenkov"。为了实现关联,必须替换掉原来显示空格得代码。重要得是要对含有展开收起操作得单元格,使用一下代码替换:
---|--------------------
| --★Servers
| |
| --★ExchangServer
| | |--■Peter Johnson
| |
| --★Notes Server
| |--■Peter Johnson
| |--■Jakob Jenkov
------------------------
综上,用和标签能让你的图标更完美如果遇见不能显示的情况,请写信给我们。
树节点上的关联对象
你可以在节点上配置关联对象。当 当前节点的几个属性不能满足你的需要时,而有想显示一些其他信息时,你可以给树节点关联对象,你可以用该关联对象赖定制树外观。树节点类node的被实现接口ITreeNode是有两个相应的对象关联的方法定义的:
/**
*返回一个对象。如果你需要在JSP页面使用一个对象来显示树的相关信息,
* 则这个被关联的对象再好不过了。
* 通过setObject(Object object)方法把关联对象返回给节点实例。
*/
public Object getObject();
/**
*使对象与树节点关联。一个关联对象
*返回一个对象。如果你需要在JSP页面使用一个对象来显示树的相关信息,
* 则这个被关联的对象再好不过了。
* 参数object:要关联的对象
*/
public void setObject(Object object);
如果你回头看前面的例子你会发现,给节点"Exhange Server"关联一个节点的代码使这样的:
server1.setObject(" - extra info ");
这个例子中,使用 String 型的参数能使该关联对象更可用!再JSP页面中,你可以通过标签来剥离一个对象。代码:
被剥离的对象将被作为一个request属性存放在request中。存放时,是按被剥离对象属性中的名字将作为关键字的。接上例,你可以这样的方式访问被剥离者:
Object detachedObject = request.getAttribute("theNodeObject");
注意:如果一个节点美元对象关联,你从request.getAttribute("theNodeObject")中得到的返回将是NULL。若存在,则现在你可用为所欲为咯,例如你可用用它的toString()方法将页面上呈现一系列字符串!:
<% if(request.getAttribute("theNodeObject") != null){
out.print(request.getAttribute("theNodeObject"));
}
%>
在节点名字所在的单元格的后面插入关联对象自己的单元格,浏览器显示:
---|--------------------
| --★Servers
| |
| --★ExchangServer- extra info
| | |--■Peter Johnson
| |
| --★Notes Server
| |--■Peter Johnson
| |--■Jakob Jenkov
------------------------
在节点"Exhange Server"名字后面显示字符串" - extra info "。如果你用Struts,你可以用自定义标签写出对象的属性:
抑或可以用Struts的匹配标签来进一步定制节点外观。例如下列添加图标或文字:
... do something
...或者使用 或等等。例如你可以使用Struts的匹配标签把超过范围的节点名字着成红色。
记住,你可以在树标签的匹配标签里面嵌套Struts标签,反之亦然(vice versa)~
服务器端事件侦听
给一个ITree实例添加服务期端的事件侦听是很有必要的.可以侦听展开,收起,选择,释放选择事件.用 ITree的下列方法可以添加事件侦听:
public void addExpandListener (IExpandListener expandListener);
public void removeExpandListener (IExpandListener expandListener);
public void addCollapseListener (ICollapseListener collapseListener);
public void removeCollapseListener (ICollapseListener collapseListener);
public void addSelectListener (ISelectListener selectListener);
public void removeSelectListener (ISelectListener selectListener);
public void addUnselectListener (IUnselectListener unselectListener);
public void removeUnselectListener (IUnselectListener unselectListener);
在处理含有树型结构的JSP页面中,当如果任何被侦听的事件发生,Listeners被调用.当你在树实例中自己调用ITree.expand(), ITree.collapse(), ITree.select(),ITree.unselect(),或ITree.unselectAll()方法时侦听器Listeners也会被调用.
当你想使用fly - lazy的方式赋值时,展开操作的侦听器就很好用.开始时,先建3层树结构中第一层,根节点,这3层是:根节点,他们的第一级子节点,及子节点的子节点.当根节点展开,你就必须显示他们的根节点级子节点.
在你的展开事件侦听器中,就必须访问被展开节点.外型:
public class ExpandListener implements IExpandListener{
public void nodeExpanded(ITreeNode node, ITree tree){
// - 移除被展开节点的所有子节点 ( node.removeAllChildren(); )
// - 从节点或关联对象中获得所需ID.
// - 按照获得的ID加载被展开节点的子节点,并把他们添加到被展开的节点上.
// - 按照获得的ID加载被展开节点的子节点的子节点.并将他们添加到子节点上
}
}
你也许会疑问,为什么要加载被展开节点的孙节点!只是被展开节点的子节点被用到了,不是吗?但这是有原因的哈:
如果你通过看它是否有子节点来判断是否该节点有展开操作, 然后子节点加载,但是这些子节点都是没有展开操作的!在模型中,难道子节点就没有儿子?只是没有加载罢了.一旦加载了孙子节点,子节点就可以有展开操作可用,即使这些孙节点从获得的ID找不到的.通过加载孙节点,可以保证有子节点的节点将显示展开操作,而没有子节点的节点不会有.如果你想精简一点,你可以给没有展开操作的子节点只添加一个孙节点.倘若有的话.当节点被展开时,他们始终重新加载.注意:如果你的WEB工程跑在一个异构平台下的集群服务器端,事件侦听不许implement java.io.Serializable,更多请见"A Note on Performance" 和 "A Note on Web / Application Server Clusters"章节
客户端事件侦听00:
你可以在客户端添加类似服务器端的事件侦听器。相比于服务器端,客户端的事件侦听器却不是java类。客户端的事件侦听器是一段当展开,收起,选择,释放选择事件发生时选择执行的JSP片段,下面时展开操作的侦听器。事件侦听标签内的javascript代码重新载入一个"name"祯,该祯里面的页面含有request参数,当前被展开,收起,选择,释放选择的节点的ID。
你可以把合法的JSP碎片放入事件侦听标签中,这里我们选择了javascript,但是它也可以是java代码或html或自定义标签等等.注意下你是如何把我们在标签中用到的节点的ID的作为数据源的.
你也可以把整个节点作为事件标签的数据源。
执行完上面标签的代码,这个事件源节点就可以作为request属性按以"expandedNode", "collapsedNode", "selectedNode" or
"unselectedNode"属性给定关键字使用啦.他们能被java代码或其它标签(Struts标签...etc)访问.和 标签一样,事件侦听标签在使用到"expand", "collapse", "select", or "unselect"参数时将去request中核对。如果你在标签中修改了这些参数的名称,你同样必须修改事件侦听标签中的名称,与标签的使用类似。例子:
显示节点的ID和类型
节点的ID是用来标记哪个节点是被展开了.节点的类型是是来定制树的外观的(图标,文字...etc).有时他们是永远不会显示在JSP页面上的,但是,在页面以HTML方式显示树时你又不得不需要它.例如,用来展开,收起,选择链接的节点的ID是起着标识的作用.下面的两个标签能输出被页面获取的节点的ID,类型.
节点的ID或type,在显示的HTML中将会用具体的值替换掉那些标签。
隐藏根节点:
通过设置 属性includeRootNode="false"隐藏根节点.代码如下:
<%-- the tree --%>
如果以前的代码+现在的代码,则浏览器的显示如下:
-----------------------------------
| --★ExchangServer- extra info
| | |--■Peter Johnson
|
| --★Notes Server
| |--■Peter Johnson
| |--■Jakob Jenkov
-----------------------------------
修改request中展开和选择操作的参数的名称
Changing Expand and Select Request Parameter Names
标签能够自动发现节点的展开,收起,选择,和释放选择操作.如果一个包含树结构的页面请求的参数有"expand","collapse", "select", 或 "unselect",然后这个参数的值将会被认为是有展开,收起,选择,释放选择等操作的节点的ID.有时,受一些其他因素的影响,你的参数名称不能是"expand", "collapse", "select", 或 "unselect"。例如,之前你已经使用select参数了.当然当一个页面有多颗树的情况下,修该参数的名字就很有必要!不然的话,一个节点的展开操作很有可能是作用于另外一个节点的。
在标签中按expandParam, collapseParam, selectParam, 和unselectParam属性修改相应的参数名称。例子:
expandParam="expandThis" collapseParam="collapseThis"
selectParam="selectThis" unselectParam="unselectThis" >
现在标签将寻找名字是"expandThis", "collapseThis","selectThis",和"unselectThis"的request参数.如果你在标签中改变了参数名称,在后面相应的expand, collapse, select, 和 unselect链接中要记住上面参数的名字。
参数名称前缀
从2.3.7版本开始,树标签 之间又有一种新的修改参数名称的方法,那就是 标签的paramPrefix属性。一旦你设置了paramPrefix变量,标签将paramPrefix的值+默认的参数名称而形成的新值作为参数名称。例如,展开操作的默认参数名称是"expand",如果你把paramPrefix设置为"tree1_",那么树标签真正起作用的属性的名字是tree1_expand. 当然这个做法对其它属性也是有意义的,比如 "collapse", "select", "selectOnly" 和 "unselect" (加上其它的,比如
"selectAncestorsAndSelf").当你的同一个页面中有两颗树,那么paramPrefix属性,就大大方便了你的操作,而不会出现该节点的展开操作,作用于另一个节点...etc
Model 2 框架 (Struts, Spring etc.)
解释一下:
Model 1 中,JSP 直接处理Web 浏览器送来之请求( Request ),并辅以JavaBean 处理应用相关逻辑。Model 1 架构单纯编写比较容易,但在Model 1 中JSP 可能同时肩负View 与Controller 角色,两类程序代码有可能混杂而不易维护。
Model 2 中将Servlet 纳入架构中扮演前端Controller 角色,将Web 浏览器送出之请求集中送至Servlet ,Servlet 可集中管理使用者登入、权限控制、多国语言转换等前置处理,再视需求转向给对应之JSP 处理。Model 2 中采用了较佳之MVC 模式,但增加了编写复杂度。
当在WEB项目中使用树型结构,有时,在JSP页面中让树子更新显的很迟。典型的,在WEB项目中,JSP页面是的handler/action控制链中最后被处理的.你也许需要在handler/action处理JSP页面之前更新下树型结构。类TreeUpdater解决了这个问题。TreeUpdater与一样可以更新树结构。其实标签本身用的就是TreeUpdater的一个实例。看看怎么用:
ITree tree1 = (ITree) session.getAttribute("tree.model");
TreeUpdater treeUpdater = new TreeUpdater(request, tree1 );
treeUpdater.update();
update()方法返回的是一个更新过的树。TreeUpdater也把自身作为一个request属性捆绑到request对象上。这样标签得到的就是更新过的树,但要防止反复更新。因为更新会潜在的破坏展开,选择等操作,因为在handler/action中,这些展开,选择等操作是在treeUpdater.update()之后的。因此,标签不要再更新自己是很重要的,如果之前已经更新过了的话。因为程序能自动侦测树是否被更新了,所以我们不用担心。
注意事项:
在页面显示时,只有可见的节点会被获取。所以树模型的大小在执行时没有太大影响,它应该只影响到有多少节点可见,在有些服务器中,尤其是在集群服务器中,session对象是持久化的,也就是说它是存在数据库,或从其他服务器中复制过来的。这样是为了使用户通过他们的session访问不同的服务器更加安全。安全的意思是说向不同的服务器发出的对话属性不会丢失,也就是说在session属性中你存放的数据越多,你向数据库或其他服务器存入的数据就越多。这样看来,树模型越大,页面的处理速度就越慢。
在Web / Application服务器上的一个节点能将不同服务器上的不同节点串在一起,当然也有其他一些情况要考虑。 树模型从v. 2.1.7就Serializable,当添加事件侦听时要记得implement java.io.Serializable.否则,用于串化的服务器就不会serialize树模型和对话,然后树就不会被持久化入不同的服务器中,甚至会报错。
|