从开始构建动态 Web 应用程序起,开发人员一直都是使用传统分页技术。每当需要显示大量的数据时,都要使用分页技术来每次显示一部分数据。用户使用 Next 或 Previous 按钮从一个数据集或页面导航到另一个数据集或页面。
如今,传统分页技术的一些变体也得到应用。有些是向用户显示 First、Previous、Next 和 Last 按钮,有些是使用数字,还有一些则结合使用按钮和数字。分页的概念可以帮助用户和服务器有效地处理大量的数据。
然而,最近 Ajax 的盛行却改变了开发人员设计和构建 Web 应用程序的方式。通过使用 Ajax,可以构建外观和行为更接近于传统桌面应用程序的 Web 应用程序。有些开放源码和商业 JavaScript/Ajax 框架和库提供了直接可用的小部件(widget),使得 Ajax 模型更易于实现。开发人员可以使用这些小部件轻松地为 Web 应用程序添加丰富的特性和功能。
其中一个那样的框架就是 Rico。在本文中,我将简要地介绍这个开放源码的客户端 JavaScript 框架,然后着重描述它的一个小部件,即 LiveGrid。正如本文要演示的那样,可以通过实现 LiveGrid 来替代 Web 应用程序中传统的分页模型。
Rico 库
Rico 库是一个 JavaScript 文件,可以将这个库包括在任何 Web 页面中,以便使用丰富的特性和小部件。这个库处理很多复杂的实现细节,包括跨浏览器的兼容性,使您得以心无旁骛地创建页面功能。
Rico 特性或小部件使用起来很简单:只需编写几行代码将那个特性或小部件集成到一个给定页面上。例如,Rico 为将 Ajax 支持添加到 Web 页面中提供了一个非常简单的接口。只需添加 5 到 6 行 JavaScript 代码,就可以将 Ajax 功能添加到 Web 页面中,然后,就可以向服务器发出 Ajax 请求,并处理来自服务器的响应。无需直接与 XmlHttpRequest
JavaScript 对象打交道,这一切都可以实现。
通过 Rico,将拖放之类的可视化效果添加到 Web 页面也很容易。通过 Rico 库,可以将任何 HTML 或 JavaScript 对象定义为 draggable(可拖放的) 或一个 drop zone(拖放区)。这样定义之后,就可以在页面上拖动可拖放对象,并将其放入一个或多个用户定义的拖放区。Rico 自动处理该功能的所有实现细节,所以只需编写几行代码。
如果使用 Rico,那么添加淡入、动画缩放和位移、旋转等动画效果也是非常简单的。最流行的一个 Rico 小部件就是 Accordion,它显示一组可折叠的抽屉。每个抽屉有一个抽屉标题和一个内容区。单击一个抽屉标题就可以显示那个抽屉的内容,同时也隐藏其他抽屉中的内容。另一个非常流行的小部件是 LiveGrid,本文的后面将着重讲到这个小部件。
一个简单的 LiveGrid
图 1 中的 LiveGrid 是一个可滚动的信息表,每当用户单击滚动条,表中的数据就动态更新。这个表被连接到一个活动的数据源,对这个表的更新是借助 Ajax 完成的。
图 1. 初始的 LiveGrid
LiveGrid 显示一个可滚动的包含 20 部影片的列表。每当用户单击滚动条,表的内容就随之更新,显示新的一组影片。当需要新的一组数据时,Rico 对服务器发出 Ajax 调用。它使用缓冲技术,以取得更好的性能,并使滚动看上去更为流畅。因此,可能并不是每次单击滚动条都会导致 Ajax 请求。Rico 首先使用缓冲区中的数据来更新网格,当用光了缓冲区中所有的数据时,才发出 Ajax 请求。图 2 显示了用户单击一次滚动条后 LiveGrid 看上去的样子。LiveGrid 还支持列排序。如果网格中实现了排序,那么用户可以单击列名,然后对这个列进行排序。
图 2. 单击滚动条后的 LiveGrid
创建一个 LiveGrid
作为一个学习的例子,我将展示如何创建 Movies LiveGrid above。您需要从 Rico 主页下载 download rico.js 和 prototype.js。注意,Rico 库使用了另一个开放源代码框架 Prototype 中的特性,因此这里需要 prototype.js。此外,还需要一个 Web 应用服务器,用于处理来自 Rico 的 Ajax 调用。对于这个例子,我将使用 Apache Tomcat 6.0。
可以从编写这个例子的客户端代码开始。首先要做的是将 rico.js 和 prototype.js 库包括在页面中。假设这两个库文件与 HTML 文件在同一个目录中,清单 1 展示了如何将这两个库包括在页面中。
清单 1. 从 Rico 和 Prototype 库开始
<script src="prototype.js"></script> <script src="rico.js"></script> |
接下来,添加一个占位符,用于容纳显示图 1 中文本 "Showing 1 - 5 of 20 Movies" 的标签。每当用户单击滚动条,这个标签就会更新。
清单 2. 设置 showLabel
<div id="showingLabel" >Showing 1 - 5 of 20 Movies</div> |
然后,添加显示图 1 中列名(例如 Number、Title、Year)的 LiveGrid 标题。
清单 3. 添加 LiveGrid 标题
<table cellspacing="0" cellpadding="0" width="400"> <tr> <th bgcolor="#999999" width="30">#</th> <th bgcolor="#999999" width="310">Title</th> <th bgcolor="#999999" width="60">Year</th> </tr> </table> |
最后,可以创建 Rico 用于显示影片数据的表。用下面代码创建的表有 6 个行,3 个列。表中每隔一行有一个浅灰色背景。要使 LiveGrid 正确运行,需要在表中额外创建一行。因此,如果创建一个要显示 5 个行的 LiveGrid,那么所创建的 HTML 表需要 6 行。如果没有额外的行,那么 LiveGrid 的滚动功能将失常。清单 4 显示了创建 LiveGrid 使用的 HTML 表所需的代码。
清单 4. LiveGrid 数据 HTML 表
<div id="movieDiv" style="float:left"> <table id="movie_grid" cellspacing="0" cellpadding="0" width = "400px" style="border:1px solid #ababab" > <tr> <td width="30"> </td> <td width="310"> </td> <td width="60"> </td> </tr> <tr> <td bgcolor="#eeeeee" width="30"> </td> <td bgcolor="#eeeeee" width="310"> </td> <td bgcolor="#eeeeee" width="60"> </td> </tr> <tr> <td width="30"> </td> <td width="310"> </td> <td width="60"> </td> </tr> <tr> <td bgcolor="#eeeeee" width="30"> </td> <td bgcolor="#eeeeee" width="310"> </td> <td bgcolor="#eeeeee" width="60"> </td> </tr> <tr> <td width="30"> </td> <td width="310"> </td> <td width="60"> </td> </tr> <tr> <td bgcolor="#eeeeee" width="30"> </td> <td bgcolor="#eeeeee" width="310"> </td> <td bgcolor="#eeeeee" width="60"> </td> </tr> </table> </div>
|
<script> function loadGrid() { var opts = { prefetchBuffer: true, onscroll : updateLabel }; var liveGrid = new Rico.LiveGrid( 'movie_grid',5, 20, 'MovieData', opts); } </script> |
loadGrid
函数中的第一行指定 LiveGrid 的选项。当把 prefetchBuffer
选项设为 true 时,就是告诉 Rico 在构造 LiveGrid 时取数据。如果将这个选项设为 false,那么 LiveGrid 装载时没有任何数据,直到单击一次滚动条之后才显示数据。
通过使用 onScroll
选项可以定义一个函数,每当用户单击滚动条时,Rico 就调用这个函数。在这个例子中,需要更新显示文本 "Showing 1 - 5 of 20 Movies" 的 label。为此,可以定义一个名为 updateLabel
的 JavaScript 函数,并让 Rico 在用户单击滚动条的时候调用该函数。还可以使用本例中没有使用的 onscrollidle
选项来定义当滚动暂停或停止时调用的 JavaScript 函数。
另一个有用的选项是 requestParameters
,可以使用该选项将额外的参数发送至处理 Ajax 请求的服务。还可以使用 requestParameters
过滤 LiveGrid 中显示的数据。例如,如果只想在 LiveGrid 中显示动作片,那么可以将 requestParameters
设置为 genre=action,处理 Ajax 调用的服务将返回那种类型的影片。
loadGrid
函数中的第二行声明 LiveGrid。构造函数中的第一个参数是被转换成 LiveGrid 的 HTML 表的 ID。在这个例子中,这个 ID 为 "movie_grid
"。第二个参数表明要在 LiveGrid 中显示多少行,在这个例子中是 5 行。第三个参数表明 LiveGrid 中的最大行数,在这个例子中为 20。第四个参数应该为负责处理 Ajax 调用的服务器端 script/servlet 的 URL。在这个例子中,我创建了一个名为 MovieData 的 servlet,用于处理来自 Rico 的 Ajax 请求。
装载和更新网格
每当装载 HTML 页面时,需要调用 loadGrid
函数。清单 6 显示了这是怎么完成的。
清单 6. 调用 loadGrid
<body onload="javascript:loadGrid()"> |
最后,定义用于更新 Movie LiveGrid 上的 label 的函数,如清单 7 所示。
清单 7. updateLabel 函数
<script> function updateLabel( liveGrid, offset ) { $('showingLabel').innerHTML = "Showing " + (offset+1) + " - " + (offset+liveGrid.metaData.getPageSize()) + " of " + liveGrid.metaData.getTotalRows() + " movies"; } </script> |
每当用户单击滚动条时,就会调用 updateLabel
函数。它主要组合变量和 Rico 库来构造出类似 "Showing 1 - 5 of 20 movies" 的字符串。 如果不熟悉 Prototype JavaScript 框架,可以使用 $(DIVNAME)
语法用与 DIVNAME
匹配的 ID 引用页面上的任何对象。因此,$('showingLabel').innerHTML
相当于 document.getElementById("showingLabel").innerHTML
。在这个例子中,每当用户单击滚动条时,便用一个更新的字符串替换 DIV
标记的 innerHTML
值。
设置服务器端
Rico 中的大部分代码编写工作是在客户端进行的,但在服务器端也有一些重要的事情要做。也就是说,需要设置服务器端脚本或 servlet 来处理 Ajax 调用。如前所说,我创建了一个例子 servlet,用于处理来自 Rico 的 Ajax 请求。清单 8 显示为了使 LiveGrid 小部件正常运行,应该如何编写来自 servlet 的 XML 响应。
清单 8. XML 响应所需的格式
<?xml version="1.0" encoding="ISO-8859-1"?> <ajax-response> <response type="object" id='movie_grid_updater'> <rows update_ui='true' > <tr> <td>1</td> <td>Star Wars</td> <td>1977</td> </tr> <tr> <td>2</td> <td >The Godfather</td> <td>1972</td> </tr> ... </rows> </response> </ajax-response> |
注意,response 标记中 id 属性的前两个单词("movie_grid
")与之前为 LiveGrid 定义的 HTML 表中的 id 属性是匹配的。为了使 Rico 更新 LiveGrid 中的数据,它们应该匹配。在通常情况下,服务器端脚本或 servlet 连接到数据源,并生成以上格式的 XML。Rico 负责从这个 XML 响应中提取数据,并用新数据填充 LiveGrid。注意要将响应的 content-type 设置为 "text/xml"。如果没有这样做,LiveGrid 将不能工作。XML 版本和编码也必须分别设置为 "1.0" 和 "ISO-8859-1"。可以如清单 9 所示用 Java™ 代码设置 content-type。
清单 9. 设置 Ajax 响应的 content-type
response.setHeader("Content-Type", "text/xml"); |
4 个数据参数
现在,您可能对脚本或 servlet 如何知道何时生成什么数据感到困惑。在本文的前面,我提到 Rico 框架使用了缓冲技术,当它需要数据时,它就会请求数据。当 Rico 向后端发出一个 Ajax 请求时,它可能会以 URL 中的查询字符串的形式发送 4 个参数。当提供了这些参数时,后端必须使用这些参数来生成适当的 Ajax 响应。这 4 个参数是: