非常酷的树:Struts2 + JSON + Extjs Tree
最近尝试用extjs来展示树状菜单。着实花了一番功夫。树状菜单的菜单项需要动态加载,而目前版本的extjs中只支持JSON格式的数据。查了一些资料,决定使用struts2的json-plugin。首先按照例子做了一个,但是结果就是不成功,界面上只出来了一个js中生成的root节点,不能加载从后台生成的数据。研究后发现是数据格式有问题。使用json-plugin生成的数据格式如下:
{"cls":"folder","id":10,"leaf":false,"children":[{"cls":"file","id":11,"leaf":true,"children":null,"text":"S600"},{"cls":"file","id":12,"leaf":true,"children":null,"text":"SLK200"}],"text":"Benz"}
而extjs需要的数据格式如下:
[{"cls":"folder","id":10,"leaf":false,"children":[{"cls":"file","id":11,"leaf":true,"children":null,"text":"S600"},{"cls":"file","id":12,"leaf":true,"children":null,"text":"SLK200"}],"text":"Benz"}]
区别很小,就只相差最外面的两个方括号。但是少了这两个方括号,在json中,含义迥然不同,前者表示一个对象,而后者表示一个数组。而extjs中 tree的dataloader需要的数据必须是一个数组。而这样的数据格式是json-plugin自动生成的,无法改变。所以,我最后放弃了json -plugin,转而使用json-lib来解决这个问题。
1. 下载json-lib, http://json-lib.sourceforge.net/
2. lib目录下的jar文件清单:
commons-beanutils-1.7.0.jar
commons-collections-3.2.jar
commons-digester-1.6.jar
commons-lang-2.3.jar
commons-logging-1.1.jar
dom4j-1.6.1.jar
ezmorph-1.0.4.jar
freemarker-2.3.8.jar
json-lib-2.2.1-jdk15.jar
log4j-1.2.13.jar
ognl-2.6.11.jar
struts2-core-2.0.11.jar
xml-apis-1.0.b2.jar
xwork-2.0.4.jar
首先配置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">
??<welcome-file-list>
????<welcome-file>index.jsp</welcome-file>
??</welcome-file-list>
??<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>
</web-app>
?
然后是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>
????<constant?name="struts.devMode"?value="true"/>
????<constant?name="struts.i18n.encoding"?value="UTF-8"/>
????<package?name="person"?extends="struts-default">
????????<action?name="menus"?method="execute"?class="com.lab.MenuAction">
????????????<result>/menu.jsp</result>
????????</action>
????</package>
</struts>
?
3. 树的节点模型(省略了getter,setter)
public?class?Menu?{
????private?int?id;
????private?String?text;
????private?boolean?leaf;
????private?String?cls;
????private?List<Menu>?children;
}
4. action
package?com.lab;
import?java.util.ArrayList;
import?java.util.List;
import?net.sf.json.JSONArray;
public?class?MenuAction?{
????private?String?menuString;
???
????private?List<Menu>?menus;
???
????public?String?execute()?{
????????menus?=?new?ArrayList<Menu>();
???????
????????Menu?benz?=?new?Menu();
????????benz.setText("Benz");
????????benz.setCls("folder");
????????benz.setLeaf(false);
????????benz.setId(10);
????????menus.add(benz);
???????
????????List<Menu>?benzList?=?new?ArrayList<Menu>();
????????benz.setChildren(benzList);
???????
????????Menu?menu;
????????menu?=?new?Menu();
????????menu.setText("S600");
????????menu.setCls("file");
????????menu.setLeaf(true);
????????menu.setId(11);
????????benzList.add(menu);
????????menu?=?new?Menu();
????????menu.setText("SLK200");
????????menu.setCls("file");
????????menu.setLeaf(true);
????????menu.setId(12);
????????benzList.add(menu);
???????
????????Menu?bmw?=?new?Menu();
????????bmw.setText("BMW");
????????bmw.setCls("folder");
????????bmw.setLeaf(false);
????????bmw.setId(20);
????????menus.add(bmw);
???????
????????List<Menu>?bmwList?=?new?ArrayList<Menu>();
????????bmw.setChildren(bmwList);
???????
????????menu?=?new?Menu();
????????menu.setText("325i");
????????menu.setCls("file");
????????menu.setLeaf(true);
????????menu.setId(21);
????????bmwList.add(menu);
???????
????????menu?=?new?Menu();
????????menu.setText("X5");
????????menu.setCls("file");
????????menu.setLeaf(true);
????????menu.setId(22);
????????bmwList.add(menu);
???????
????????JSONArray?jsonObject?=?JSONArray.fromObject(menus);
????????try?{
????????????menuString?=?jsonObject.toString();
????????}?catch?(Exception?e)?{
????????????menuString?=?"ss";
????????}
????????return?"success";
????}
????public?String?getMenuString()?{
????????return?menuString;
????}
????public?void?setMenuString(String?menuString)?{
????????this.menuString?=?menuString;
????}
}
5. menu.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<s:property value="menuString" escape="false"/>
6. html页面和js
我使用的就是extjs的example中的reorder.html和reorder.js,更改了reorder.js中treeloader的dataurl: menus.action
<html>
<head>
<meta?http-equiv="Content-Type"?content="text/html;?charset=iso-8859-1">
<title>Reorder?TreePanel</title>
<link?rel="stylesheet"?type="text/css"?href="extjs/resources/css/ext-all.css"?/>
????<!--?GC?-->
?????<!--?LIBS?-->
?????<script?type="text/javascript"?src="extjs/adapter/ext/ext-base.js"></script>
?????<!--?ENDLIBS?-->
?
????<script?type="text/javascript"?src="extjs/ext-all.js"></script>
<script?type="text/javascript"?src="reorder.js"></script>
<!--?Common?Styles?for?the?examples?-->
<link?rel="stylesheet"?type="text/css"?href="extjs/resources/css/example.css"?/>
</head>
<body>
<script?type="text/javascript"?src="../examples.js"></script><!--?EXAMPLES?-->
<h1>Drag?and?Drop?ordering?in?a?TreePanel</h1>
<p>This?example?shows?basic?drag?and?drop?node?moving?in?a?tree.?In?this?implementation?there?are?no?restrictions?and?
anything?can?be?dropped?anywhere?except?appending?to?nodes?marked?"leaf"?(the?files).?<br></p>
<p>Drag?along?the?edge?of?the?tree?to?trigger?auto?scrolling?while?performing?a?drag?and?drop.</p>
<p>In?order?to?demonstrate?drag?and?drop?insertion?points,?sorting?was?<b>not</b>?enabled.</p>
<p>The?data?for?this?tree?is?asynchronously?loaded?with?a?JSON?TreeLoader.</p>
<p>The?js?is?not?minified?so?it?is?readable.?See?<a?href="reorder.js">reorder.js</a>.</p>
<div?id="tree-div"?style="overflow:auto;?height:300px;width:250px;border:1px?solid?#c3daf9;"></div>
</body>
</html>
js:
/*
?*?Ext?JS?Library?2.0.1
?*?Copyright(c)?2006-2008,?Ext?JS,?LLC.
?*?licensing@extjs.com
?*
?*?http://extjs.com/license
?*/
Ext.onReady(function(){
????//?shorthand
????var?Tree?=?Ext.tree;
???
????var?tree?=?new?Tree.TreePanel({
????????el:'tree-div',
????????autoScroll:true,
????????animate:true,
????????enableDD:true,
????????containerScroll:?true,
????????loader:?new?Tree.TreeLoader({
????????????dataUrl:'http://localhost:8080/lab/menus.action'
????????})
????});
????//?set?the?root?node
????var?root?=?new?Tree.AsyncTreeNode({
????????text:?'Ext?JS',
????????draggable:false,
????????id:'source'
????});
????tree.setRootNode(root);
????//?render?the?tree
????tree.render();
????root.expand();
});
?
?
1 楼 xuyao 2009-01-05 lz新人吧,记住,一定要发个可下载的文件包 2 楼 Johnhe 2009-01-06 是的哦。或者用code下编辑代码下 看起来好难受 3 楼 cqsdqopgsf 2009-01-06 谢谢大家的建议,下一篇文章一定改进 4 楼 panpanshupian 2009-01-06 LZ有没有考虑过:JSONArray jsonObject = JSONArray.fromObject(menus);
当menus在记录很多的情况下会不会很慢啊!
刚接触JSON,呵呵! 5 楼 kjj 2009-01-13 panpanshupian 写道LZ有没有考虑过:
JSONArray jsonObject = JSONArray.fromObject(menus);
当menus在记录很多的情况下会不会很慢啊!
刚接触JSON,呵呵!
一个菜单能有多少记录啊 6 楼 javascripteye 2009-01-14 dojo吧,前景一片光明 7 楼 wish520 2009-01-17 不错 楼主 8 楼 babymouse 2009-03-11 这个真费劲~用freemarker不就简单多了 9 楼 usherlight 2009-03-11 兄弟是转载吧。 10 楼 zhujianyu 2009-03-12 为什么我做的时候就会无法显示了,报错。。。
严重: Servlet.service() for servlet default threw exception
java.lang.NoClassDefFoundError: org/apache/commons/lang/exception/NestableRuntimeException
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1817)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:872)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1325)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1204)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
at com.tool.Action.MenuAction.execute(MenuAction.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:404)
at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:267)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:229)
at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:221)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:150)
at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:48)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:123)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:184)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:105)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:83)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:207)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:74)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:127)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at org.apache.struts2.interceptor.ProfilingActivationInterceptor.intercept(ProfilingActivationInterceptor.java:107)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:206)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:115)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:143)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:121)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:170)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:123)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:176)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:50)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:504)
at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:422)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Unknown Source)
2009-3-12 14:29:00 org.apache.coyote.http11.Http11Protocol pause
11 楼 BBZ.DD.A 2009-08-11 还是放一个压缩包吧 这样看有点费劲 12 楼 dyllove98 2010-01-04 "前者表示一个对象,而后者表示一个数组。而extjs中 tree的dataloader需要的数据必须是一个数组。而这样的数据格式是json-plugin自动生成的,无法改变。所以,我最后放弃了json -plugin,转而使用json-lib来解决这个问题。"
替换下符号不行?