一.概要:
这篇文章是自己在学习struts2时从struts2的官方英文文档实例中整理出来的,此处介绍三种Struts2树状菜单的实现方式
分别是:静态,动态,动态Ajax三种方式.
以下实例均在windowsXpsp2+Struts2+MyEcliplse6.01+Tomcat6.0.14+JDK1.6.0_03
下调试通过,项目名称为study3,包名为com.zhang.struts3
这三种实例用到的包文件(,jar包)如下:
commons-io-1.3.2.jar
ognl-2.6.11.jar
struts2-core-2.1.2.jar
struts2-dojo-plugin-2.1.2.jar
xwork-2.1.1.jar
二.具体实现
1.静态指单独使用struts2的tree标签脚本实现的.如以下文件treeStatic.jsp
此文件不需要任何Action类支持,启动Tomcat后在地址栏中输入:
http://127.0.0.1:8080/study3/treeStatic.jsp就行.
*****treeStatic.jsp********************************************************************
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="sx" uri="/struts-dojo-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<html>
<head>
<sx:head />
<base href="<%=basePath%>">
<title>Tree Example(Static)</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script language="JavaScript" type="text/javascript">
dojo.event.topic.subscribe("treeSelected", function treeNodeSelected(node) {
dojo.io.bind({
url: "<s:url value='/info/staticTreeAction.action'/>?nodeId="+node.node.title,
load: function(type, data, evt) {
var divDisplay = dojo.byId("displayIt");
divDisplay.innerHTML=data;
},
mimeType: "text/html"
});
});
</script>
</head>
<body>
<div id="displayIt">
Please click on any node on the tree.
</div>
<div style="float: left; margin-right: 50px;">
<sx:tree label="软件工程" templateCssPath="/struts/tree.css"
showRootGrid="true" showGrid="true" treeSelectedTopic="treeSelected">
<sx:treenode label="开发工具">
<sx:treenode label="java" id="grandchild1Id" />
<sx:treenode label="c++" id="grandchild2Id" />
<sx:treenode label="delphi" id="grandchild3Id" />
</sx:treenode>
<sx:treenode label="编程思想" id="child2Id" />
<sx:treenode label="数据结构" id="child3Id" />
<sx:treenode label="设计模式" id="child4Id" />
<sx:treenode label="相关框架插件" id="child5Id">
<sx:treenode label="Dojo" id="gChild1Id" />
<sx:treenode label="Hibernate" id="gChild2Id" />
</sx:treenode>
</sx:tree>
</div>
</body>
</html>
******************************************************************************
2.动态树状菜单:地址栏访问方法http://127.0.0.1:8080/study3/tree/showDynamicTreeAction.action
具体需要如下文件:struts2的类库文件即与struts2相关的包文件,web.xml,struts.xml,动态树状菜单的显示页面treeExampleDynamic.jsp,
辅助显示页面:dynamicTreeSelect.jsp,动态Action类文件DynamicTreeSelectAction.java,
提供数据的Bean类文件:Category.java,
******************************************************************************
所需要的包文件如下:
******************************************************************************
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
freemarker-2.3.12.jar
ognl-2.6.11.jar
struts2-core-2.1.2.jar
xwork-2.1.1.jar
struts2-dojo-plugin-2.1.2.jar
******************************************************************************
web.xml配置文件
*****web.xml******************************************************************
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
******************************************************************************
在此struts.xml配置文件中我将动态树状菜单以及动态Ajax树状菜单的配置内容写在了这一个
配置文件中,其中动态树状菜单需要的action有dynamicTreeSelectAction,showDynamicTreeAction
动态Ajax树状菜单需要的action有dynamicTreeSelectAction,showDynamicAjaxTreeAction,
getNodes,这两个实例共用了提供数据的bean类文件Category.java文件.同时它们共用了
dynamicTreeSelectAction,但用于显示的Action类文件分别用了自己的Action类文件.
******************************************************************************
*******struts.xml***************************************************************
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="default" extends="struts-default"
namespace="/tree">
<action name="dynamicTreeSelectAction"
class="com.zhang.study3.DynamicTreeSelectAction">
<result>/dynamicTreeSelect.jsp</result>
</action>
<action name="showDynamicTreeAction"
class="com.zhang.study3.ShowDynamicTreeAction">
<result>/treeExampleDynamic.jsp</result>
</action>
<action name="showDynamicAjaxTreeAction">
<result>/treeExampleAjaxDynamic.jsp</result>
</action>
<action name="getNodes"
class="com.zhang.study3.ShowAjaxDynamicTreeAction">
<result type="freemarker">
/treeExampleAjaxDynamic.ftl
</result>
</action>
</package>
</struts>
******************************************************************************
********treeExampleDynamic.jsp***************************************************
<%@ page language="java" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="sx" uri="/struts-dojo-tags"%>
<html>
<head>
<title>Showcase - UI Tag Example - Tree Example (Dynamic)</title>
<sx:head />
</head>
<body>
<script language="JavaScript" type="text/javascript">
dojo.event.topic.subscribe("treeSelected", function treeNodeSelected(node) {
dojo.io.bind({
url: "<s:url value='/tree/dynamicTreeSelectAction.action'/>?nodeId="+node.node.widgetId,
load: function(type, data, evt) {
var divDisplay = dojo.byId("displayId");
divDisplay.innerHTML=data;
},
mimeType: "text/html"
});
});
</script>
<div style="float: left; margin-right: 50px;">
<sx:tree id="tree" rootNode="%{treeRootNode}"
childCollectionProperty="children" nodeIdProperty="id"
nodeTitleProperty="name" treeSelectedTopic="treeSelected">
</sx:tree>
</div>
<div id="displayId">
Please click on any of the tree nodes.
</div>
</body>
</html>
******************************************************************************
******dynamicTreeSelect.jsp***************************************************
<%@ page language="java" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
Id:<s:property value="%{nodeId}" /><br/>
Name:<s:property value="%{nodeName}" /><br/>
(注:此文件就这三行代码负责将你选择的树状菜单相应结点信息如节点Id值与节点名称显示
在.jsp页面上.)
******************************************************************************
用于生成树状菜单的Action类文件
******DynamicTreeSelectAction.java********************************************
package com.zhang.study3;
import com.opensymphony.xwork2.ActionSupport;
import com.zhang.study3.Category;
public class DynamicTreeSelectAction extends ActionSupport {
private long nodeId;
private Category currentCategory;
public void setNodeId(long nodeId) {
this.nodeId = nodeId;
}
public long getNodeId() {
return nodeId;
}
public String execute() throws Exception {
currentCategory = Category.getById(nodeId);
return SUCCESS;
}
public String getNodeName() {
return currentCategory != null ? currentCategory.getName() : "Node not found";
}
}
******************************************************************************
提供数据的bean类文件
****Category.java*************************************************************
package com.zhang.study3;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Category {
private static Map<Long, Category> catMap = new HashMap<Long, Category>();
static {
new Category(1, "省总公司",
new Category(2, "地市1分公司",
new Category(3, "桥东区办事处",
new Category(4, "市场组"),
new Category(7, "客服组"),
new Category(8, "技术组")),
new Category(9, "开发区办事处",
new Category(10, "市场组"),
new Category(11, "客服组"),
new Category(12, "技术组"),
new Category(13, "后勤组"))),
new Category(14, "地市2分公司",
new Category(15, "软件开发组"),
new Category(16, "软件测试组"),
new Category(17, "售后服务组"),
new Category(18, "市场拓展组"),
new Category(19, "财物组")));
}
public static Category getById(long id) {
return catMap.get(id);
}
private long id;
private String name;
private List<Category> children;
private boolean toggle;
public Category(long id, String name, Category... children) {
this.id = id;
this.name = name;
this.children = new ArrayList<Category>();
for (Category child : children) {
this.children.add(child);
}
catMap.put(id, this);
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Category> getChildren() {
return children;
}
public void setChildren(List<Category> children) {
this.children = children;
}
public void toggle() {
toggle = !toggle;
}
public boolean isToggle() {
return toggle;
}
}
******************************************************************************
用于显示树状菜单的Action类文件
*****ShowDynamicTreeAction.java************************************************
package com.zhang.study3;
import com.opensymphony.xwork2.ActionSupport;
import com.zhang.study3.Category;
public class ShowDynamicTreeAction extends ActionSupport {
public Category getTreeRootNode() {
return Category.getById(1);
}
}
致此动态树状菜单的实例已经完成,可以测试了.
******************************************************************************
3.动态Ajax树状菜单:
地址栏访问方法:http://127.0.0.1:8080/study3/tree/showDynamicAjaxTreeAction.action
具体需要如下文件:
动态树状菜单的显示页面:treeExampleAjaxDynamic.jsp,
dojo辅助文件treeExampleAjaxDynamic.ftl,动态Action类文件
ShowAjaxDynamicTreeAction.java,DynamicTreeSelectAction.java(此文件同上)
******************************************************************************
动态Ajax树状菜单的action类文件
******ShowAjaxDynamicTreeAction.java******************************************
package com.zhang.study3;
import com.zhang.study3.Category;
import com.opensymphony.xwork2.ActionSupport;
public class ShowAjaxDynamicTreeAction extends ActionSupport {
private int nodeId = 1;
public Category getCategory() {
return Category.getById(nodeId);
}
public int getNodeId() {
return nodeId;
}
public void setNodeId(int nodeId) {
this.nodeId = nodeId;
}
}
******************************************************************************
显示Ajax树状菜单的.jsp页面
*****treeExampleAjaxDynamic.jsp***********************************************
<%@ page language="java" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="sx" uri="/struts-dojo-tags"%>
<html>
<head>
<title>Showcase - UI Tag Example - Tree Example (Dynamic)</title>
<sx:head />
</head>
<body>
<script language="JavaScript" type="text/javascript">
dojo.event.topic.subscribe("treeSelected", function treeNodeSelected(node) {
dojo.io.bind({
url: "<s:url value='/tree/dynamicTreeSelectAction.action'/>?nodeId="+node.node.widgetId,
load: function(type, data, evt) {
var divDisplay = dojo.byId("displayId");
divDisplay.innerHTML=data;
},
mimeType: "text/html"
});
});
</script>
<s:url var="nodesUrl" namespace="/tree" action="getNodes" />
<div style="float: left; margin-right: 50px;">
<sx:tree id="tree" href="%{#nodesUrl}"
treeSelectedTopic="treeSelected" />
</div>
<div id="displayId">
Please click on any of the tree nodes.
</div>
</body>
</html>
******************************************************************************
辅助生成Ajax树状菜单的.ftl文件,此文件是从官方Struts2的实例包struts2-showcase-2.1.2
中找到复制到项目中的,你也可以按以下代码用记事本保存一个此文件.
****treeExampleAjaxDynamic.ftl************************************************
[
<#list category.children as node>
{
label: '${node.name}',
id: '${node.id}',
hasChildren: ${(node.children.size() > 0)?string}
},
</#list>
]
******************************************************************************
显示Ajax的Action类文件
***ShowAjaxDynamicTreeAction.java**********************************************
package com.zhang.study3;
import com.zhang.study3.Category;
import com.opensymphony.xwork2.ActionSupport;
public class ShowAjaxDynamicTreeAction extends ActionSupport {
private int nodeId = 1;
public Category getCategory() {
return Category.getById(nodeId);
}
public int getNodeId() {
return nodeId;
}
public void setNodeId(int nodeId) {
this.nodeId = nodeId;
}
}
******************************************************************************
致此动态Ajax树状菜单的实例已经完成,可以测试了.
需要注意的是后两种动态树状菜单在地址栏访问时必须以action的方式来访问,而且在以上两种地址栏中
的"/tree"中在struts.xml中配置的命名空间的值即: namespace="/tree"如果输入的是相应的.jsp文件来访问,将不会成功出现想要的结果.
其实以上两个动态菜单的实例还可以进一步扩展,将提供数据的Category.java中的静态数据通过MySql或其它数据库来提供就更实用了.如果有时间我会再整理出通过数据库来提供数据的方式.
从数据库中取值
这里只介绍实现的流程及步骤,方便以后查阅,并没有考虑代码的优化及其它的方面,有兴趣的可以自己去修改!
另外,这里只是实现显示树,并没有实现节点的添加及删除,有需要的可以自己去完成!
首先,建立数据库表,treeTabel
create table treeTables
(
treeId int identity(1,1) primary key, //id标识列,自动增长
treeName varchar(20), //节点名称
parent_treeId int //父节点id
)
建立数据库的操作类,得到树的详细信息
package com.tree.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import javax.sql.rowset.CachedRowSet;
import com.sun.rowset.CachedRowSetImpl;
public class TreeService {
private TreeService(){
try {
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static TreeService treeService;
public static TreeService getInstance(){
if(treeService == null)
treeService = new TreeService();
return treeService;
}
public Connection getConn() throws Exception{
return DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433;databasename=testDB","sa","");
}
/**
* 得到树信息
* @return
* @throws Exception
*/
public Treeinfo[] getAllTreeinfo() throws Exception{
Connection conn = getConn();
PreparedStatement smt = conn.prepareStatement("select * from treeTable where parent_treeId = -1");
CachedRowSet cs = new CachedRowSetImpl();
cs.populate(smt.executeQuery());
List<Treeinfo> treeinfoList = new ArrayList<Treeinfo>();
while(cs.next()){
Treeinfo treeinfo = new Treeinfo();
treeinfo.setTreeId(cs.getInt(1));
treeinfo.setNodeName(cs.getString(2));
treeinfo.setParentId(cs.getInt(3));
treeinfo.setChildren(getChildren(treeinfo.getTreeId()));
treeinfoList.add(treeinfo);
}
cs.close();
smt.close();
conn.close();
Treeinfo[] treeinfo = new Treeinfo[treeinfoList.size()];
treeinfo = treeinfoList.toArray(treeinfo);
return treeinfo;
}
/**
* 得到当前id的子节点信息,这里用了递归调用
* @param treeNodeId
* @return
* @throws Exception
*/
public Treeinfo[] getChildren(int treeNodeId) throws Exception{
Connection conn = getConn();
PreparedStatement smt = conn.prepareStatement("select * from treeTable where parent_treeId = ?");
smt.setInt(1, treeNodeId);
ResultSet rs = smt.executeQuery();
List<Treeinfo> childList = new ArrayList<Treeinfo>();
while(rs.next()){
Treeinfo treeinfo = new Treeinfo();
treeinfo.setTreeId(rs.getInt(1));
treeinfo.setNodeName(rs.getString(2));
treeinfo.setParentId(rs.getInt(3));
treeinfo.setChildren(getChildren(treeinfo.getTreeId()));
childList.add(treeinfo);
}
rs.close();
smt.close();
conn.close();
Treeinfo[] childResult = new Treeinfo[childList.size()];
childResult = childList.toArray(childResult);
return childResult;
}
}
建立中间层Action类
package com.tree.test;
import com.opensymphony.xwork2.ActionSupport;
public class TreeAction extends ActionSupport {
private Treeinfo root;
@Override
public String execute() throws Exception {
root = new Treeinfo();
root.setTreeId(-1);
root.setNodeName("root");
root.setChildren(TreeService.getInstance().getAllTreeinfo());
return SUCCESS;
}
public Treeinfo getRoot() {
return root;
}
public void setRoot(Treeinfo root) {
this.root = root;
}
}
最后新建显示树形菜单的jsp页面
<%@ page language="java" pageEncoding="GB18030"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'testtree.jsp' starting page</title>
<s:head theme="ajax" debug="true" />
<script language="JavaScript">
function treeNodeSelected(arg) {
alert("id["+arg.source.widgetId+"], name["+ arg.source.title+ "] selected");
}
dojo.addOnLoad(function() {
var s = dojo.widget.byId('treeTestId').selector;
dojo.event.connect(s, 'select', 'treeNodeSelected');
});
</script>
</head>
<body>
<s:tree id="treeTestId"
theme="ajax"
rootNode="root"
childCollectionProperty="children"
nodeIdProperty="treeId"
nodeTitleProperty="nodeName"
treeSelectedTopic="treeSelected">
</s:tree>
</body>
</html>
特别注意,要在页面引入 <s:head theme="ajax" debug="true" />