jQuery 推广在 Cascading Style Sheets (CSS) 语法中使用选择器来检索 Document Object Model (DOM) 元素以及对这些元素进行操作。使用 jQuery 有很多优势,其中之一是,您不再有用 JavaScript 解析复杂 DOM 树的负担了。
ZK 是一个事件驱动的、基于组件的 Ajax 框架,因其使用 Java 语言来编程和与 Java EE 技术的集成而出名。它带来了 Ajax 桌面编程体验。对于预期的 HTML,ZK UI 可能被使用标记和一个表达式语言(EL)拼凑起来。
本文探讨了,服务器端代码和 jQuery 的结合如何帮助您应对企业 Ajax 应用程序开发中的挑战。jQuery 和 ZK 结合在一起可以整合技术,帮助您轻松高效地提供丰富且反应灵敏的 UI 体验。
下载 本文使用的样例代码。
以下是企业级 Ajax 应用程序开发人员通常会遇到的挑战:
- 复杂性
- Ajax 比较复杂。在 Ajax 通信模式下,当 DOM 中的元素单独更新时,web 页面不需要整体刷新。
在企业级应用程序中,从服务器端到客户端编组复杂的业务逻辑和手工处理每个 Ajax 调用需要付出艰辛的努力。
- 服务器端编程
- 服务器端 Ajax 解决方案不能利用客户端资源来构建一个更敏感的用户体验、离线功能、或者向客户端发布计算和数据存储。
服务器端解决方案不减少服务器端的内存占用,而且还增加了应用程序的可伸缩性。
- 客户端编程
- 客户端编程进一步提出了安全考虑和开发成本。业务逻辑和敏感数据暴露在浏览器上,使应用程序易受攻击。
客户端编程涉及大量 JavaScript 实现,如果您习惯于使用 Java EE 技术进行应用程序开发,可能会是一个挑战。
- 大数据
- 当 UI 层必须在一个视图上呈现许多数据时,另一个常见问题将会出现。例如,考虑一个常规 HTML 表格。当数据源变得很大时,您可能需要以下之一:
- 冒险超载浏览器缓存或通过预加载大数据设置减缓流量。
- 每当用户发出该请求时,手工在服务器端实现一种机制,来在视图上呈现感兴趣数据的一个子集。
为了消除这些障碍,一个可行的解决方案是使用一个框架,其中客户端和服务器状态是动态同步的。您可以在服务端利用所有安全的 Java EE 技术,而使用客户端计算功能计算响应性和进行 UI 控制。
ZK 的创新架构提供了完全最大化服务器和客户端可用的所有技术的方法。您可以直接访问客户端小部件,只要您想。本小节主要介绍 ZK 服务器和客户端融合架构的一些优势。
ZK Loader 通过以下步骤为 URL 请求提供服务:
- 在请求页面创建 UI 组件
- 在组件上加载必要数据
- 在 HTML 页面上呈现这些数据
- 将其连同 ZK 的 Client Engine 发送回客户端
ZK Asynchronous Update (AU) Engine 和 Client Engine 处理 Ajax 通信,为您屏蔽掉 Ajax 调用处理的复杂性。图 1 显示 ZK Loader、ZK AU Engine 和 ZK Client Engine。
图 1. ZK 融合架构概述
ZK 的 Client Engine 和 AU Engine 充当 pitcher 和 catcher 的角色,ZK 的事件队列依次传输将要处理的事件。 结果是异步事件处理被简化的就像桌面编程一样。
因为服务器在 JVM 中包含一组 ZK 组件,相应的 ZK 小部件集作为 JavaScript 对象驻留在浏览器中。页面状态在两端保持同步。任何处理业务逻辑或敏感数据的事件处理保留在服务器端。当事件处理完成时,服务器端 ZK 组件状态的修改值反过来只会影响客户端 ZK 小部件。不会有业务逻辑暴露在客户端。
ZK 小部件是用 jQuery 编写的客户端 JavaScript 对象。因此,继承了 jQuery 的所有行为。由于在客户端和服务器端都有一个组件的完整表述及其状态,那么您可以选择在客户端或者服务器端设计应用程序。图 2 是一个示例。
图 2. ZK 客户端技术
有时候,您需要解决在客户端处理大量数据的问题。ZK 用户显示数据记录的网格、列表框和组件有一个按需加载的特性,除非视图需要,数据不能在浏览器端呈现。这也有一个分页特性,这样大量数据就可以分布在多个页面上。有了这些特性,呈现大量数据的复杂性大大减少了。
要研究 ZK 的服务器和客户端融合架构,您需要简单了解一下样例:股票查看器应用程序,用两种方式:使用一个纯服务器驱动解决方案做为参考,以及在服务器和客户端分别进行事件处理。
股票查看器应用程序将读取一个 XML 格式的历史股票记录,并以列表格式和线性图显示数据。应用程序将包括一个自动完成搜索的搜索框,比如 Google Suggest。图 3 显示了该应用程序。
图 3. 股票查看应用程序
对于服务器驱动的方法,该样例适应 ZK 的 Model-View-Controller (MVC) 方法。
- 模型
- 该模型是由 3 个 Java 类、2 个数据对象和一个 DAO 对象构成。
Stock对象相当于多个Price对象。public class Stock { private int _id; private String _name; private List<Price> _priceItems = new ArrayList<Price>(); .... //getter and setter methods } public class Price { private String _date; private double _open; private double _high; private double _low; private double _close; private int _volumn; .... //getter and setter methods }
StockDAO类解析 data.xml 中的历史股票数据,并在内存中创建Stock和Price对象。每个对象被添加到 “stocks” 链表中。public class StockDAO { private List<Stock> stocks = new LinkedList<Stock>(); public List findAll() {...} // returns all stock items in data.xml public Stock getStock(int id) {...} } - 视图
- 该视图是使用 ZK 的标记语言实现的。主页面 index.zul 被使用边界布局分段,西边部分包含搜索框和股票名列表,中间部分含有充当 price.zul 占位符的
include元素。每个股票的数据记录在网格部分展示,并在 price.zul 的制图组件中绘制。index.zul 清单如清单 1 所示:
- 第 1 行,
init标记通知 ZK Loader 初始化 ZK 的 Data Binding Manager,以便数据源能动态绑定到 UI 组件,反之亦然。 - 第 2 行,
apply="StockController"表示来自默认包的StockController将充当borderlayout组件的控制器。 - 第 12 行,使用从
StockController类的getStocks()方法获取的数据模型将model="@{main$composer.stocks}"转换成设定的listbox。 - 第 14 至 16 行,
listitem组件为每个股票对象而创建,其名称被指定给listcell标签。
清单 1. index.zul1. <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?> 2. <borderlayout id="main" apply="StockController"> 3. <west title="ZK Finance" size="250px" flex="true" 4. splittable="true" minsize="210" maxsize="500" collapsible="true"> 5. <panel> 6. <toolbar> 7. <label value="Search:" /> 8. <textbox id="searchBox" ctrlKeys="#down#up" 9. focus="true" sclass="demo-search-inp" /> 10. </toolbar> 11. <panelchildren> 12. <listbox id="itemList" model="@{main$composer.stocks}" 13. fixedLayout="true" vflex="true"> 14. <listitem self="@{each='stock'}" value="@{stock}"> 15. <listcell label="@{stock.name}" /> 16. </listitem> 17. </listbox> 18. </panelchildren> 19. </panel> 20. </west> 21. <center> 22. <include id="detail"/> 23. </center> 24. </borderlayout>
price.zul 清单如清单 2 所示:
- 第 1 行,ZK 的 Data Binding Manager 依旧在此被初始化。
- 第 2 行,
apply="PriceController"表示来自默认包的PriceController类将充当窗口组件的控制器。 - 第 3 行,使用从
PriceController类的getPrices()方法获取的数据模型将model="@{main$composer.stocks}"转换成设定网格。 - 第 13 至 20 行,为每个
price对象创建一个行组件,其值被指定给一个标签。
清单 2. price.zul1. <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?> 2. <window id="main2" apply="PriceController"> 3. <grid id="history" model="@{main2$PriceController.prices}" > 4. <columns menupopup="auto"> 5. <column label="Date" /> 6. <column label="Open" /> 7. <column label="High" /> 8. <column label="Low" /> 9. <column label="Close" /> 10. <column label="Volumn" /> 11. </columns> 12. <rows> 13. <row self="@{each='price'}"> 14. <label value="@{price.date}"/> 15. <label value="@{price.open}"/> 16. <label value="@{price.high}"/> 17. <label value="@{price.low}"/> 18. <label value="@{price.close}"/> 19. <label value="@{price.volumn}"/> 20. </row> 21. </rows> 22. </grid> 23. <chart id="line" width="500" height="250" type="line" 24. fgAlpha="128" model="@{main2$PriceController.cateModel}"/> 25. </window>
- 第 1 行,
- 控制器
- 控制器是从 ZK 的
rg.zkoss.zk.ui.util.GenericForwardComposer扩展的类,使用 UI 组件自动连接数据对象。在
StockController类中,它控制 index.zul 的borderlayout组件中的视图:- 第 8 行,含有 id=
main的borderlayout是用一个onCreate事件监听器注册的,设置了将在视图中显示的默认股票详情。 - 第 13 行,含有 id=
itemList的列表框是用一个onSelect事件监听器注册的,获取在listbox中选中的listitem,然后设置带有 id=detail的组件 — 页面资源用于展示股票数据和折线图。 - 第 18 行,带有 id=
searchBox的文本框是用onChanging监听器注册的,循环遍历股票项,返回一个和用户输入匹配的股票列表,每次文本框的值都有所改变。有争论的事件扔给org.zkoss.zk.ui.event.InputEvent,用户的输入分配给字符串key。 - 第 34 行,
getStocks()方法返回 data. xml 中列出的所有股票项。
清单 3. StockController.java1. public class StockController extends GenericForwardComposer { 2. Textbox searchBox; 3. Listbox itemList; 4. Include detail; 5. StockDAO dao = new StockDAO(); 6. private static String DETAIL_URL = "price.zul"; 7. 8. public void onCreate$main(){ 9. itemList.setSelectedIndex(0); 10. Events.postEvent(new Event(Events.ON_SELECT, itemList)); 11. } 12. 13. public void onSelect$itemList(){ 14. int id = ((Stock)itemList.getSelectedItem().getValue()).getId(); 15. detail.setSrc(DETAIL_URL + "?id=" + id); 16. } 17. 18. public void onChanging$searchBox(InputEvent event) { 19. String key = event.getValue(); 20. LinkedList item = new LinkedList(); 21. List items = dao.findAll(); 22. 23. if (key.trim().length() != 0) { 24. for (Iterator iterator = items.iterator(); iterator.hasNext();) { 25. Stock st = (Stock) iterator.next(); 26. if (st.getName().toLowerCase() 27. .indexOf(key.toLowerCase()) != -1) 28. item.add(st); 29. } 30. itemList.setModel(new ListModelList(item)); 31. } else itemList.setModel(new ListModelList(items)); 32. } 33. 34. public List getStocks(){ 35. return dao.findAll(); 36. } 37. }
PriceController类控制网格中的视图和 price.zul 中的制图组件:- 第 14 行,附加到 price.zul 请求的
id参数被捕获。org.zkoss.zk.ui.Executions类是一个非 Ajax 请求的包装器。 - 第 21 行,线性图模型的值被设置。
- 第 25 行,
getPrices()方法被声明,因此在 price.zul 中可通过网格组件中的 EL 调用。这可以正常运行,因为PriceController类扩展了org.zkoss.zk.ui.util.GenericAutowireComposer,它使用数据对象连接 UI 组件。
清单 4. PriceController1. public class PriceController extends GenericAutowireComposer { 2. 3. private StockDAO dao = new StockDAO(); 4. private CategoryModel cateModel; 5. private List items; 6. 7. public PriceController() { 8. init(); 9. } 10. 11. public void init() { 12. //get stock id 13. int id = Integer.parseInt((String) 14. Executions.getCurrent().getParameter("id")); 15. Stock stock = dao.getStock(id); 16. items = stock.getPriceItems(); 17. //create category model for chart 18. cateModel = new SimpleCategoryModel(); 19. for (Iterator iterator = items.iterator(); iterator.hasNext();) { 20. Price price = (Price) iterator.next(); 21. cateModel.setValue(stock.getName(), price.getDate(), 22. Price.getClose()); 23. } 24. } 25. public List getPrices(){ 26. return items; 27. } 28. public CategoryModel getCateModel() { 29. return cateModel; 30. } 31. } - 第 8 行,含有 id=
在此例中,需要实现搜索框功能的异步调用和图表中的动态数据更新是完全由 ZK 的 Client Engine 和 AU Engine 处理的。指令在服务器上执行,客户端仅仅接收 UI 组件和数据模型的更新。
我们来看看使用服务器和客户端融合方法的情况,用同一个样例应用程序。客户端实现最明显的候选者是搜索框,其中响应性是优先考虑的,不单是股票项名称,还有他们对应数据都需要解析和展示。在这种情况下,StockController 类不再需要,因为事件处理搜索框只在客户端实现。示例修改 index.zul 并将其重命名为 hybrid.zul。
hybrid.zul 清单如下所示,注意下列 ZK 指令:
- ZK 的客户端名称空间必须被声明为:
<zk xmlns:w="http://www.zkoss.org/2005/zk/client">
(用于小部件的名称空间 w 是一个 ZK 协议。) - 第 2 行,
apply="StockController"属性是从borderlayout中取出的,因为展示股票名的搜索框和列表框将直接在客户端更新。 - 第 8 行,
textbox的onChanging事件现在被在客户端使用注解w:声明。 - 第 9 行,您可以获取
listbox并调用其update()方法。在 Java 中,this.$f(‘list')表达式等于inp.getFellow("list"),这可以得到带有id="list"的listbox控件。 - 第 13 行,来自类 zk.Widget 的
bind_ callback方法被重写来运行update()方法,在listbox被绑定到 DOM 树之后,设置列表第一项作为选中项。 - 第 28 行,
zUtl.parseXML()是一个实用程序方法,用于解析 XML 文档。jq()在 ZK 中被用作 jQuery 选择器符号,以至于 jQuery 的$()符号仍然可用。 - 第 38 行,使用其标签值和 ID 集合为每个股票项创建一个新的
listitem小部件,并附加到listbox上作为它的子部件。 - 第 56 至 70 行,
listbox的onSelect事件是通过在服务器端为异步请求实现一个服务方法直接进行处理的。该示例为被选中的listitem检索uuid值,并将其附加到 price.zul 页面的请求中,这例展示了股票数据和线性表。
清单 5. hybrid.zul
1. <zk xmlns:w="http://www.zkoss.org/2005/zk/client">
2. <borderlayout id="main">
3. <west title="ZK Finance" size="250px" flex="true"
4. splittable="true" minsize="210" maxsize="500" collapsible="true">
5. <panel>
6. <panelchildren>
7. <textbox id="inp">
8. <attribute w:name="onChanging">
9. this.$f('list').update(event.data.value);
10. </attribute>
11. </textbox>
12. <listbox id="list" onSelect="" rows="10" width="300px">
13. <attribute w:name="bind_">
14. function (desktop, skipper, after) {
15. this.$bind_.apply(this, arguments);
16. var self = this;
17. after.push(function () {
18. self.update();
19. self.firstChild.setSelected(true);
20. });
21. }
22. </attribute>
23. <attribute w:name="update"><![CDATA[
24.
25. (function () {
26. var data;
27. function loadData(w) {
28. var xmlDoc = zk.xml.Utl.parseXML(jq(w).html().replace(/<!--|-->/g,'').trim()),
29. ids = xmlDoc.getElementsByTagName("id"),
30. labels = xmlDoc.getElementsByTagName("name");
31. data = [];
32. jq(ids).each(function (i) {
33. data.push({id: this.firstChild.nodeValue, label:
34. labels[i].firstChild.nodeValue});
35. });
36. }
37.
38. function createItems (listbox, data) {
39. if (!data.length) return;
40. jq(data).each(function () {
41. listbox.appendChild(new zul.sel.Listitem({label: this.label, uuid: this.id}));
42. });
43. }
44. return function (txt) {
45. txt = txt || '';
46. if (!data) loadData(this.$f('data'));
47. this.clear();
48. createItems(this, jq.grep(data, function (item) {
49. return item.label.toLowerCase().indexOf(txt.toLowerCase()) != -1;
50. }));
51. this.stripe();
52. };
53. })()
54. ]]></attribute>
55. </listbox>
56. <zscript>
57. public class MyService implements org.zkoss.zk.au.AuService {
58. public boolean service(org.zkoss.zk.au.AuRequest request, boolean everError) {
59. final String cmd = request.getCommand();
60. if (cmd.equals(Events.ON_SELECT)) {
61. String uuid = ((List)request.getData().get("items")).get(0);
62. System.out.println("selected:" + uuid);
63. content.setSrc("price.zul?id=" + uuid);
64. return true;
65. }
66. return false;
67. }
68. }
69. list.setAuService(new MyService());
70. </zscript>
71. <include id="data" src="data.xml" comment="true"/>
72. </panelchildren>
73. </panel>
74. </west>
75. <center>
76. <include id="content" src="price.zul?id=1"/>
77. </center>
78. </borderlayout>
79. </zk>
|
您可以 下载 样例代码。注意在搜索框的敏感性方面的改进。
在本文中,您学习了如何通过利用客户端和服务器端编程的优势在 ZK 中改进您的 Ajax 开发。ZK 在两端动态同步应用程序状态。您可以享受 Java EE 技术的生产效率,同时仍然可以选择实现客户端功能,您的实现方法变得依赖于您构建的企业级应用程序的需求,而不依赖于开发框架的优势或制约。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 本文样例代码 | zk5Client_ServerFusion.zip | 9018KB | HTTP |
学习
- “使用 ZK 框架的富 Internet 应用程序” (developerWorks,2010 年 1 月)向您介绍 ZK,通过一个真实的示例来展示其使用方法,这个示例运行在 Apache Tomcat 上并连接到 MySQL 数据库。
- 阅读关于 ZK 5.0 和 jQuery 和 ZK 5.0 和 jQuery 第 2 部分,获取介绍、演示、如何实现 ZK 控制、如何构建实例等方面的知识。
- 进一步了解 客户端名称空间 和客户端编程。
- 阅读 ZK Widget,所有小部件类的超级类。
-
ZK MVC Made Easy 详细介绍 ZK 提供的 3 个实用工具和 3 个帮助方法,将有助于您在 Model-View-Controller 模式下轻松地编写应用程序。
- 了解 ZK 数据绑定(实现 UI 组件和数据源之间数据复制完全自动化的一种机制)和数据绑定管理。
-
developerWorks Web development
专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
-
developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
-
developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。
- 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。
获得产品和技术
- 免费 下载 ZK。
- 下载 IBM 产品评估试用版软件 或 IBM SOA 人员沙箱,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 参与 developerWorks 博客 并加入 developerWorks 社区。
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
- 现在就创建您的 My developerWorks 个人档案 并设置一个关于 Ajax 的 关注清单。连接到 My developerWorks 并保持连接。
- 找到 对 web 开发感兴趣的其他 developerWorks 成员。
- 分享您的知识:加入一个关注 web 主题的 developerWorks 群组。
- Roland Barcia 撰写的关于 Web 2.0 和中间件 的博客。
- 关注 developerWorks 成员 关于 web 主题的共享书签。
- 快速获取答案:访问 Web 2.0 Apps 论坛。
- 快速获取答案:访问 Ajax 论坛。

