下载本文示例代码
Tree2组件使用HTML表格将你的数据呈现为一个树。这个树是动态的:当用户点击它们时它们可以展开或者折叠。该组件同时支持客户端和服务端的交互方式,在客户端交互时使用了JavaScript。在随后的例子中,每次用户的点击将产生一个Request / Response 周期,并在新的视图状态(View State)中重新呈现新的树结构。 注:在后面的例子中只有可见的(已经展开的节点)数据被传送到客户端。而在第一个例子(客户端Tree),在每个HTML Response中,整个树都被发送到客户端浏览器。树的每个节点都包含了不少的HTML代码(假定每个节点200个字符,这个大小将取决于你希望在节点上显示的信息的量),这些信息将被传送到浏览器,其中包括了那些不可见的节点(没有展开的节点),因为它们的一个祖系节点被展开。如果你有一个深度有四层的树,平均每个结点拥有四个子结点,这时候你就需要传输10 102 103 104 = 11 110个节点,每个节点有200个字符,这个树总共就有2 222 000个字符,也就是2M的数据。这个例子将向用户说明,虽然纯客户端Tree会给客户端带来更好的用户体验,但随之而来的带宽问题迅速的增长。纯客户端的树适用于小型的树,或者在Intranet及宽带连接中使用的中型大小的树。对于大型的树,或者你需要照顾到一些低带宽的用户的需要时,你就需要使用服务端树。你可以通过的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2>的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2 clientSideToggle="false" ...>将会使用服务端的树,属性值设为true将会使用客户端的树,默认值为true。 Backing Bean: Tree2组件对Backing Bean中的一个TreeModel进行操作。通常情况,你只要把这个TreeModel绑定到这个组件上就可以了,就像这样:
<t:tree2 value="#{myHandler.treeModel}" 下面需要建立一个类MyHandler,在faces-config.xml中的managed bean配置成myHandler,在例子中这个类提供了一个方法getTreeModel()用于返回一个TreeModel用于表示你的数据。
public class MyHandler { public TreeModel getTreeModel() { ...... }} TreeModel实际上是对TreeNode实例进行了一些简单的包装。 TreeNode是一个接口,其中和tree2相关有四个方法:
String getType()boolean isLeaf()List getChildren()int getChildCount() 其它方法都没有什么用处了,可能会在今后的版本中取消。它们要求开发者在开发backing bean中做一些并不必要的操作。 int getChildCount() 方法返回这个节点的子结点数量,这个方法了很容易的采用如下的方式实现:
public final int getChildCount() {return getChildren().size();} 该方法的出现使得对于子结点的延迟加载变的可行。该方法的实现只需要返回该节点的子结点数量,而不需要返回每个子结点的实例。 boolean isLeaf() 方法在该节点没有子节点的时候返回true。这样,一个很直截了当的实现可以这样:
public final boolean isLeaf() {return getChildren().isEmpty();} 不管你提供了什么样的实现,在任何时间任何情况下你都得保持getChildren().isEmpty() ==> isLeaf()。isLeaf()方法实际上控制了节点被怎样呈现:是否被当做树叶节点(不能被继续展开)。 String getType() 方法决定了用怎样的方式来呈现这个节点。在 JSF 页面中,可以在 <t:tree2> 的Tag 中嵌套facet,JSF将会选出与 getType() 方法返回值同名的 facet 用于呈现。如果该节点没有找到相符的 facet,将会导致一个错误,并且这个方法不会返回null。 List getChildren() 方法返回一个 List,其中包含了该节点下所有的 TreeNode,这就表示这些节点将被呈现为该节点下的子结点。该 List 不能包含 null,如果该 List 的大小和getChildCount()不符,将会报错。子结点将按照它们在 List 中的顺序呈现出来。 改变Tree中的内容 (当展开树的节点时,在后台延迟加载) 在邮件列表中有很多关于这项任务的问题和讨论,我(Marcel,一个 JSF 的初学者)在这里总结一下。如果你有更好的解决方案,请更新这些文字。 在这里存在的一个问题就是我要这样把“+”图标去掉: · <t:tree2 ... showNav="false" ...> 然后再让文件夹图标(代表包含子节点的节点)变的可点击: · <h:commandLink action="#{t.toggleExpanded}" actionListener="#{navigationBacker.processAction}"> 然后在 Java 代码中接受鼠标点击的事件。在 NavigationBacker.java 文件中的processAction(ActionEvent e) 方法里,我从 EJB3-persistency 中加载子结点的数据。 不好的是“+”图标变的不可见,但是我现在没有办法获取点击“+”图标的事件。 看起来在org.apache.myfaces.custom.tree2.HtmlTree.java这个文件里是通过注册了_expandControl = new HtmlCommandLink(); 从内部获取“+”的点击事件,但是我现在没有办法从我的代码中接受到这一事件。 为了导航,我使用了含有entries的TreeNode.getIdentifier() (参见:#{node.identifier}),看起来就是这个样子: · db_id="car_id=7,person_id=2" 这代表了后台数据库表的主键(我还没有找到一个更好的解决方案用于导航) 程序代码如下: navigation.jsp
<t:tree2 id="serverTree" value="#{navigationBacker.treeData}"var="node" varNodeToggler="t" clientSideToggle="false" showNav="false"showRootNode="false"><f:facet name="project-folder"><h:panelGroup><h:commandLink action="#{t.toggleExpanded}" actionListener="#{navigationBacker.processAction}"><t:graphicImage value="/images/yellow-folder-open.png"rendered="#{t.nodeExpanded}" border="0" /><t:graphicImage value="/images/yellow-folder-closed.png"rendered="#{!t.nodeExpanded}" border="0" /></h:commandLink><h:commandLink action="#{navigationBacker.toViewId}"styleClass="#{t.nodeSelected ? 'documentSelected':'document'}"actionListener="#{navigationBacker.nodeClicked}"value="#{node.description}" immediate="true"><f:param name="db_id" value="#{node.identifier}" /></h:commandLink><h:outputText value=" (#{node.childCount})" styleClass="childCount"rendered="#{!empty node.children}" /></h:panelGroup></f:facet><f:facet name="person-folder"><h:panelGroup> NavigationBacker.java
/** *//*** 拦截节点被展开的事件,并加载额外的数据* @param event* @throws AbortProcessingException*/public void processAction(ActionEvent event) throws AbortProcessingException {System.out.println("Entering processAction()");UIComponent component = (UIComponent) event.getSource();while (!(component != null && component instanceof HtmlTree)) { component = component.getParent();}if (component != null) { HtmlTree tree = (HtmlTree) component; TreeNodeBase node = (TreeNodeBase) tree.getNode(); if (!tree.isNodeExpanded() && node.getChildren().size() == 0) { Map<String, String> map = splitKeyValues(node.getIdentifier()); // 一些辅助代码,用于将 "car_id=7" 或 "car_id=7&person_id=12" 拆分开 this.car_id = map.get("car_id"); if (this.car_id != null) { appendPersonsNodes(node); // 参见下面的例子 } this.person_id = map.get("person_id"); if (this.person_id != null) { appendLicensesNodes(node); // 没有显示 } }}}/** *//** 把当前car_id下的Person子结点加入导航中 */private void appendPersonsNodes(TreeNodeBase carDetailNode) { VariableResolver resolver = FacesContext.getCurrentInstance().getApplication().getVariableResolver(); PersonsTable personsTable = (PersonsTable) resolver.resolveVariable(FacesContext.getCurrentInstance(),"personsTable"); List<Person> personsList = personsTable.getCarPersons(); for (Person o : personsList) { List<TreeNodeBase> list = carDetailNode.getChildren(); list.add(new TreeNodeBase("person-folder", o.getDescription(),"person_id=" o.getPersonId(), true)); } System.out.println("NavigationBacker fetched " personsList.size() " Persons for carId=" this.car_id);} 这里有一段辅助代码用于从 h:commandLink 中获取 f:param 用于多种用途。
/** *//*** 当 JSF 组件 h:commandLink 包含有 f:param 成员, 这些 name-value 对被放到 * request 参数表中供后面的action handler使用。不幸的是,这样的用法不能用在* h:commandButton上。我们没有办法把通过 button 来传递这些参数。** 因为 Action Listeners 可以保证在 Action 方法前被执行到,所以 Action Listeners * 可以调用该方法更新 Action 方法所需要的任何上下文。** From * sakai2/gradebook/tool/src/java/org/sakaiproject/tool/gradebook/jsf/FacesUtil.java* Educational Community License Version 1.0*/public static final Map getEventParameterMap(FacesEvent event) { Map<String, String> parameterMap = new HashMap<String, String>(); List children = event.getComponent().getChildren(); for (Iterator iter = children.iterator(); iter.hasNext();) { Object next = iter.next(); if (next instanceof UIParameter) { UIParameter param = (UIParameter) next; parameterMap.put(param.getName(), "" param.getValue()); } } //System.out.println("parameterMap=" parameterMap); return parameterMap;} 注:在上面的例子里,backing bean都存放于 session 作用域里,可以在WEB-INF/examples-config.xml 中进行配置。
Tree2组件使用HTML表格将你的数据呈现为一个树。这个树是动态的:当用户点击它们时它们可以展开或者折叠。该组件同时支持客户端和服务端的交互方式,在客户端交互时使用了JavaScript。在随后的例子中,每次用户的点击将产生一个Request / Response 周期,并在新的视图状态(View State)中重新呈现新的树结构。 注:在后面的例子中只有可见的(已经展开的节点)数据被传送到客户端。而在第一个例子(客户端Tree),在每个HTML Response中,整个树都被发送到客户端浏览器。树的每个节点都包含了不少的HTML代码(假定每个节点200个字符,这个大小将取决于你希望在节点上显示的信息的量),这些信息将被传送到浏览器,其中包括了那些不可见的节点(没有展开的节点),因为它们的一个祖系节点被展开。如果你有一个深度有四层的树,平均每个结点拥有四个子结点,这时候你就需要传输10 102 103 104 = 11 110个节点,每个节点有200个字符,这个树总共就有2 222 000个字符,也就是2M的数据。这个例子将向用户说明,虽然纯客户端Tree会给客户端带来更好的用户体验,但随之而来的带宽问题迅速的增长。纯客户端的树适用于小型的树,或者在Intranet及宽带连接中使用的中型大小的树。对于大型的树,或者你需要照顾到一些低带宽的用户的需要时,你就需要使用服务端树。你可以通过的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2>的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2 clientSideToggle="false" ...>将会使用服务端的树,属性值设为true将会使用客户端的树,默认值为true。 Backing Bean: Tree2组件对Backing Bean中的一个TreeModel进行操作。通常情况,你只要把这个TreeModel绑定到这个组件上就可以了,就像这样:
<t:tree2 value="#{myHandler.treeModel}" 下面需要建立一个类MyHandler,在faces-config.xml中的managed bean配置成myHandler,在例子中这个类提供了一个方法getTreeModel()用于返回一个TreeModel用于表示你的数据。
public class MyHandler { public TreeModel getTreeModel() { ...... }} TreeModel实际上是对TreeNode实例进行了一些简单的包装。 TreeNode是一个接口,其中和tree2相关有四个方法:
String getType()boolean isLeaf()List getChildren()int getChildCount() 其它方法都没有什么用处了,可能会在今后的版本中取消。它们要求开发者在开发backing bean中做一些并不必要的操作。 int getChildCount() 方法返回这个节点的子结点数量,这个方法了很容易的采用如下的方式实现:
public final int getChildCount() {return getChildren().size();} 该方法的出现使得对于子结点的延迟加载变的可行。该方法的实现只需要返回该节点的子结点数量,而不需要返回每个子结点的实例。 boolean isLeaf() 方法在该节点没有子节点的时候返回true。这样,一个很直截了当的实现可以这样:
public final boolean isLeaf() {return getChildren().isEmpty();} 不管你提供了什么样的实现,在任何时间任何情况下你都得保持getChildren().isEmpty() ==> isLeaf()。isLeaf()方法实际上控制了节点被怎样呈现:是否被当做树叶节点(不能被继续展开)。 String getType() 方法决定了用怎样的方式来呈现这个节点。在 JSF 页面中,可以在 <t:tree2> 的Tag 中嵌套facet,JSF将会选出与 getType() 方法返回值同名的 facet 用于呈现。如果该节点没有找到相符的 facet,将会导致一个错误,并且这个方法不会返回null。 List getChildren() 方法返回一个 List,其中包含了该节点下所有的 TreeNode,这就表示这些节点将被呈现为该节点下的子结点。该 List 不能包含 null,如果该 List 的大小和getChildCount()不符,将会报错。子结点将按照它们在 List 中的顺序呈现出来。 改变Tree中的内容 (当展开树的节点时,在后台延迟加载) 在邮件列表中有很多关于这项任务的问题和讨论,我(Marcel,一个 JSF 的初学者)在这里总结一下。如果你有更好的解决方案,请更新这些文字。 在这里存在的一个问题就是我要这样把“+”图标去掉: · <t:tree2 ... showNav="false" ...> 然后再让文件夹图标(代表包含子节点的节点)变的可点击: · <h:commandLink action="#{t.toggleExpanded}" actionListener="#{navigationBacker.processAction}"> 然后在 Java 代码中接受鼠标点击的事件。在 NavigationBacker.java 文件中的processAction(ActionEvent e) 方法里,我从 EJB3-persistency 中加载子结点的数据。 不好的是“+”图标变的不可见,但是我现在没有办法获取点击“+”图标的事件。 看起来在org.apache.myfaces.custom.tree2.HtmlTree.java这个文件里是通过注册了_expandControl = new HtmlCommandLink(); 从内部获取“+”的点击事件,但是我现在没有办法从我的代码中接受到这一事件。 为了导航,我使用了含有entries的TreeNode.getIdentifier() (参见:#{node.identifier}),看起来就是这个样子: · db_id="car_id=7,person_id=2" 这代表了后台数据库表的主键(我还没有找到一个更好的解决方案用于导航) 程序代码如下: navigation.jsp
<t:tree2 id="serverTree" value="#{navigationBacker.treeData}"var="node" varNodeToggler="t" clientSideToggle="false" showNav="false"showRootNode="false"><f:facet name="project-folder"><h:panelGroup><h:commandLink action="#{t.toggleExpanded}" actionListener="#{navigationBacker.processAction}"><t:graphicImage value="/images/yellow-folder-open.png"rendered="#{t.nodeExpanded}" border="0" /><t:graphicImage value="/images/yellow-folder-closed.png"rendered="#{!t.nodeExpanded}" border="0" /></h:commandLink><h:commandLink action="#{navigationBacker.toViewId}"styleClass="#{t.nodeSelected ? 'documentSelected':'document'}"actionListener="#{navigationBacker.nodeClicked}"value="#{node.description}" immediate="true"><f:param name="db_id" value="#{node.identifier}" /></h:commandLink><h:outputText value=" (#{node.childCount})" styleClass="childCount"rendered="#{!empty node.children}" /></h:panelGroup></f:facet><f:facet name="person-folder"><h:panelGroup> NavigationBacker.java
/** *//*** 拦截节点被展开的事件,并加载额外的数据* @param event* @throws AbortProcessingException*/public void processAction(ActionEvent event) throws AbortProcessingException {System.out.println("Entering processAction()");UIComponent component = (UIComponent) event.getSource();while (!(component != null && component instanceof HtmlTree)) { component = component.getParent();}if (component != null) { HtmlTree tree = (HtmlTree) component; TreeNodeBase node = (TreeNodeBase) tree.getNode(); if (!tree.isNodeExpanded() && node.getChildren().size() == 0) { Map<String, String> map = splitKeyValues(node.getIdentifier()); // 一些辅助代码,用于将 "car_id=7" 或 "car_id=7&person_id=12" 拆分开 this.car_id = map.get("car_id"); if (this.car_id != null) { appendPersonsNodes(node); // 参见下面的例子 } this.person_id = map.get("person_id"); if (this.person_id != null) { appendLicensesNodes(node); // 没有显示 } }}}/** *//** 把当前car_id下的Person子结点加入导航中 */private void appendPersonsNodes(TreeNodeBase carDetailNode) { VariableResolver resolver = FacesContext.getCurrentInstance().getApplication().getVariableResolver(); PersonsTable personsTable = (PersonsTable) resolver.resolveVariable(FacesContext.getCurrentInstance(),"personsTable"); List<Person> personsList = personsTable.getCarPersons(); for (Person o : personsList) { List<TreeNodeBase> list = carDetailNode.getChildren(); list.add(new TreeNodeBase("person-folder", o.getDescription(),"person_id=" o.getPersonId(), true)); } System.out.println("NavigationBacker fetched " personsList.size() " Persons for carId=" this.car_id);} 这里有一段辅助代码用于从 h:commandLink 中获取 f:param 用于多种用途。
/** *//*** 当 JSF 组件 h:commandLink 包含有 f:param 成员, 这些 name-value 对被放到 * request 参数表中供后面的action handler使用。不幸的是,这样的用法不能用在* h:commandButton上。我们没有办法把通过 button 来传递这些参数。** 因为 Action Listeners 可以保证在 Action 方法前被执行到,所以 Action Listeners * 可以调用该方法更新 Action 方法所需要的任何上下文。** From * sakai2/gradebook/tool/src/java/org/sakaiproject/tool/gradebook/jsf/FacesUtil.java* Educational Community License Version 1.0*/public static final Map getEventParameterMap(FacesEvent event) { Map<String, String> parameterMap = new HashMap<String, String>(); List children = event.getComponent().getChildren(); for (Iterator iter = children.iterator(); iter.hasNext();) { Object next = iter.next(); if (next instanceof UIParameter) { UIParameter param = (UIParameter) next; parameterMap.put(param.getName(), "" param.getValue()); } } //System.out.println("parameterMap=" parameterMap); return parameterMap;} 注:在上面的例子里,backing bean都存放于 session 作用域里,可以在WEB-INF/examples-config.xml 中进行配置。
下载本文示例代码
深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法深入讨论JSF中Tree2组件使用方法
阅读(153) | 评论(0) | 转发(0) |