需求:
动态加载目录树节点数据。即:树的初始状态为根节点树,当点击其中一个节点时,再从数据库中获取当前节点的子节点。
环境:
Struts1+Hibernate3.0+jQuery+treeview
解决方案:
1.我自己的项目需求是:获取linux的FTP目录,以动态加载的方式取得该FTP上的所有目录及文件生成js树,点击文件获取该文件在FTP上的路径,目录不能点击。
2.准备工作:
<link rel="stylesheet" href="css/jquery.treeview.css" /> <link rel="stylesheet" href="css/screen.css" /> <script src="js/jquery.js" type="text/javascript"></script> <script src="js/jquery.cookie.js" type="text/javascript"></script> <script src="js/jquery.treeview.js" type="text/javascript"></script> <script src="js/jquery.treeview.async.js" type="text/javascript"></script>
|
以上各引用文件可以从网上下载的treeview 包里得到,下载地址:http://jquery.bassistance.de/treeview/jquery.treeview.zip。注意js文件的引用顺序,黄色部分是异步加载必须的js文件。具体应用时根据自己情况决定,上面的文件不一定全得有。以下是我的jsp的代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <%@ include file="/general/common.jsp" %> <head> <% response.setHeader("Cache-Control","no-cache"); response.setHeader("Pragma","no-cache"); response.setDateHeader("Expires",0); %> <link rel="stylesheet" href="css/jquery.treeview.css" />
<script src="javascript/jquery.treeview.js" type="text/javascript"></script> <script src="javascript/jquery.treeview.async.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function(){ $("#black").treeview({ url: "source.do" }) });
function returnID(Path) { if(Path!="undefined") document.getElementById("Path").value=Path; else document.getElementById("Path").value=""; } </script> </head> <body>
<h1><bean:message key="ProgramMediacontent.SourceFileName"/></h1> <br/> <br/> <br/> <table align="center"> <tr align="left">
<td width="60%" valign="top"> <ul id="black" class="filetree treeview-famfamfam"></ul> </td>
<td width="40%" valign="bottom" align="center"> <div class="content_panel" id="div_function_panel"> <center> <bean:message key="Content.ListFile"/><bean:message key="Colon" bundle="common"/> <input id="Path" style="overflow-x:visible;width:60;" type="text"></input> <br/> <div style="text-align:center " class="buttons"> <button type="button" onclick="return onSubmit();"><bean:message key="Button.OK" bundle="common"/> </button> <button type="button" onclick="return back2list();"><bean:message key="Button.Cancel" bundle="common"/></button> </div> </center> </div> </td>
</tr> </table>
<script type="text/javascript"> function onSubmit() { var Path = document.getElementById("Path").value; var myString = new Array(); myString[0]=Path; return return2parent(btn.ok,myString); }
function back2list() { return return2parent(btn.back);
} </script> </body> </html> |
我自己的公用文件里已经有jquery的js了,所以没有引用treeview自带的文件,jquery.cookie.js说是用来保存树的状态的,但我用后就觉得很不爽。因为我要在父页面选择一个FTP服务器,然后在上面这个dialog页面里显示其目录结构,加上jquery.cookie.js,后无论选择哪个服务器,新弹出的页面中的树总是第一次生成树的内容(这里可能表达的不是很清楚,说白了就是不刷新)。无奈去掉它,再加上jsp页面禁用缓存的经典代码,问题解决。
上面是完整的代码,为了给大家一个整体感觉,但其实生成树所用的代码只有一行:
<ul id="black" class="filetree treeview-famfamfam"></ul>
把标签与treeview连接的js如下:
<script type="text/javascript"> $(document).ready(function(){ $("#black").treeview({ url: "source.do" }) }); </script> |
看起来很简单。<ul>是要节点生成的位置,id随便取,与上面js一致就行,class是treeview自带的样式(自带了三种)。Js中的url是js树获得数据的数据源,可以是php页面、jsp页面或者是从后台action传递过来的数据。但因为treeview是Jquery的插件,所以无论哪种方式,传递时都必须用json串来通讯。
Struts中配置数据源与页面关联:
<action path="/source" scope="request" type="com.blossom.cmportal.view.content.ListFTPFileAction" validate="false"> </action> |
一个jsp页面数据源的例子如下:source.jsp
<% String text="[{/"text/": /"计算机学院/",/"expanded/": false,/"children/":[{/"text/": /"软件工程/",/"children/":[{/"text/":/"Java/"},{/"text/":/"C++/"}]},{/"text/": /"网络工程/",/"children/":[{/"text/":/"Cisco/"}]},{/"text/": /"计算机应用/",/"children/":[{/"text/":/"多媒体应用/",/"id/":/"dmt/",/"hasChildren/":true}]}]},{/"text/": /"金融学院/",/"id/":/"jr/",/"hasChildren/":true}]"; String para=request.getParameter("root"); if("source".equals(para)){ System.out.println("Success"); out.print(text); } else if("jr".equals(para)) { String jr="[{/"text/": /"证券/",/"expanded/": true,/"children/":[{/"text/": /"银河证券/"},{/"text/": /"长江证券/"}]},{/"text/": /"银行/"},{/"text/": /"保险/"},{/"text/": /"股市/"}]"; out.print(jr); } else if("dmt".equals(para)){ String dmt="[{/"text/": /"视频编解码/",},{/"text/": /"MaltLab/"},{/"text/": /"SecondLife/"}]"; out.print(dmt); } %> |
该jsp页面就只有这些代码,其他的什么都不要带。有些乱,但如果了解json,上面代码很容易理解。
如果只是想简单的测试一下,上面的两个jsp已经可以形成树了,另外就是要注意css/js/images与jsp页面的路径,根据自己情况配置好。
我的项目要求数据必须是从后台获取,所以我的treeview的URL是source.do(我用的struts1),这个根据自己项目写。
以下是Action代码:
package com.blossom.cmportal.view.content;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping;
import com.blossom.cmportal.service.mam.business.MediaServerService; import com.blossom.cmportal.service.mam.vo.MediaServer; import com.blossom.cmportal.view.general.CommonAction; import com.blossom.cmportal.view.general.FTPFileMenuGetter;
public class ListFTPFileAction extends CommonAction { private static final Log log = LogFactory.getLog(ListFTPFileAction.class);
@Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("Enter ListFTPFileAction Class"); FTPFileMenuGetter ftptest = new FTPFileMenuGetter(); response.setHeader("Cache-Control", "no-cache"); response.setContentType("text/json;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); String root = request.getParameter("root"); String json = null; String ID = request.getSession().getAttribute("ID").toString(); MediaServerService service = new MediaServerService(); MediaServer vo = service.queryByID(ID); ftptest.login(vo.getServerip(), vo.getServerport(), vo.getUsername(), vo.getPassword()); if (root.equals("source")) json = ftptest.listfile(vo.getServerroot()); else json = ftptest.listfile(root); out.print(json); out.flush(); out.close(); ftptest.close(); log.info("Quit ListFTPFileAction Class"); return null; }
} |
以上代码有我取FTP数据的内容,所以看起来乱了点。其实形成树后台要传递的内容主要就是:
response.setHeader("Cache-Control", "no-cache"); response.setContentType("text/json;charset=UTF-8");//注意这里是text/json response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); String root = request.getParameter("root");//treeview定义了一个root变量,初次访问时固定值为“source”,再次访问时将用你当前所选节点的id来赋值给root,这些都可以配置,但为了简便,我没有改。想自己配置的就自己看treeview的源代码,不多,很容易看懂。 if (root.equals("source")) //如果是第一次访问,则只传递FTP根目录下的所有目录及文件 json = ftptest.listfile(vo.getServerroot()); else json = ftptest.listfile(root); //否则,把当前所选目录的路径返回赋值给root(其实就是ul标签的id属性值,为实现项目功能,我把目录路径作为了标签的id) out.print(json); //把json串返回给页面 out.flush(); out.close();
|
以下是我把FTP目录结构包装成json串的代码,主要是让大家看一下怎样组织json串。
Java生成json要用到以下jar包:
commons-collections-3.1.jar,commons-lang-2.0.jar ,commons-logging-1.1.1.jar,commons-net-2.0.jar, ezmorph-1.0.4.jar, json-lib-2.1-jdk13.jar,共六个。
各种Java数组或者list转json的代码网上随便就能找到,以下仅供参考。
public String listfile(String path) { FTPFile[] ftpfiles=getFiles(path);//取得了一个文件数组 int i; int k; if(path.equals("/")) k=0; else k=2; JSONArray baseArray = new JSONArray(); for(i=k;i<ftpfiles.length;i++) { JSONObject baseObject = new JSONObject(); String name=null; try { name = new String(ftpfiles[i].getName().trim().getBytes("ISO8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if(ftpfiles[i].isDirectory()) { if(path.equals("/")) baseObject.put("id",path+name ); else baseObject.put("id",path+"/"+name ); baseObject.put("classes", "folder"); baseObject.put("hasChildren", true); } else { if(path.equals("/")) baseObject.put("path",path+name ); else baseObject.put("path", path+"/"+name); baseObject.put("classes", "file"); } baseObject.put("text",name ); baseArray.add(baseObject); } String json =null; if(baseArray!=null) json= baseArray.toString(); else json="[]"; System.out.println(json); return json; } |
上面的大概意思就是取得当前目录下所有目录及文件,自定义属性”path”,”classes”是treeview中自带的样式所需属性,”text”属性是节点要显示的名称,其实这些就是ul/li标签的属性。
至于要在js树上加上链接,及返回参数等等动作就要改jquery.treeview.async.js文件的代码了,以下是我修改的部分代码,大家可以参考下载的源码对比一下。
function load(settings, root, child, container) { $.getJSON(settings.url, {root: root}, function(response) { function createNode(parent) { if((this.id+"")!="undefined") var current = $("<li/>").attr("id", this.id ).html("<span>" + this.text + "</span>").appendTo(parent); else var current = $("<li/>").attr("id", this.id||"").html("<span><a href=/"javascript:returnID('"+this.path+"');/" >" + this.text + "</a></span>").appendTo(parent);
if (this.classes) { current.children("span").addClass(""+this.classes);
} if (this.expanded) { current.addClass("open"); } if (this.hasChildren || this.children && this.children.length) { var branch = $("<ul/>").appendTo(current); if (this.hasChildren) { current.addClass("hasChildren"); createNode.call({ text:"placeholder", id:"placeholder", children:[] }, branch); } if (this.children && this.children.length) { $.each(this.children, createNode, [branch]) } } } $.each(response, createNode, [child]); $(container).treeview({add: child}); }); } |
上面的大概意思就是,属性为目录的节点都有id属性,而属性为文件的节点没有id属性但多了一个返回当前路径的链接。页面上接收传递回的path的js代码如下:
function returnID(Path) { if(Path!="undefined") document.getElementById("Path").value=Path; else document.getElementById("Path").value=""; } |
+++++++++++++++++++++++++++++++++++
到这里,整个目录树就算是完成了。生成的树与treeview示例中的树一样。
3.上面是分开来说,可能第一次接触这东西会觉得很乱。那么总体来讲一下整个过程就是:
从主页面通过struts配置的action跳转到dialog页面,dialog页面加载完成后执行jquery脚本语言,(根据url)去struts配置中寻找source.do并执行相应的action,该action返回封装好的json串给jsp页面(通过out输出),jsp页面所加载的treeview的代码在接收到数据后形成正确的树结构。