欢迎进入Wiki » FAQ » 如何开发 AJAX 列表(list)页面、如何开发多页签的列表页面?

如何开发 AJAX 列表(list)页面、如何开发多页签的列表页面?

在2014-11-19 18:04上被李小翔修改
评论 (0) · 附件 (1) · 记录 · 信息

为了提升性能与用户体验,可以列表界面加载数据时,可以采用ajax方式。

在 BroFramework 中,通过查询框架,可以很轻松的实现 ajax 加载的需求,示例如下。

  • 修改 list.gsp,给 s:form 标签添加属性 ajax,如:
    <s:form action="${actionName}" ajax="true">
  • 将 list.gsp 中的 <div class="list"><div class="paginateButtons">两端代码,剪切出来粘贴到一个新的 _list.gsp 文件中
  • 修改 list.gsp ,在上述两个 div 原本所在位置插入标签 g:render,如:
    <g:render template="/foo/bar/list" plugin="bro-foobar"  />
    注:如果是插件开发,则需要 plugin 属性,否则不需要
  • 修改控制器中的 list 方法,将最后的 render 修改为:
    if ( isAjax() ) {
        render ( view:"${VIEW_PATH}_list", model:[......] )
    } else {
        render ( view:"${VIEW_PATH}list", model:[......] )
    }

    也就是判断 ajax 请求时,仅渲染 _list.gsp

经过上述改造后,页面加载的时候会一次性渲染完整的第一页,但是点击翻页、表头排序、查询时,都会自动采用 ajax 方式加载列表了。

此外,某些场景下,列表页面中需要加载多个页签,并且每个页签下显示一个独立的列表,但是页签顶部有一个统一的查询栏,效果如下图所示:

多页签.png

这个需求开发略微复杂,以上述截图中的待办列表为例。

改造 list.gsp,添加页签:

<%-- s:form 标签添加 ajax 属性--%>
<s:form action="${actionName}" ajax="true"><s:setup/>
   <div class="body">
       <h1><g:message code="bropen.workbench.task.Todo"/></h1>
       <g:if test="${flash.message}"><div class="message">${flash.message}</div></g:if>
       <g:if test="${flash.errors?.hasErrors()}"><div class="errors"><g:renderErrors bean="${flash.errors}" as="list" /></div></g:if>
       
       <%-- 唯一的搜索栏 --%>
       <s:filter>
           <div class="right">
                ....
           </div>
       </s:filter>
       
       <%-- 生成页签 --%>
       <script>$j(function(){ $j("#tabs").tabs() })</script>
       <div id="tabs">
           <ul>
               <g:each in="${lists}" var="m" status="i">
                   <li><a href="#tab-${i}">${m.name}(${m.total})</a></li>
               </g:each>
               <%-- 创建两个右对齐的按钮型页签 --%>
               <li class="right refresh" style="cursor: pointer" onclick="reloadTasks()">&nbsp;</li>
               <li class="right oarequirement" style="cursor: pointer" onclick="xx">&nbsp;</li>
           </ul>
   
           <%-- 遍历多个页签,生成列表及其页签容器,注意容器设置了一个css class,生成列表时,model中增加了一个参数 tabId 用于标识哪个页签、并用于控制器判断加载更新哪个页签的数据 --%>
           <g:each in="${lists}" var="m" status="i">
           <div id="tab-${i}" class="tabId${m.tabId}">
               <g:render template="/foo/bar/list" plugin="bro-foobar" model="[list: m.list, total: m.total, tabId: m.tabId]" />
           </div>
           </g:each>
       </div>
   </div>
</s:form>

子列表页面 _list.gsp 如下:

<div class="list">
   <table class="fixedEllipsis">
       <%-- 设置表头,注意参数 tabId --%>
       <thead>
           <tr>
               <g:sortableColumn params="[tabId: tabId]" property="security" title="${message(code:'bropen.workbench.task.Task.security')}" width="30" />
                ......
           </tr>
       </thead>
       <tbody>
       <g:each in="${list}" status="i" var="task">
           <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
               <td><a href="${task.url}" target="_blank">${task.docNumber}</a></td>
                ......
           </tr>
       </g:each>
       <g:each in="${(total != null && setting('bropen.framework.pagination.default')-list.size()) ? (list.size()..setting('bropen.framework.pagination.default')-1) : null}" var="i">
           <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">${"<td>&nbsp;</td>".multiply(10)}</tr>
       </g:each>
       </tbody>
   </table>
</div>
<%-- 设置翻页栏,注意参数 tabId --%>
<g:if test="${total}"><div class="paginateButtons"><g:paginate total="${total}" params="[tabId: tabId]" /></div></g:if>

控制器操作如下:

// ajax 加载一个页签
if ( params.tabId ) {
   def list = ...
   def total = ...
   return render ( view:"${VIEW_PATH}_list", model:[list: list, total: total, tabId: params.tabId] )
}
// 第一次加载或搜索,计算多个页签
else {
   def lists = []
   for ( x in xx ) {
        lists << [tabId: 标签标识, name: 标签名,list: 列表数据, total: N]
   }
    render ( view:"${VIEW_PATH}list",  model:[lists: lists] )
}

这样改造后,任意一个列表翻页、排序时,会覆盖其他列表,因此,每次翻页或排序时,需要指定本次操作的容器(某个页签),这里通过查询框架的事件来实现;此外,由于只有一个查询栏,点搜索按钮时也会自动用 ajax 方式查询,得到的结果也不是想要的,需要改造成搜索时不通过 ajax、直接加载整个页面,此时同样可以通过查询框架的事件来执行。如下面的 js 示例代码,贴到 list.gsp 中:

1、在页签中翻页、排序时(我们在 _list.gsp 中设置了一个 tabId 的额外参数,通过它可以判断),给 s:form 设置一个 ajaxContainer 的表单参数,指向 list.gsp 中的页签 div 容器

2、搜索操作时,删除 s:form 标签的 ajax 属性,实现非 ajax 查询

/**
 * 多页签时,用 ajax 方式提交翻页、排序,非ajax方式提交搜索
 */

search.callbackBeforeSubmit = function( actionUrl ) {
   var form = $j(search.form());
   // 如果是多页签
   if ( form.attr("ajax") && $j("#tabs").length ) {
       if ( actionUrl.indexOf("tabId") > 0 ) {
           // 翻页、排序时,设置列表容器
           var container = actionUrl.replace(/.+(tabId=[^&]+)(&.+)?/, "$1").replace("=", "");
            form.attr("ajaxContainer", "." + container);
        } else {
           // 点搜索按钮时,取消ajax
           form.removeAttr("ajax");
        }
    }
}

最后,默认的 jQuery UI 的页签组件显示效果适用于表单,在列表界面中不太好看,因此,需要设置一些 css 样式,上图中的样式示例如下:

/** 页签高宽 */
.ui-tabs .ui-tabs-panel {
   padding: 0px;
}
.ui-tabs .ui-tabs-nav {
   padding-top: 0px;
}
.ui-tabs .ui-tabs-nav li,
.ui-tabs .ui-tabs-nav li.ui-tabs-active {
   cursor: pointer;
   border: none;
   margin-right: 15px;
   margin-bottom: 2px;
   padding-bottom: 1px;
   text-align: center;      /** 文字居中 */
   height: 20px;            /** 和图片高宽吻合 */
   width: 99px;
}
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
   float: none;            /** 文字居中 */
}

/** 页签背景 */
.ui-tabs .ui-corner-top {
   border-top-right-radius: 0px;
   border-top-left-radius: 0px;
}
.ui-tabs .ui-widget-header {
   background: url("tab-background.gif") repeat-x;
   border: none;
}
.ui-tabs .ui-state-default {
   background: url("tab.jpg") repeat-x scroll 50% 50% #ffffff;
}
.ui-tabs .ui-state-active {
   background: url("tab-active.jpg") repeat-x scroll 50% 50% #f1f1f1;
}

/** 页签字体 */
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited {
   color: #333;
   line-height: 2;
}
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited {
   color: #06C;
   line-height: 2;
   font-weight: bold;
}

/** 右侧页签 */
.ui-tabs .ui-tabs-nav li.right {
   float: right;
   margin-right: 0px;
   margin-left: 15px;
}
/** 右侧页签按钮示例 */
.ui-tabs .ui-tabs-nav li.oarequirement {
   background: url("sample/oarequirement.gif") no-repeat scroll 50% 50% #ffffff;
   width: 63px;
}
.ui-tabs .ui-tabs-nav li.refresh {
   background: url("sample/refresh.gif") no-repeat scroll 50% 50% #ffffff;
   width: 63px;
}

/** 搜索栏 */
.searchfilter {
   border: none;
}
在2014-11-19 17:53上被李小翔创建

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号