在上一篇文章《Struts 2与AJAX(第一部分)》,我已经简单地介绍了<s:tree
        />的一些用法,接下来我将继续深入讲解<s:tree />的使用和通过DWR实现AJAX校验。
    
        更多<s:tree />
    
        在Struts 2的showcase中有两个<s:tree />的例子,分别是静态树与动态树。所谓的静态树即是在编写JSP代码时通过<s:treenode
        />生成树节点。我的上一篇文章的例子就是一个典型的静态树。而动态树则是在程序运行期间,Struts 2 运行时(Runtime)根据程序中的数据动态创建树节点。虽然在两个例子中<s:tree
        />的theme属性都为“ajax”,但是从严格意义上来说,这两种树都不属于AJAX树,因为它们都是在输出页面时将全部节点加载到其中,而不是在父节点展开时通过XHR(XMLHttpRequest)获取节点数据。
    
        动态树
    
        下面我们先看一下动态树的例子,接着再一步步地将其改造为名副其实的AJAX 树。下例将会把WEB应用程序的目录树展现在JSP页面中。因此,我需要先包装一下java.io.File
        类,代码如下:
    
         package tutorial;
package tutorial;
                
                 import java.io.File;
import java.io.File;
                        
                        
 public 
        class FileWrapper
public 
        class FileWrapper 
        
             {
{
                     private
                        File file;
                       private
                        File file;
                        
                        
 public
                            FileWrapper(String path)
                           public
                            FileWrapper(String path) 
                                 {
{
                                         file = new
                                                File(path);
                                               file = new
                                                File(path);
                                                 }
                                                   }
                                                     
                                                       
                                                    
 public
                                                        FileWrapper(File file)
                                                       public
                                                        FileWrapper(File file) 
                                                             {
{
                                                                     this.file
                                                                    = file;
                                                                           this.file
                                                                    = file;
                                                                         }
                                                                           }
                                                                             
                                                                               
                                                                            
 public
                                                                                String getId()
                                                                               public
                                                                                String getId() 
                
                     {
{
                             return "file_" 
                        + file.hashCode();
                                   return "file_" 
                        + file.hashCode();
                             }
                               }
                                 
                                   
                                
 public
                                    String getName()
                                   public
                                    String getName() 
                                         {
{
                                                 return file.getName();
                                                       return file.getName();
                                                     }
                                                       }
                                                         
                                                           
                                                        
 public
                                                            String getAbsolutePath()
                                                           public
                                                            String getAbsolutePath() 
                                                                 {
{
                                                                         return file.getAbsolutePath();
                                                                               return file.getAbsolutePath();
                                                                             }
                                                                               }
                                                                                 
                                                                                   
                                                                                
 public
                                                                                    FileWrapper[] getChildren()
                                                                                   public
                                                                                    FileWrapper[] getChildren() 
                
                     {
{
                             File[] files = file.listFiles();
                                   File[] files = file.listFiles();
                                
 if(files
                                != 
                        null && files.length 
                        > 0)
                                       if(files
                                != 
                        null && files.length 
                        > 0) 
                                 {
{
                                         int length = files.length;
                                                   int length = files.length;
                                                 FileWrapper[] wrappers 
                                    = 
                                        new FileWrapper[length];
                                                           FileWrapper[] wrappers 
                                    = 
                                        new FileWrapper[length];
                                            
 for(int
                                                    i = 
                                    0; i < length; 
                                    ++i)
                                                       for(int
                                                    i = 
                                    0; i < length; 
                                    ++i) 
                                         {
{
                                                 wrappers[i] 
                                                    = new FileWrapper(files[i]);
                                                               wrappers[i] 
                                                    = new FileWrapper(files[i]);
                                                         }
                                                                   }
                                                             return wrappers;
                                                                       return wrappers;
                                                                 }
                                                                       }
                                                                     return new
                                                                            FileWrapper[0];
                                                                           return new
                                                                            FileWrapper[0];
                                                                                 }
                                                                                   }
                                                                                     }
} 
    清单1 src/tutorial/FileWrapper.java
    
        之所以需要对File类进行如此包装,是因为<s:tree />用于动态树时,rootNode、nodeIdProperty、nodeTitleProperty
        和 childCollectionProperty等属性都必填的。
    
        然后是Action类的代码如下:
    
         package tutorial;
package tutorial;
                
                 import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;
                        
                         import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletRequestAware;
                                
                                 import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ActionSupport;
                                        
                                        
 public 
        class DynamicTreeAction
        extends ActionSupport
        implements ServletRequestAware
public 
        class DynamicTreeAction
        extends ActionSupport
        implements ServletRequestAware
        
             {
{
                     private
                    static 
                final long serialVersionUID 
                = 
                    1128593047269036737L;
                       private
                    static 
                final long serialVersionUID 
                = 
                    1128593047269036737L;
                         
                           
                         private
                            HttpServletRequest request;
                           private
                            HttpServletRequest request;
                             private
                                FileWrapper root;
                               private
                                FileWrapper root;
                                
                                
 public
                                void setServletRequest(HttpServletRequest
                                    request)
                                   public
                                void setServletRequest(HttpServletRequest
                                    request) 
                                         {
{    
                                                 this.request
                                                = request;
                                                       this.request
                                                = request;
                                                     }
                                                       }
                                                         
                                                           
                                                        
 public
                                                            FileWrapper getRoot()
                                                           public
                                                            FileWrapper getRoot() 
                                                                 {
{
                                                                         return root;
                                                                               return root;
                                                                             }
                                                                               }
                                                                                 
                                                                                   
                                                                                 @Override
                                                                                   @Override
                                                                                
 public
                                                                                    String execute()
                                                                                   public
                                                                                    String execute() 
                
                     {
{
                             root = new
                                    FileWrapper(request.getSession().getServletContext().getRealPath("/"));
                                   root = new
                                    FileWrapper(request.getSession().getServletContext().getRealPath("/"));
                                                   
                                             return SUCCESS;
                                                   return SUCCESS;
                                                 }
                                                   }
                                                     }
} 
    清单2 src/tutorial/DynamicTreeAction.java
    
        上述代码取得WEB应用程序的根目录的绝对路径后,初始化FileWrapper对象root。该对象将为JSP页面的<s:tree />的根节点。如下代码所示:
    
        <%@ page language="java" contentType="text/html; charset=utf-8"
                                            pageEncoding="utf-8"%>
                                                    <%@ taglib prefix="s"
                                                                        uri="/struts-tags"%>
                                                                                        
                                                                                    
        <!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">
                    <head>
                                <title>Struts 2 AJAX - More Tree</title>
                                                <s:head theme="ajax" debug="true" 
        />
                    <script type="text/javascript">
                                
                            /* <![CDATA[ 
        */
                        
                function treeNodeSelected(arg)
                    {
                                    alert(arg.source.title 
        + ' selected');
                        }
                        
                        
                function treeNodeExpanded(arg)
                    {
                                    alert(arg.source.title 
        + ' expanded');
                        }
                        
                        
                function treeNodeCollapsed(arg)
                    {
                                    alert(arg.source.title 
        + ' collapsed');
                        }
                        
                        dojo.addOnLoad(function()
                    {                
                                    var
                            t = dojo.widget.byId('appFiles');
                                                dojo.event.topic.subscribe(t.eventNames.expand,
                                treeNodeExpanded);                
                                                dojo.event.topic.subscribe(t.eventNames.collapse,
                                treeNodeCollapsed);
                                                
                                                
        var s 
                = t.selector;  
                                 
                                    dojo.event.connect(s, 'select',
                    'treeNodeSelected');
                                });
                            
                        /* ]]> 
        */    
                    </script>
                        </head>
                                <body>
                                            <h2>
                                                        Dynamic Tree Example
                                                    </h2>
                                                            <div 
        style="float:left;
            margin-right: 50px;">
                            <s:tree id="appFiles" theme="ajax" rootNode="root"
                                                    nodeTitleProperty="name" nodeIdProperty="id"
                                                                childCollectionProperty="children" 
        />
                    </div>
                        </body>
                        </html>
    清单3 WebContent/Tree.jsp
    
        因为<s:tree />的treeCollapsedTopic和treeExpandedTopic属性都没有起作用,所以如果我们想要监听这两个事件,就必须使用上述代码的方法。
    
        最后是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="Struts2_AJAX_DEMO" extends="struts-default">
                                            <action name="DynamicTree" class="tutorial.DynamicTreeAction">
                                                                
        <result>Tree.jsp</result>
                                </action>
                                    </package>
                                    </struts>
    清单4 src/struts.xml
    
        发布运行应用程序,在浏览器地址栏中键入http://localhost:8080/Struts2_Ajax2/DynamicTree.action,有如下图所示页面:
    
        
        图1 动态树示例
    
    
        AJAX 树
    
        正如我在文章开头所说,Struts 2所提供的静态树和动态树都不是严格意义上的AJAX树。下面就让我们来实现一个如假包换的AJAX树。首先要说明的是,Struts
        2的<s:tree />默认是不支持这种按需加载数据的AJAX树。不过因为它是基于Dojo的树控件(Widget)所以要扩展也很方便。
    
        Dojo 通过名为“TreeRPCController”的控件实现 AJAX 树,它会监听被控制树的事件。当发生展开节点的事件时,TreeRPCController就会向URL发送XHR请求,该URL由TreeRPCController的RPCUrl
        属性定义。XHR请求格式类似如下格式:
    
        http://localhost:8080/Struts2_Ajax2/AjaxTree.action?action=getChildren&data={"node":{"widgetId":"file_226092423","objectId":"C:\\Program
                                                Files\\Tomcat 5.5\\webapps\\Struts2_Ajax2","index":0,"isFolder":true},"tree":{"widgetId":"appFiles","objectId":""}}&dojo.preventCache=1182913465392
    清单5 XHR样本
    
        显而易见,请求中包含三个参数,分别是action为“getChildren”(固定值),data一个包含当前节点与树信息的JSON串和dojo.preventCache随机串,用于缓存不同节点的请求响应(父节点只会在第一次被展开时到服务器端加载数据,之后都是从浏览器的缓存中读取数据,可以提高应用程序性能)。
    
        首先我要先写一个加载树节点数据的Action类,代码如下:
    
         package tutorial;
package tutorial;
                
                 import java.util.Map;
import java.util.Map;
                        
                         import com.googlecode.jsonplugin.JSONExeption;
import com.googlecode.jsonplugin.JSONExeption;
                                 import com.googlecode.jsonplugin.JSONUtil;
import com.googlecode.jsonplugin.JSONUtil;
                                        
                                        
 public 
        class AjaxTreeAction
        extends DynamicTreeAction
public 
        class AjaxTreeAction
        extends DynamicTreeAction
        
             {
{
                     private
                    static 
                final long serialVersionUID 
                = 
                    3970019751740942311L;
                       private
                    static 
                final long serialVersionUID 
                = 
                    3970019751740942311L;
                        
                         private
                            String action;
                           private
                            String action;
                             private
                                String data;
                               private
                                String data;
                                 private
                                    FileWrapper[] wrappers;
                                   private
                                    FileWrapper[] wrappers;
                                    
                                    
 public
                                    void setAction(String
                                        action)
                                       public
                                    void setAction(String
                                        action) 
                                             {
{
                                                     this.action
                                                    = action;
                                                           this.action
                                                    = action;
                                                         }
                                                           }
                                                             
                                                               
                                                            
 public
                                                            void setData(String
                                                                data)
                                                               public
                                                            void setData(String
                                                                data) 
                                                                     {
{
                                                                             this.data
                                                                            = data;
                                                                                   this.data
                                                                            = data;
                                                                                 }
                                                                                   }
                                                                                    
                                                                                    
 public
                                                                                        FileWrapper[] getWrappers()
                                                                                       public
                                                                                        FileWrapper[] getWrappers() 
                
                     {
{
                             return wrappers;
                                   return wrappers;
                                 }
                                   }
                                    
                                     @Override
                                       @Override
                                    
 public
                                        String execute()
                                       public
                                        String execute() 
                                             {
{
                                                    
 if("getChildren".equals(action))
                                                           if("getChildren".equals(action)) 
                                                
                                                     {
{
                                                            
 try
                                                                       try 
                                                                     {
{
                                                                             Object o 
                                                                                = JSONUtil.deserialize(data);
                                                                                           Object o 
                                                                                = JSONUtil.deserialize(data);
                                                                                     String path 
                                                                        = ((Map) ((Map) o).get("node")).get("objectId").toString();
                                                                                                   String path 
                                                                        = ((Map) ((Map) o).get("node")).get("objectId").toString();
                                                                                         wrappers 
                                                                        = 
                                                                            new FileWrapper(path).getChildren();
                                                                                                       wrappers 
                                                                        = 
                                                                            new FileWrapper(path).getChildren();
                                                                                
 }
                                                                                catch (JSONExeption
                                                                                    e)
                                                                                           }
                                                                                catch (JSONExeption
                                                                                    e) 
                                                                                         {
{
                                                                                                 e.printStackTrace();
                                                                                                               e.printStackTrace();
                                                                                                 }
                                                                                                           }
                                                                                                     return "ajax";
                                                                                                               return "ajax";
                                                                                                                 }
                                                                                                                       }    
                                                                                                                     return 
                                                super.execute();
                                                                                                                           return 
                                                super.execute();
                                                     }
                                                       }
                                                         }
} 
    清单6 src/tutorial/AjaxTreeAction.java
    
        上述代码可能需要解释一下:
    
        - action属性对应于XHR中的action,如果它为“getChildren”时,则需要进行加载子节点操作。否则,会读取树的根节点,并返回JSP页面; 
- 通过上面XHR的分析,大家可以知道data是代表树和当前节点的JSON串,故应将其反串行化为Map对象,并将其 objectId属性取出。通常情况下,Dojo树的objectId属性代表服务器端的对象的标识,在本例中为文件夹的绝对路径;
        
- wrappers属性表示当前文件夹下的文件数组,它被传送到Freemarker页面,翻译为Dojo树节点数组的JSON串。 
        下面是Freemarker页面的代码:
    
        [
        <#list 
        wrappers as r>
                { "title": "${r.name}", "isFolder": 
                <#if r.children?size
                    gt 0>true<#else>false</#if>, "id": "${r.id}", "objectId": "${r.absolutePath?js_string}"
                                    }<#if 
        r_has_next>,</#if>
                </#list>
                        ]
    清单7 WebContent/AjaxTree.ftl
    
        以上代码中<#list></#lsit>的写法是Freemarker中遍历集合的写法;而<#if r.children?size
        gt 0>判断“r”对象的children属性是否为空;r.absolutePath?js_string 就是将“r”的absolutePath属性的值输出为Javascript
        的字串符形式;<#if r_has_next></#if>判断集合是否有下一项数据。如果希望更详细地了解Freemarker的使用,请参考该手册。
    
        接下来,让我们看看Action的配置代码片段:
    
                
            <action name="AjaxTree" class="tutorial.AjaxTreeAction">
                                    <result>AjaxTree.jsp</result>
                                                    
        <result 
        name="ajax" type="freemarker">AjaxTree.ftl</result>
                                    </action>
    清单8 src/struts.xml配置片段
    
        最后是JSP页面代码:
    
        <%@ page language="java" contentType="text/html; charset=utf-8"
                                            pageEncoding="utf-8"%>
                                                    <%@ taglib prefix="s"
                                                                        uri="/struts-tags"%>
                                                                                        
                                                                                    
        <!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">
                    <head>
                                <title>Struts 2 AJAX - More Tree</title>
                                                <s:head theme="ajax" debug="true" 
        />
                    <script type="text/javascript">
                                
                            /* <![CDATA[ 
        */
                        
                function treeNodeSelected(arg)
                    {
                                    alert(arg.source.title 
        + ' selected');
                        }
                            
                        dojo.addOnLoad(function()
                    {                
                                    var
                            t = dojo.widget.byId('appFiles');        
                                       
                                                
        var s 
                = t.selector;  
                                 
                                    dojo.event.connect(s, 'select',
                    'treeNodeSelected');
                                });    
                            
                        /* ]]> 
        */    
                    </script>
                        </head>
                                <body>
                                            <h2>
                                                        AJAX Tree Example
                                                    </h2>
                                                            <div 
        style="float:left;
            margin-right: 50px;">
                            <script type="text/javascript">
                                        
                                /* <![CDATA[ 
        */
                            dojo.require("dojo.lang.*");
                                        dojo.require("dojo.widget.*");
                                                    dojo.require("dojo.widget.Tree");
                                                                dojo.require("dojo.widget.TreeRPCController");
                                                                               
                                                                             /*
                                                                        ]]> */
                                                                                         </script>             
                                                                                                
        <div 
        dojoType="TreeRPCController" widgetId="treeController"
                                DNDcontroller="create" RPCUrl="<s:url />"></div>
                                            <div dojoType="Tree" widgetId="appFiles" toggle="fade" controller="treeController">
                                                                        
        <div 
        dojoType="TreeNode" title='<s:property 
        value="root.name" />'
                                    widgetId='<s:property 
        value="root.id" />'
                                    isFolder='<s:property 
        value="root.children.length
            > 0" />'
                                    objectId='<s:property 
        value="root.absolutePath" />'>
                                
                    </div>
                                    </div>
                                        </div>
                                            </body>
                                            </html>
    清单9 WebContent/AjaxTree.jsp
    
        由于上面所提及的原因,我在上述的代码中并没有使用<s:tree />标志,而是使用了Dojo的写法——创建 widgetId 为“treeController”的
        TreeRPCController 并将设为树的控制器。
    
        发布运行应用程序,在浏览器地址栏中键入http://localhost:8080/Struts2_Ajax2/AjaxTree.action,点开某个节点,在节点加载的过程中,加号图标变成时钟状图标,如下图所示页面:
    
        
        图2 AJAX树示例
    
    
        自定义<s:tree />的AJAX的主题(theme)
    
        Struts 2的标志过人之外在于它允许开发人员自定义标志的页面输出。要做到这一点,你所需要做的只是创建一个自定义的theme并将其应用到相应标志。下面就让我自定义一个真正的AJAX的<s:tree/>的theme。
    
        首先,你的源文件的根目录下新建包“template.realajax”。
    
        然后,在上一步所建的包中新建“tree.ftl”文件,内容如下:
    
        <script 
        type="text/javascript">
            /* <![CDATA[ 
                    */
                            dojo.require("dojo.lang.*");
                                        dojo.require("dojo.widget.*");
                                                    dojo.require("dojo.widget.Tree");
                                                                dojo.require("dojo.widget.TreeRPCController");    <#--
                                                                                    Added by Max 
        -->            
        /* ]]> 
                */
                </script>    
                    <#-- 
        Added by Max -->             
        <div 
        dojoType="TreeRPCController" 
                 widgetId="${parameters.id?html}_controller"
                     DNDcontroller="create" 
                         RPCUrl="<@s.url />">
                        </div>
                            <#-- 
        End -->    
        <div 
        dojoType="Tree"   
                <#if parameters.blankIconSrc?exists>
                    gridIconSrcT="<@s.url value='${parameters.blankIconSrc}' 
                            encode="false"
                                includeParams='none'/>"
                                        </#if>
                                                <#if
                                            parameters.gridIconSrcL?exists>
                                                    gridIconSrcL="<@s.url value='${parameters.gridIconSrcL}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.gridIconSrcV?exists>
                                    gridIconSrcV="<@s.url value='${parameters.gridIconSrcV}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.gridIconSrcP?exists>
                                    gridIconSrcP="<@s.url value='${parameters.gridIconSrcP}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.gridIconSrcC?exists>
                                    gridIconSrcC="<@s.url value='${parameters.gridIconSrcC}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.gridIconSrcX?exists>
                                    gridIconSrcX="<@s.url value='${parameters.gridIconSrcX}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.gridIconSrcY?exists>
                                    gridIconSrcY="<@s.url value='${parameters.gridIconSrcY}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.gridIconSrcZ?exists>
                                    gridIconSrcZ="<@s.url value='${parameters.gridIconSrcZ}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.expandIconSrcPlus?exists>
                                        expandIconSrcPlus="<@s.url value='${parameters.expandIconSrcPlus}' 
        includeParams='none'/>"
                    </#if>
                            <#if
                        parameters.expandIconSrcMinus?exists>
                                    expandIconSrcMinus="<@s.url value='${parameters.expandIconSrcMinus?html}' 
        includeParams='none'/>"
                    </#if>
                            <#if
                        parameters.iconWidth?exists>
                                iconWidth="<@s.url value='${parameters.iconWidth?html}'
                                encode="false" includeParams='none'/>"
                                                </#if>
                                                        <#if
                                                    parameters.iconHeight?exists>
                                                            iconHeight="<@s.url value='${parameters.iconHeight?html}'
                                                            encode="false" includeParams='none'/>"
                                                                            
        </#if>
                    <#if
                parameters.toggleDuration?exists>
                            toggleDuration=${parameters.toggleDuration?c}
                            </#if>
                                    <#if
                                parameters.templateCssPath?exists>
                                            templateCssPath="<@s.url value='${parameters.templateCssPath}' 
        encode="false" includeParams='none'/>"
                        </#if>
                                <#if
                            parameters.showGrid?exists>
                                    showGrid="${parameters.showGrid?default(true)?string}"
                                    </#if>
                                            <#if
                                        parameters.showRootGrid?exists>
                                                showRootGrid="${parameters.showRootGrid?default(true)?string}"
                                                </#if>
                                                        <#if
                                                    parameters.id?exists>
                                                            id="${parameters.id?html}"
                                                            </#if>
                                                                    <#if
                                                                parameters.treeSelectedTopic?exists>
                                                                            publishSelectionTopic="${parameters.treeSelectedTopic?html}"
                                                                            
        </#if>
                    <#if
                parameters.treeExpandedTopic?exists>
                            publishExpandedTopic="${parameters.treeExpandedTopic?html}"
                            </#if>
                                    <#if
                                parameters.treeCollapsedTopic?exists>
                                            publishCollapsedTopic="${parameters.treeCollapsedTopic?html}"
                                            </#if>
                                                    <#if
                                                parameters.toggle?exists>
                                                        toggle="${parameters.toggle?html}"
                                                        </#if>
                                                                controller="${parameters.id?html}_controller" 
        <#-- 
        Added by Max -->    
                >
                <#if
            parameters.label?exists>
                    <div
                dojoType="TreeNode" title="${parameters.label?html}"
                            <#if parameters.nodeIdProperty?exists>
                                id="${stack.findValue(parameters.nodeIdProperty)}"
                                <#else>
                                        id="${parameters.id}_root"
                                        </#if>
                                                >
                                                <#elseif
                                            parameters.rootNode?exists>
                                                    ${stack.push(parameters.rootNode)}
                                                    <#--
                                                Edited by Max -->    
                                                        <div
                                                    dojoType="TreeNode" 
                                                                     title="${stack.findValue(parameters.nodeTitleProperty)}"
                                                                         widgetId="${stack.findValue(parameters.nodeIdProperty)}"
                                                                             isFolder="<#if
                                                                    stack.findValue(parameters.childCollectionProperty)?size gt 0>true<#else>false</#if>"
                                                                                     objectId="${stack.findValue(parameters.nameValue)}">
                                                                                        
        </div>
                    <#--
                End -->        
                        <#assign
                    oldNode = stack.pop()/> 
        <#-- 
        pop the node off of the stack, but don't show it -->
                    </#if>    
    清单10 src/template/realajax/tree.ftl
    
        对上述稍作解释,上述代码主要在原版的src/template/ajax/tree.ftl的基础上添加了TreeRPCController的控件,并只输出根节点。由于<s:tree
        />没有类似nodeObjectIdProperty的属性,所以我用了value属性表示objectId对应的属性名称。
    
        接着新建tree-close.ftl文件,内容和原版的一样,如下所示:
    
        <#if 
        parameters.label?exists></div></#if></div>
    清单11 src/template/realajax/tree-close.ftl
    
        再下来就应该是将theme应用到<s:tree />,如下代码所示:
    
        <%@ page language="java" contentType="text/html; charset=utf-8"
                                            pageEncoding="utf-8"%>
                                                    <%@ taglib prefix="s"
                                                                        uri="/struts-tags"%>
                                                                                        
                                                                                    
        <!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">
                    <head>
                                <title>Struts 2 AJAX - More Tree</title>
                                                <s:head theme="ajax" debug="true" 
        />
                    <script type="text/javascript">
                                
                            /* <![CDATA[ 
        */
                        
                function treeNodeSelected(arg)
                    {
                                    alert(arg.source.title 
        + ' selected');
                        }
                            
                        dojo.addOnLoad(function()
                    {                
                                    var
                            t = dojo.widget.byId('appFiles');        
                                       
                                                
        var s 
                = t.selector;  
                                 
                                    dojo.event.connect(s, 'select',
                    'treeNodeSelected');
                                });    
                            
                        /* ]]> 
        */    
                    </script>
                        </head>
                                <body>
                                            <h2>
                                                        AJAX Tree Example
                                                    </h2>
                                                            <div 
        style="float:left;
            margin-right: 50px;">
                            <s:tree id="appFiles" theme="realajax" rootNode="root"
                                                    nodeTitleProperty="name" nodeIdProperty="id"
                                                                childCollectionProperty="children" value="absolutePath" 
        />
                    </div>
                        </body>
                        </html>
    清单12 WebContent/AjaxTreeTheme.jsp
    
        上述代码中<s:tree />的用法,除了theme改为“realajax”和多了value="absolutePath"外,几乎和静态树中的一样。
    
        为了不影响前一个例子,我们为该JSP文件配置类型相同的Action,如下代码所示:
    
                
            <action name="AjaxTreeTheme" class="tutorial.AjaxTreeAction">
                                    <result>AjaxTreeTheme.jsp</result>
                                                    
        <result 
        name="ajax" type="freemarker">AjaxTree.ftl</result>
                                    </action>
    清单13 src/struts.xml配置片段
    
        发布运行应用程序,在浏览器地址栏中键入http://localhost:8080/Struts2_Ajax2/AjaxTreeTheme.action,结果如图2所示。
    
    
        总结
    
        通过上述例子,大家知道Struts 2 的AJAX 标志是基于Dojo控件开发的,所以如果大家希望熟练地使用这些标志,最好去了解一下Dojo。
    
        本来还打算介绍一下Struts 2与DWR,不过看看文章的篇幅似乎足够自成一篇了,因此DWR相关的内容要留待下文继续了。
	posted on 2007-06-27 18:33 
Max 阅读(31426) 
评论(41)  编辑  收藏  所属分类: 
Struts 2.0系列