ID #6552

用jQuery和jTemplates插件实现客户端分页的表格展现

一直以来觉得用JSON和JavaScript在客户端绑定数据给一个表格或者Grid是件很麻烦的事 情。Microsoft ASP.NET Ajax提供了类似Sys.Date.DataTable和Sys.Dat.DataView这样的类 来帮助实现客户端绑定,也可以用for循环来动态构建表格,但这些都显得很麻烦而且很不灵 活。jQuery的jTemplates插件实现了一种灵活的方式来控制显示,它允许我们定义好一个显 示模板,jQuery在展现数据时根据选择的模板来动态生成。这就类似于ASP.NET中的 ItemTemplate,也和XSLT有些类似。通过这样的方式,你可以很容易的在客户端通过自定义 模板以很灵活的方式展现列表数据。

jQuery官方网站给jTemplates的定义是:jTemplates is a template engine 100% in JavaScript.更多的信息可以参考 http://jtemplates.tpython.com/。 接下来我们实现一个小例子来演示如何使用jTemplate 去构建一个RSS阅读器。

创建RSS阅读器

RSS源通常都位于另外的domain中,而 在AJAX中浏览器通常禁止cross-domain的访问,在这里,为了避开这个问题我们可以在服务 端去取得数据。通常我们可以将这些方法做成WebMethod方法放到WebServices中,但这里我 们可以将这些方法放到页面的cs文件中。需要注意的是,这个方法必须被声明为Static方法 ,而且要以WebMethod标注。这样做的目的是,只有在标注为WebMethod,客户端才能正常访 问这个方法。而Static标记标识了这个方法不与任何这个页面的实例相关,而是而这个页面 本身相关,对于任何一个实例而言都是相同的。所以在你调用的时候,你不需要与任何页面 类的实例相关。

[WebMethod]
public static IEnumerable GetFeeds (int PageSize,int PageNumber)
{
  string strFeedUrl = System.Configuration.ConfigurationManager.AppSettings["FeedUrl"];
  XDocument feedXML = XDocument.Load(strFeedUrl);

  var feeds = from feed in feedXML.Descendants("item")
        select new
        {
          Date = DateTime.Parse (feed.Element("pubDate").Value).ToShortDateString(),
           Title = feed.Element("title").Value,
           Link = feed.Element("link").Value,
          Description = feed.Element("description").Value,
        };

  //paging... (LINQ)
  return feeds.Skip((PageNumber - 1) * PageSize).Take(PageSize);
}

在上边的方法中设定了RSS的地址,并通 过LINQ to XML来取得我们想要的属性。Skip和Take函数联合起来实现了一个分页的功能。

通过jQuery调用Page Method

jQuery.Ajax方法实现了用Ajax的方式来请求一 个页面并设定回调函数来处理相应状态和结果。在我们的实例中,需要请求上边写的 PageMethod并处理返回结果。

function DisplayRSS(page) {
   $.ajax({
    type: "POST",
    url: "Default.aspx/GetFeeds",
    data: " {'PageSize':'" + pageSize + "', 'PageNumber':'" + page + "'}",
     contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function(msg) {
       //TODO:Show the result as a table.
      alert(msg);
    }
  });
}

在success的回调函数中我们什么也没有做,先来看看 result到底是个什么东西。在浏览器中设置允许调试脚本,定义一个函数供回调函数中调用 ,然后设定断点在新的函数中。

我们看到在服务端将数据以IEnumerable返回后实际 上在result中包含的是一个JSON表示的数据集合。每个对象含有Date, Description, Link和 Title属性,这和前边用LINQ取XML字段时是相符的。因为你已经拥有了这个数据集合,接下 来所要做的就是在客户端通过某种方式展现出来。你也许会想到用动态拼接Table的方式来做 ,但这并不灵活。jTemplates提供了更优雅的方式来实现。

用jTemplate来展现数据

jTemplate就把数据展现的方式和业务逻辑分开来,允许你定义好一个模板,然后再 运行时会根据模板的内容来render. 和ASP.NET中的绑定相似,也有一个特定的符号来表示绑 定的上下文对象$T。$T就类似于上图中的result.d[n]—某一个业务对象,需要展现哪 个属性后边直接跟.PropertyName即可。因为表格的行是可变的,所以这里就类似于XSLT中一 样来控制动态生成.

<table id="RSSTable" cellspacing="0">
<thead>
  <tr>
    <th width="80">Date</th>
   <th>Title / Excerpt</th>
  </tr>
</thead>
<tbody>
  {#foreach $T.d as post}
  <tr>
   <td rowspan="2">{$T.post.Date}</td>
   <td><a href="{$T.post.Link}">{$T.post.Title}</a></td>
   </tr>
  <tr>
   <td>{$T.post.Description} </td>
  </tr>
  {#/for}
</tbody>
</table>

在页面请求完PageMethod并成功返回后可以来应用模板展 现数据了:

$(document).ready(function() {
// On page load, display the first page of results.
DisplayRSS(1);
});

function DisplayRSS(page) {
……
success: function(msg) {
  // Render the resulting data, via template.
ApplyTemplate (msg);
  // TODO: Update navigation status
}
……
}

function ApplyTemplate(msg) {
$('#Container').setTemplateURL('Template/RSSTable.htm',
                  null, { filter_data: false });
$('#Container').processTemplate(msg);
}

现在为止我们 只取得了数据并展现了特定的条数而无法实现分页。看起来一切都好—除了分页。

增加客户端分页功能

为了实现分页首先需要知道页码总数,这样我们可以简 单的通过Previous | Next来实现导航。假设一下在服务端我们需要什么:总页数,页大小, 当前页,判断并控制Previous|Next导航的有效性及其动作。ok,明白我们所要做的步骤:

· 获取页总数 – 通过Page Method来返回

· 控制页大小和当前页

· 控制Previous | Next导航的有效性

· 实现Previous | Next导航动作

首先,在Template中增加 页面导航:

<div id="Paging">
  <a id="PrevPage" class="paging">&laquo; Previous Page</a>
  <a id="NextPage" class="paging">Next Page &raquo;</a>
</div>

其次,声明获取页面总数或者条目总数的Page Method. 和前 边获取数据源的方法很类似我们除了不需要返回任何XML的属性值外仅仅调用Count方法即可 。

  [WebMethod]
  public static int GetFeedsCount()
   {
    string strFeedUrl = System.Configuration.ConfigurationManager.AppSettings["FeedUrl"];

    XDocument feedXML = XDocument.Load(strFeedUrl);

     return feedXML.Descendants("item").Count();
  }

设置默认的页面大小,并在显示数据后更新导航栏状态。在页面中我们需要请求这个 PageMethod并来计算总的页数。

var currentPage = 1;
var lastPage = 1;
var pageSize = 5;

function GetRSSItemCount() {
$.ajax ({
  type: "POST",
  url: "Default.aspx/GetFeedsCount",
  data: "{}",
   contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function(msg) {
   // msg.d will contain the total number of items, as
   // an integer. Divide to find total number of pages.
   lastPage = Math.ceil(msg.d / pageSize);
    // TODO: Update navigation status
  }
});
}

接 下来就是实现// TODO: Update navigation status标记的内容了:更新导航栏的状态及其动 作。当前页的值存储在currentPage变量中,lastPage告诉我们哪一页是最后页,通过这两个 参数可以很容易的控制导航状态。而因为他们是全局变量,所以执行导航时只需要通过简单 的++/--操作,最终请求GetFeeds方法时会取得相应页的数据。

function UpdatePaging() {
// If we're not on the first page, enable the "Previous" link.
if (currentPage != 1) {
   $('#PrevPage').attr('href', '#');
   $('#PrevPage').click(PrevPage);
}

// If we're not on the last page, enable the "Next" link.
if (currentPage != lastPage) {
  $('#NextPage').attr('href', '#');
  $('#NextPage').click(NextPage);
}
}

function NextPage(evt) {
// Prevent the browser from navigating needlessly to #.
evt.preventDefault();

// Entertain the user while the previous page is loaded.
DisplayProgressIndication();

// Load and render the next page of results, and
// increment the current page number.
DisplayRSS(++currentPage);
}

function PrevPage(evt) {
// Prevent the browser from navigating needlessly to #.
evt.preventDefault ();

// Entertain the user while the previous page is loaded.
DisplayProgressIndication();

// Load and render the previous page of results, and
// decrement the current page number.
DisplayRSS(-- currentPage);
}

用UpdatePaging函数替换上边标注的TODO部分,完整 的分页功能即可实现。当然,这里的分页显得很粗糙,但你可以通过扩展其样式来做出更丰 富的导航栏。另外示例中也提供了loading时提示用户,这里不尽具表。一个很好的启示是方 便的展现LIST并提供简单的客户端分页。


2011-07-01 18:55
阅读:
I'm VC , Just U know Y
本站部分文章来源于互联网,版权归原作者所有。

延伸阅读:

一次JQuery性能优化实战

jquery中使用表单元素选择器

JQuery的扩展接口简单示例

jquery使用attr访问自定义属性,减少javascript脚本中

Jquery弹出层插件Thickbox使用心得