jsf树型菜单.docx
《jsf树型菜单.docx》由会员分享,可在线阅读,更多相关《jsf树型菜单.docx(12页珍藏版)》请在冰豆网上搜索。
jsf树型菜单
使用JSF中的Sidebar>
Sidebar>
Sidebar>实现动态加载菜单,其中bar.getChildren().add(aSidebarGroup);是实现动态加载首节点,HtmlSidebarGroupaSidebarGroup=(HtmlSidebarGroup)application.createComponent(HtmlSidebarGroup.COMPONENT_TYPE);
aSidebarGroup.setText(boardName);
aSidebarGroup.setExpanded(true);
aSidebarGroup.setId(viewRoot.createUniqueId());实现加载该节点下的子节点,非常方便。
privateSidebarbuildSideBar(Sidebarbar)
{
booleanisInitialised=bar.getAttributes().get("IS_INITIALISED")!
=null;
if(!
isInitialised){
//GetareferencetotheJSFApplication,
//weneedittocreatevaluebindingslater-on
Applicationapplication=FacesContext.getCurrentInstance().getApplication();
UIViewRootviewRoot=FacesContext.getCurrentInstance().getViewRoot();
/*CreateInfragisticsSidebarGroup*/
//SettheSidebar'swidthandheight
bar.getAttributes().put("style","width:
155px;height:
590px;");
//CreateSidebarGroup
Listlist=this.getBoardNameForMenu();
for(inti=0;i { Object[]row=(Object[])list.get(i); //Object[]row=(Object[])list.listIterator().next(); StringboardId=String.valueOf(row[0]); StringboardName=String.valueOf(row[1]); StringparentId=String.valueOf(row[2]); HtmlSidebarGroupaSidebarGroup=(HtmlSidebarGroup)application.createComponent(HtmlSidebarGroup.COMPONENT_TYPE); aSidebarGroup.setText(boardName); aSidebarGroup.setExpanded(true); aSidebarGroup.setId(viewRoot.createUniqueId()); //AddthenewlycreatedSidebarGrouptotheSidebar'schildrenlist bar.getChildren().add(aSidebarGroup); Listls=this.getBoardNameForMenuBar(Integer.parseInt(boardId)); if(ls.size()!=0) { for(intj=0;j { Object[]row1=(Object[])ls.get(j); Stringboardid=String.valueOf(row1[0]); Stringboardname=String.valueOf(row1[1]); //HtmlSidebarGroupaSidebarGroup=(HtmlSidebarGroup)application.createComponent(HtmlSidebarGroup.COMPONENT_TYPE); HtmlLinkaLink1=(HtmlLink)application.createComponent(HtmlLink.COMPONENT_TYPE); aLink1.setId(viewRoot.createUniqueId()); //aSidebarGroup.setText(sidebarGroupNameStr); //aSidebarGroup.setExpanded(true); //aSidebarGroup.setId(viewRoot.createUniqueId()); //aLink1.setId(parentId); aLink1.setUrl("/pages/bbs/visitor/topic/topicforboard.jsf?boardid="+boardid+""); aLink1.setValue(boardname); aSidebarGroup.getChildren().add(aLink1); } } }深入讨论JSF中Tree2组件使用方法2006-01-1608:48作者:steady出处:Java博客责任编辑:方舟 Tree2组件使用HTML表格将你的数据呈现为一个树。这个树是动态的:当用户点击它们时它们可以展开或者折叠。该组件同时支持客户端和服务端的交互方式,在客户端交互时使用了JavaScript。在随后的例子中,每次用户的点击将产生一个Request/Response周期,并在新的视图状态(ViewState)中重新呈现新的树结构。 注:在后面的例子中只有可见的(已经展开的节点)数据被传送到客户端。而在第一个例子(客户端Tree),在每个HTMLResponse中,整个树都被发送到客户端浏览器。树的每个节点都包含了不少的HTML代码(假定每个节点200个字符,这个大小将取决于你希望在节点上显示的信息的量),这些信息将被传送到浏览器,其中包括了那些不可见的节点(没有展开的节点),因为它们的一个祖系节点被展开。如果你有一个深度有四层的树,平均每个结点拥有四个子结点,这时候你就需要传输10+102+103+104=11110个节点,每个节点有200个字符,这个树总共就有2222000个字符,也就是2M的数据。这个例子将向用户说明,虽然纯客户端Tree会给客户端带来更好的用户体验,但随之而来的带宽问题迅速的增长。纯客户端的树适用于小型的树,或者在Intranet及宽带连接中使用的中型大小的树。对于大型的树,或者你需要照顾到一些低带宽的用户的需要时,你就需要使用服务端树。你可以通过的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2>的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2clientSideToggle="false"...>将会使用服务端的树,属性值设为true将会使用客户端的树,默认值为true。 BackingBean: Tree2组件对BackingBean中的一个TreeModel进行操作。通常情况,你只要把这个TreeModel绑定到这个组件上就可以了,就像这样:<t:tree2value="#{myHandler.treeModel}" 下面需要建立一个类MyHandler,在faces-config.xml中的managedbean配置成myHandler,在例子中这个类提供了一个方法getTreeModel()用于返回一个TreeModel用于表示你的数据。publicclassMyHandler{ publicTreeModelgetTreeModel(){ ...... }} TreeModel实际上是对TreeNode实例进行了一些简单的包装。 TreeNode是一个接口,其中和tree2相关有四个方法:StringgetType()booleanisLeaf()ListgetChildren()intgetChildCount() 其它方法都没有什么用处了,可能会在今后的版本中取消。它们要求开发者在开发backingbean中做一些并不必要的操作。 intgetChildCount()方法返回这个节点的子结点数量,这个方法了很容易的采用如下的方式实现:publicfinalintgetChildCount(){returngetChildren().size();} 该方法的出现使得对于子结点的延迟加载变的可行。该方法的实现只需要返回该节点的子结点数量,而不需要返回每个子结点的实例。 booleanisLeaf()方法在该节点没有子节点的时候返回true。这样,一个很直截了当的实现可以这样:publicfinalbooleanisLeaf(){returngetChildren().isEmpty();} 不管你提供了什么样的实现,在任何时间任何情况下你都得保持getChildren().isEmpty()==>isLeaf()。isLeaf()方法实际上控制了节点被怎样呈现:是否被当做树叶节点(不能被继续展开)。 StringgetType()方法决定了用怎样的方式来呈现这个节点。在JSF页面中,可以在<t:tree2>的Tag中嵌套facet,JSF将会选出与getType()方法返回值同名的facet用于呈现。如果该节点没有找到相符的facet,将会导致一个错误,并且这个方法不会返回null。 ListgetChildren()方法返回一个List,其中包含了该节点下所有的TreeNode,这就表示这些节点将被呈现为该节点下的子结点。该List不能包含null,如果该List的大小和getChildCount()不符,将会报错。子结点将按照它们在List中的顺序呈现出来。 改变Tree中的内容 (当展开树的节点时,在后台延迟加载) 在邮件列表中有很多关于这项任务的问题和讨论,我(Marcel,一个JSF的初学者)在这里总结一下。如果你有更好的解决方案,请更新这些文字。 在这里存在的一个问题就是我要这样把“+”图标去掉: ·<t:tree2...showNav="false"...> 然后再让文件夹图标(代表包含子节点的节点)变的可点击: ·<h:commandLinkaction="#{t.toggleExpanded}"actionListener="#{navigationBacker.processAction}"> 然后在Java代码中接受鼠标点击的事件。在NavigationBacker.java文件中的processAction(ActionEvente)方法里,我从EJB3-persistency中加载子结点的数据。 不好的是“+”图标变的不可见,但是我现在没有办法获取点击“+”图标的事件。 看起来在org.apache.myfaces.custom.tree2.HtmlTree.java这个文件里是通过注册了_expandControl=newHtmlCommandLink();从内部获取“+”的点击事件,但是我现在没有办法从我的代码中接受到这一事件。 为了导航,我使用了含有entries的TreeNode.getIdentifier()(参见:#{node.identifier}),看起来就是这个样子: ·db_id="car_id=7,person_id=2" 这代表了后台数据库表的主键(我还没有找到一个更好的解决方案用于导航) 程序代码如下: navigation.jsp<t:tree2id="serverTree"value="#{navigationBacker.treeData}"var="node"varNodeToggler="t"clientSideToggle="false"showNav="false"showRootNode="false"><f:facetname="project-folder"><h:panelGroup><h:commandLinkaction="#{t.toggleExpanded}"actionListener="#{navigationBacker.processAction}"><t:graphicImagevalue="/images/yellow-folder-open.png"rendered="#{t.nodeExpanded}"border="0"/><t:graphicImagevalue="/images/yellow-folder-closed.png"rendered="#{!t.nodeExpanded}"border="0"/></h:commandLink><h:commandLinkaction="#{navigationBacker.toViewId}"styleClass="#{t.nodeSelected?'documentSelected':'document'}"actionListener="#{navigationBacker.nodeClicked}"value="#{node.description}"immediate="true"><f:paramname="db_id"value="#{node.identifier}"/></h:commandLink><h:outputTextvalue="(#{node.childCount})"styleClass="childCount"rendered="#{!emptynode.children}"/></h:panelGroup></f:facet><f:facetname="person-folder"><h:panelGroup> NavigationBacker.java/***//***拦截节点被展开的事件,并加载额外的数据*@paramevent*@throwsAbortProcessingException*/publicvoidprocessAction(ActionEventevent)throwsAbortProcessingException{System.out.println("EnteringprocessAction()");UIComponentcomponent=(UIComponent)event.getSource();while(!(component!=null&&componentinstanceofHtmlTree)){ component=component.getParent();}if(component!=null){ HtmlTreetree=(HtmlTree)component; TreeNodeBasenode=(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子结点加入导航中*/privatevoidappendPersonsNodes(TreeNodeBasecarDetailNode){ VariableResolverresolver=FacesContext.getCurrentInstance().getApplication().getVariableResolver(); PersonsTablepersonsTable=(PersonsTable)resolver.resolveVariable(FacesContext.getCurrentInstance(),"personsTable"); List<Person>personsList=personsTable.getCarPersons(); for(Persono:personsList){ List<TreeNodeBase>list=carDetailNode.getChildren(); list.add(newTreeNodeBase("person-folder",o.getDescription(),"person_id="+o.getPersonId(),true)); } System.out.println("NavigationBackerfetched"+personsList.size()+"PersonsforcarId="+this.car_id);} 这里有一段辅助代码用于从h:commandLink中获取f:param用于多种用途。/***//***当JSF组件h:commandLink包含有f:param成员,这些name-value对被放到*request参数表中供后面的actionhandler使用。不幸的是,这样的用法不能用在*h:commandButton上。我们没有办法把通过button来传递这些参数。**因为ActionListeners可以保证在Action方法前被执行到,所以ActionListeners*可以调用该方法更新Action方法所需要的任何上下文。**Fromhttp://cvs.sakaiproject.org/release/2.0.0/*sakai2/gradebook/tool/src/java/org/sakaiproject/tool/gradebook/jsf/FacesUtil.java*EducationalCommunityLicenseVersion1.0*/publicstaticfinalMapgetEventParameterMap(FacesEventevent){ Map<String,String>parameterMap=newHashMap<String,String>(); Listchildren=event.getComponent().getChildren(); for(Iteratoriter=children.iterator();iter.hasNext();){ Objectnext=iter.next(); if(nextinstanceofUIParameter){ UIParameterparam=(UIParameter)next; parameterMap.put(param.getName(),""+param.getValue()); } } //System.out.println("parameterMap="+parameterMap); returnparameterMap;}TreeNodeBase的构造方法有三种:TreeNodeBase()TreeNodeBase(java.lang.Stringtype,java.lang.Stringdescription,booleanleaf)TreeNodeBase(java.lang.Stringtype,java.lang.Stringdescription,java.lang.Stringidentifier,booleanleaf)最后一个参数表示是否是未级节点。把XHTML中的代码与bean中的方法和构造方法联系起来看就比较容易了。第一个参数是type,也就是facetname="root">对应的如何渲染的部分;第二个参数是description,也就是tree要显示的内容;第三个参数是treeNode的ID;第四个参数表示是否有下级节点 1.前言在JSF的项目开发中,用到树形菜单时可以考虑使用myfacs实现的tree2组件。网上已经有很多文章对tree2进行介绍,我这里主要对一些重点进行描述,提醒使用者一些注意事项。2.Tree2的组件您可以到http://myfaces.apache.org/tomahawk下载tomahawk包,放到项目的lib路径中.我这里使用的是tomahawk-1.1.5.jar.然后再页面头部加上下面一行:<%@tagliburi="http://myfaces.apache.org/tomahawk"prefix="t"%>3.编写JS
Object[]row=(Object[])list.get(i);
//Object[]row=(Object[])list.listIterator().next();
StringboardId=String.valueOf(row[0]);
StringboardName=String.valueOf(row[1]);
StringparentId=String.valueOf(row[2]);
HtmlSidebarGroupaSidebarGroup=(HtmlSidebarGroup)application.createComponent(HtmlSidebarGroup.COMPONENT_TYPE);
aSidebarGroup.setId(viewRoot.createUniqueId());
//AddthenewlycreatedSidebarGrouptotheSidebar'schildrenlist
bar.getChildren().add(aSidebarGroup);
Listls=this.getBoardNameForMenuBar(Integer.parseInt(boardId));
if(ls.size()!
=0)
for(intj=0;j { Object[]row1=(Object[])ls.get(j); Stringboardid=String.valueOf(row1[0]); Stringboardname=String.valueOf(row1[1]); //HtmlSidebarGroupaSidebarGroup=(HtmlSidebarGroup)application.createComponent(HtmlSidebarGroup.COMPONENT_TYPE); HtmlLinkaLink1=(HtmlLink)application.createComponent(HtmlLink.COMPONENT_TYPE); aLink1.setId(viewRoot.createUniqueId()); //aSidebarGroup.setText(sidebarGroupNameStr); //aSidebarGroup.setExpanded(true); //aSidebarGroup.setId(viewRoot.createUniqueId()); //aLink1.setId(parentId); aLink1.setUrl("/pages/bbs/visitor/topic/topicforboard.jsf?boardid="+boardid+""); aLink1.setValue(boardname); aSidebarGroup.getChildren().add(aLink1); } } }深入讨论JSF中Tree2组件使用方法2006-01-1608:48作者:steady出处:Java博客责任编辑:方舟 Tree2组件使用HTML表格将你的数据呈现为一个树。这个树是动态的:当用户点击它们时它们可以展开或者折叠。该组件同时支持客户端和服务端的交互方式,在客户端交互时使用了JavaScript。在随后的例子中,每次用户的点击将产生一个Request/Response周期,并在新的视图状态(ViewState)中重新呈现新的树结构。 注:在后面的例子中只有可见的(已经展开的节点)数据被传送到客户端。而在第一个例子(客户端Tree),在每个HTMLResponse中,整个树都被发送到客户端浏览器。树的每个节点都包含了不少的HTML代码(假定每个节点200个字符,这个大小将取决于你希望在节点上显示的信息的量),这些信息将被传送到浏览器,其中包括了那些不可见的节点(没有展开的节点),因为它们的一个祖系节点被展开。如果你有一个深度有四层的树,平均每个结点拥有四个子结点,这时候你就需要传输10+102+103+104=11110个节点,每个节点有200个字符,这个树总共就有2222000个字符,也就是2M的数据。这个例子将向用户说明,虽然纯客户端Tree会给客户端带来更好的用户体验,但随之而来的带宽问题迅速的增长。纯客户端的树适用于小型的树,或者在Intranet及宽带连接中使用的中型大小的树。对于大型的树,或者你需要照顾到一些低带宽的用户的需要时,你就需要使用服务端树。你可以通过的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2>的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:tree2clientSideToggle="false"...>将会使用服务端的树,属性值设为true将会使用客户端的树,默认值为true。 BackingBean: Tree2组件对BackingBean中的一个TreeModel进行操作。通常情况,你只要把这个TreeModel绑定到这个组件上就可以了,就像这样:<t:tree2value="#{myHandler.treeModel}" 下面需要建立一个类MyHandler,在faces-config.xml中的managedbean配置成myHandler,在例子中这个类提供了一个方法getTreeModel()用于返回一个TreeModel用于表示你的数据。publicclassMyHandler{ publicTreeModelgetTreeModel(){ ...... }} TreeModel实际上是对TreeNode实例进行了一些简单的包装。 TreeNode是一个接口,其中和tree2相关有四个方法:StringgetType()booleanisLeaf()ListgetChildren()intgetChildCount() 其它方法都没有什么用处了,可能会在今后的版本中取消。它们要求开发者在开发backingbean中做一些并不必要的操作。 intgetChildCount()方法返回这个节点的子结点数量,这个方法了很容易的采用如下的方式实现:publicfinalintgetChildCount(){returngetChildren().size();} 该方法的出现使得对于子结点的延迟加载变的可行。该方法的实现只需要返回该节点的子结点数量,而不需要返回每个子结点的实例。 booleanisLeaf()方法在该节点没有子节点的时候返回true。这样,一个很直截了当的实现可以这样:publicfinalbooleanisLeaf(){returngetChildren().isEmpty();} 不管你提供了什么样的实现,在任何时间任何情况下你都得保持getChildren().isEmpty()==>isLeaf()。isLeaf()方法实际上控制了节点被怎样呈现:是否被当做树叶节点(不能被继续展开)。 StringgetType()方法决定了用怎样的方式来呈现这个节点。在JSF页面中,可以在<t:tree2>的Tag中嵌套facet,JSF将会选出与getType()方法返回值同名的facet用于呈现。如果该节点没有找到相符的facet,将会导致一个错误,并且这个方法不会返回null。 ListgetChildren()方法返回一个List,其中包含了该节点下所有的TreeNode,这就表示这些节点将被呈现为该节点下的子结点。该List不能包含null,如果该List的大小和getChildCount()不符,将会报错。子结点将按照它们在List中的顺序呈现出来。 改变Tree中的内容 (当展开树的节点时,在后台延迟加载) 在邮件列表中有很多关于这项任务的问题和讨论,我(Marcel,一个JSF的初学者)在这里总结一下。如果你有更好的解决方案,请更新这些文字。 在这里存在的一个问题就是我要这样把“+”图标去掉: ·<t:tree2...showNav="false"...> 然后再让文件夹图标(代表包含子节点的节点)变的可点击: ·<h:commandLinkaction="#{t.toggleExpanded}"actionListener="#{navigationBacker.processAction}"> 然后在Java代码中接受鼠标点击的事件。在NavigationBacker.java文件中的processAction(ActionEvente)方法里,我从EJB3-persistency中加载子结点的数据。 不好的是“+”图标变的不可见,但是我现在没有办法获取点击“+”图标的事件。 看起来在org.apache.myfaces.custom.tree2.HtmlTree.java这个文件里是通过注册了_expandControl=newHtmlCommandLink();从内部获取“+”的点击事件,但是我现在没有办法从我的代码中接受到这一事件。 为了导航,我使用了含有entries的TreeNode.getIdentifier()(参见:#{node.identifier}),看起来就是这个样子: ·db_id="car_id=7,person_id=2" 这代表了后台数据库表的主键(我还没有找到一个更好的解决方案用于导航) 程序代码如下: navigation.jsp<t:tree2id="serverTree"value="#{navigationBacker.treeData}"var="node"varNodeToggler="t"clientSideToggle="false"showNav="false"showRootNode="false"><f:facetname="project-folder"><h:panelGroup><h:commandLinkaction="#{t.toggleExpanded}"actionListener="#{navigationBacker.processAction}"><t:graphicImagevalue="/images/yellow-folder-open.png"rendered="#{t.nodeExpanded}"border="0"/><t:graphicImagevalue="/images/yellow-folder-closed.png"rendered="#{!t.nodeExpanded}"border="0"/></h:commandLink><h:commandLinkaction="#{navigationBacker.toViewId}"styleClass="#{t.nodeSelected?'documentSelected':'document'}"actionListener="#{navigationBacker.nodeClicked}"value="#{node.description}"immediate="true"><f:paramname="db_id"value="#{node.identifier}"/></h:commandLink><h:outputTextvalue="(#{node.childCount})"styleClass="childCount"rendered="#{!emptynode.children}"/></h:panelGroup></f:facet><f:facetname="person-folder"><h:panelGroup> NavigationBacker.java/***//***拦截节点被展开的事件,并加载额外的数据*@paramevent*@throwsAbortProcessingException*/publicvoidprocessAction(ActionEventevent)throwsAbortProcessingException{System.out.println("EnteringprocessAction()");UIComponentcomponent=(UIComponent)event.getSource();while(!(component!=null&&componentinstanceofHtmlTree)){ component=component.getParent();}if(component!=null){ HtmlTreetree=(HtmlTree)component; TreeNodeBasenode=(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子结点加入导航中*/privatevoidappendPersonsNodes(TreeNodeBasecarDetailNode){ VariableResolverresolver=FacesContext.getCurrentInstance().getApplication().getVariableResolver(); PersonsTablepersonsTable=(PersonsTable)resolver.resolveVariable(FacesContext.getCurrentInstance(),"personsTable"); List<Person>personsList=personsTable.getCarPersons(); for(Persono:personsList){ List<TreeNodeBase>list=carDetailNode.getChildren(); list.add(newTreeNodeBase("person-folder",o.getDescription(),"person_id="+o.getPersonId(),true)); } System.out.println("NavigationBackerfetched"+personsList.size()+"PersonsforcarId="+this.car_id);} 这里有一段辅助代码用于从h:commandLink中获取f:param用于多种用途。/***//***当JSF组件h:commandLink包含有f:param成员,这些name-value对被放到*request参数表中供后面的actionhandler使用。不幸的是,这样的用法不能用在*h:commandButton上。我们没有办法把通过button来传递这些参数。**因为ActionListeners可以保证在Action方法前被执行到,所以ActionListeners*可以调用该方法更新Action方法所需要的任何上下文。**Fromhttp://cvs.sakaiproject.org/release/2.0.0/*sakai2/gradebook/tool/src/java/org/sakaiproject/tool/gradebook/jsf/FacesUtil.java*EducationalCommunityLicenseVersion1.0*/publicstaticfinalMapgetEventParameterMap(FacesEventevent){ Map<String,String>parameterMap=newHashMap<String,String>(); Listchildren=event.getComponent().getChildren(); for(Iteratoriter=children.iterator();iter.hasNext();){ Objectnext=iter.next(); if(nextinstanceofUIParameter){ UIParameterparam=(UIParameter)next; parameterMap.put(param.getName(),""+param.getValue()); } } //System.out.println("parameterMap="+parameterMap); returnparameterMap;}TreeNodeBase的构造方法有三种:TreeNodeBase()TreeNodeBase(java.lang.Stringtype,java.lang.Stringdescription,booleanleaf)TreeNodeBase(java.lang.Stringtype,java.lang.Stringdescription,java.lang.Stringidentifier,booleanleaf)最后一个参数表示是否是未级节点。把XHTML中的代码与bean中的方法和构造方法联系起来看就比较容易了。第一个参数是type,也就是facetname="root">对应的如何渲染的部分;第二个参数是description,也就是tree要显示的内容;第三个参数是treeNode的ID;第四个参数表示是否有下级节点 1.前言在JSF的项目开发中,用到树形菜单时可以考虑使用myfacs实现的tree2组件。网上已经有很多文章对tree2进行介绍,我这里主要对一些重点进行描述,提醒使用者一些注意事项。2.Tree2的组件您可以到http://myfaces.apache.org/tomahawk下载tomahawk包,放到项目的lib路径中.我这里使用的是tomahawk-1.1.5.jar.然后再页面头部加上下面一行:<%@tagliburi="http://myfaces.apache.org/tomahawk"prefix="t"%>3.编写JS
Object[]row1=(Object[])ls.get(j);
Stringboardid=String.valueOf(row1[0]);
Stringboardname=String.valueOf(row1[1]);
//HtmlSidebarGroupaSidebarGroup=(HtmlSidebarGroup)application.createComponent(HtmlSidebarGroup.COMPONENT_TYPE);
HtmlLinkaLink1=(HtmlLink)application.createComponent(HtmlLink.COMPONENT_TYPE);
aLink1.setId(viewRoot.createUniqueId());
//aSidebarGroup.setText(sidebarGroupNameStr);
//aSidebarGroup.setExpanded(true);
//aSidebarGroup.setId(viewRoot.createUniqueId());
//aLink1.setId(parentId);
aLink1.setUrl("/pages/bbs/visitor/topic/topicforboard.jsf?
boardid="+boardid+"");
aLink1.setValue(boardname);
aSidebarGroup.getChildren().add(aLink1);
}
深入讨论JSF中Tree2组件使用方法
2006-01-1608:
48作者:
steady出处:
Java博客责任编辑:
方舟
Tree2组件使用HTML表格将你的数据呈现为一个树。
这个树是动态的:
当用户点击它们时它们可以展开或者折叠。
该组件同时支持客户端和服务端的交互方式,在客户端交互时使用了JavaScript。
在随后的例子中,每次用户的点击将产生一个Request/Response周期,并在新的视图状态(ViewState)中重新呈现新的树结构。
注:
在后面的例子中只有可见的(已经展开的节点)数据被传送到客户端。
而在第一个例子(客户端Tree),在每个HTMLResponse中,整个树都被发送到客户端浏览器。
树的每个节点都包含了不少的HTML代码(假定每个节点200个字符,这个大小将取决于你希望在节点上显示的信息的量),这些信息将被传送到浏览器,其中包括了那些不可见的节点(没有展开的节点),因为它们的一个祖系节点被展开。
如果你有一个深度有四层的树,平均每个结点拥有四个子结点,这时候你就需要传输10+102+103+104=11110个节点,每个节点有200个字符,这个树总共就有2222000个字符,也就是2M的数据。
这个例子将向用户说明,虽然纯客户端Tree会给客户端带来更好的用户体验,但随之而来的带宽问题迅速的增长。
纯客户端的树适用于小型的树,或者在Intranet及宽带连接中使用的中型大小的树。
对于大型的树,或者你需要照顾到一些低带宽的用户的需要时,你就需要使用服务端树。
你可以通过的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:
tree2>的clientSideToggle这个属性来选择你使用的是客户端的树或者服务端的树,<t:
tree2clientSideToggle="false"...>将会使用服务端的树,属性值设为true将会使用客户端的树,默认值为true。
BackingBean:
Tree2组件对BackingBean中的一个TreeModel进行操作。
通常情况,你只要把这个TreeModel绑定到这个组件上就可以了,就像这样:
<t:
tree2value="#{myHandler.treeModel}"
下面需要建立一个类MyHandler,在faces-config.xml中的managedbean配置成myHandler,在例子中这个类提供了一个方法getTreeModel()用于返回一个TreeModel用于表示你的数据。
publicclassMyHandler{
publicTreeModelgetTreeModel(){
......
TreeModel实际上是对TreeNode实例进行了一些简单的包装。
TreeNode是一个接口,其中和tree2相关有四个方法:
StringgetType()
booleanisLeaf()
ListgetChildren()
intgetChildCount()
其它方法都没有什么用处了,可能会在今后的版本中取消。
它们要求开发者在开发backingbean中做一些并不必要的操作。
intgetChildCount()方法返回这个节点的子结点数量,这个方法了很容易的采用如下的方式实现:
publicfinalintgetChildCount(){
returngetChildren().size();
该方法的出现使得对于子结点的延迟加载变的可行。
该方法的实现只需要返回该节点的子结点数量,而不需要返回每个子结点的实例。
booleanisLeaf()方法在该节点没有子节点的时候返回true。
这样,一个很直截了当的实现可以这样:
publicfinalbooleanisLeaf(){
returngetChildren().isEmpty();
不管你提供了什么样的实现,在任何时间任何情况下你都得保持getChildren().isEmpty()==>isLeaf()。
isLeaf()方法实际上控制了节点被怎样呈现:
是否被当做树叶节点(不能被继续展开)。
StringgetType()方法决定了用怎样的方式来呈现这个节点。
在JSF页面中,可以在<t:
tree2>的Tag中嵌套facet,JSF将会选出与getType()方法返回值同名的facet用于呈现。
如果该节点没有找到相符的facet,将会导致一个错误,并且这个方法不会返回null。
ListgetChildren()方法返回一个List,其中包含了该节点下所有的TreeNode,这就表示这些节点将被呈现为该节点下的子结点。
该List不能包含null,如果该List的大小和getChildCount()不符,将会报错。
子结点将按照它们在List中的顺序呈现出来。
改变Tree中的内容
(当展开树的节点时,在后台延迟加载)
在邮件列表中有很多关于这项任务的问题和讨论,我(Marcel,一个JSF的初学者)在这里总结一下。
如果你有更好的解决方案,请更新这些文字。
在这里存在的一个问题就是我要这样把“+”图标去掉:
·<t:
tree2...showNav="false"...>
然后再让文件夹图标(代表包含子节点的节点)变的可点击:
·<h:
commandLinkaction="#{t.toggleExpanded}"actionListener="#{navigationBacker.processAction}">
然后在Java代码中接受鼠标点击的事件。
在NavigationBacker.java文件中的processAction(ActionEvente)方法里,我从EJB3-persistency中加载子结点的数据。
不好的是“+”图标变的不可见,但是我现在没有办法获取点击“+”图标的事件。
看起来在org.apache.myfaces.custom.tree2.HtmlTree.java这个文件里是通过注册了_expandControl=newHtmlCommandLink();从内部获取“+”的点击事件,但是我现在没有办法从我的代码中接受到这一事件。
为了导航,我使用了含有entries的TreeNode.getIdentifier()(参见:
#{node.identifier}),看起来就是这个样子:
·db_id="car_id=7,person_id=2"
这代表了后台数据库表的主键(我还没有找到一个更好的解决方案用于导航)
程序代码如下:
navigation.jsp
tree2id="serverTree"value="#{navigationBacker.treeData}"
var="node"varNodeToggler="t"clientSideToggle="false"showNav="false"
showRootNode="false">
<f:
facetname="project-folder">
<h:
panelGroup>
graphicImagevalue="/images/yellow-folder-open.png"
rendered="#{t.nodeExpanded}"border="0"/>
graphicImagevalue="/images/yellow-folder-closed.png"
rendered="#{!
t.nodeExpanded}"border="0"/>
</h:
commandLink>
commandLinkaction="#{navigationBacker.toViewId}"
styleClass="#{t.nodeSelected?
'documentSelected':
'document'}"
actionListener="#{navigationBacker.nodeClicked}"
value="#{node.description}"immediate="true">
paramname="db_id"value="#{node.identifier}"/>
outputTextvalue="(#{node.childCount})"styleClass="childCount"
emptynode.children}"/>
</f:
facet>
facetname="person-folder">
NavigationBacker.java
/***//**
*拦截节点被展开的事件,并加载额外的数据
*@paramevent
*@throwsAbortProcessingException
*/
publicvoidprocessAction(ActionEventevent)throwsAbortProcessingException{
System.out.println("EnteringprocessAction()");
UIComponentcomponent=(UIComponent)event.getSource();
while(!
(component!
=null&&componentinstanceofHtmlTree)){
component=component.getParent();
if(component!
=null){
HtmlTreetree=(HtmlTree)component;
TreeNodeBasenode=(TreeNodeBase)tree.getNode();
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!
appendPersonsNodes(node);//参见下面的例子
this.person_id=map.get("person_id");
if(this.person_id!
appendLicensesNodes(node);//没有显示
/***//**把当前car_id下的Person子结点加入导航中*/
privatevoidappendPersonsNodes(TreeNodeBasecarDetailNode){
VariableResolverresolver=FacesContext.getCurrentInstance().getApplication().getVariableResolver();
PersonsTablepersonsTable=(PersonsTable)resolver.resolveVariable(FacesContext.getCurrentInstance(),
"personsTable");
List<Person>personsList=personsTable.getCarPersons();
for(Persono:
personsList){
List<TreeNodeBase>list=carDetailNode.getChildren();
list.add(newTreeNodeBase("person-folder",o.getDescription(),"person_id="+o.getPersonId(),true));
System.out.println("NavigationBackerfetched"+personsList.size()+"PersonsforcarId="+this.car_id);
这里有一段辅助代码用于从h:
commandLink中获取f:
param用于多种用途。
*当JSF组件h:
commandLink包含有f:
param成员,这些name-value对被放到
*request参数表中供后面的actionhandler使用。
不幸的是,这样的用法不能用在
*h:
commandButton上。
我们没有办法把通过button来传递这些参数。
*
*因为ActionListeners可以保证在Action方法前被执行到,所以ActionListeners
*可以调用该方法更新Action方法所需要的任何上下文。
*Fromhttp:
//cvs.sakaiproject.org/release/2.0.0/
*sakai2/gradebook/tool/src/java/org/sakaiproject/tool/gradebook/jsf/FacesUtil.java
*EducationalCommunityLicenseVersion1.0
publicstaticfinalMapgetEventParameterMap(FacesEventevent){
Map<String,String>parameterMap=newHashMap<String,String>();
Listchildren=event.getComponent().getChildren();
for(Iteratoriter=children.iterator();iter.hasNext();){
Objectnext=iter.next();
if(nextinstanceofUIParameter){
UIParameterparam=(UIParameter)next;
parameterMap.put(param.getName(),""+param.getValue());
//System.out.println("parameterMap="+parameterMap);
returnparameterMap;
TreeNodeBase的构造方法有三种:
TreeNodeBase()
TreeNodeBase(java.lang.Stringtype,java.lang.Stringdescription,booleanleaf)
TreeNodeBase(java.lang.Stringtype,java.lang.Stringdescription,java.lang.Stringidentifier,booleanleaf)
最后一个参数表示是否是未级节点。
把XHTML中的代码与bean中的方法和构造方法联系起来看就比较容易了。
第一个参数是type,也就是facetname="root">对应的如何渲染的部分;第二个参数是description,也就是tree要显示的内容;第三个参数是treeNode的ID;第四个参数表示是否有下级节点 1.前言在JSF的项目开发中,用到树形菜单时可以考虑使用myfacs实现的tree2组件。网上已经有很多文章对tree2进行介绍,我这里主要对一些重点进行描述,提醒使用者一些注意事项。2.Tree2的组件您可以到http://myfaces.apache.org/tomahawk下载tomahawk包,放到项目的lib路径中.我这里使用的是tomahawk-1.1.5.jar.然后再页面头部加上下面一行:<%@tagliburi="http://myfaces.apache.org/tomahawk"prefix="t"%>3.编写JS
facetname="root">对应的如何渲染的部分;第二个参数是description,也就是tree要显示的内容;第三个参数是treeNode的ID;第四个参数表示是否有下级节点
1.前言
在JSF的项目开发中,用到树形菜单时可以考虑使用myfacs实现的tree2组件。
网上已经有很多文章对tree2进行介绍,我这里主要对一些重点进行描述,提醒使用者一些注意事项。
2.Tree2的组件
您可以到http:
//myfaces.apache.org/tomahawk下载tomahawk包,放到项目的lib路径中.
我这里使用的是tomahawk-1.1.5.jar.
然后再页面头部加上下面一行:
<%@tagliburi="http:
//myfaces.apache.org/tomahawk"prefix="t"%>
3.编写JS
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1