JSF 和 Ajax:使用 Rational Application Developer V7 轻松实现 Web 2.0 应用程序

Ajax 是当今互联网上最流行的技术,因为它被称作 Web 2.0 的基石。不幸的是,创建 Ajax 应用程序并不容易,尤其是当您需要与其他框架融合时,例如 JavaServer™ Framework (JSF)。幸运的是,IBM Rational Application Developer V7 为 JSF 组件提供了 Ajax 功能,这一功能使得这样的任务变得简单。本文介绍了如何在 IBM Rational Application Developer V7 中使用 Ajax 和 JSF 以降低任务的难度,并通过一个例子示范如何在现存的应用程序中增加 Ajax 支持。

Yury Kats (ykats@us.ibm.com), 顾问软件工程师, IBM

Yury Kats photoYury Kats 是 IBM Rational Application Developer 团队的一名开发成员,主要从事于 JSF 实现,组件以及工具的开发。



2007 年 4 月 12 日

Ajax 的优势和缺点

虽然不是什么革新性创新,Ajax 技术在近两年变得非常流行。大量主要 Web 站点(例如 Google、Yahoo!、 Amazon 和 Netflix)使用这一技术来改善它们网站用户的访问体验。事实上,改进用户体验正是 Ajax 要做的。

在过去十年的 Web 应用程序开发中,用户与浏览器间和浏览器与服务器间的交互是明确界定及显而易见的: 用户在浏览器内浏览网页,执行操作(从上下文菜单中选择内容,或者选择一些复选框), 之后通过点击链接或提交按钮指导浏览器与服务器通信。浏览器给服务器发送请求并传递用户输入。服务器处理请求并返回响应,返回的可能是更新的页面或是相同的页面,总之进行了更新。

这样的 Web 应用程序现在被称为 Web 1.0。 从用户体验的观点来看,它们具有两条缺陷:

  • 浏览器和服务器的交互只能由页面中有限的控件发起——常常是链接和按钮。这样就不能在用户选择了复选框(check box)或在组合框(combo box)后迅速通知服务器。
  • 浏览器和服务器的这种交互所造成的后果就是需要更新整个浏览器窗口。这种情况通常很慢,页面更新往往需要用户等待很长时间。更坏的是,当同一页面被重新加载或重新刷新时,经常会在浏览器窗口中闪烁。

新一代被称为 Web 2.0 的 Web 应用程序通过利用 Ajax 技术弥补了这些缺点(也作AJAX,即 Asynchronous JavaScript and XML,异步的 JavaScript 与 XML)。在 Ajax 中,浏览器和服务器的交互发生在后台,用户将不会觉察。而且它比普通的“浏览器 - 服务器”交互更具针对性,仅仅需要将页面的子集发送给服务器,并且服务器仅仅返回所需更新的子集页面。这种方式所带来的结果就是,浏览器和服务器间的通信可被任何事件初始化,例如在组合框或复选框中进行选择,或鼠标指针停留事件等。这样带来了很大好处:

  • 通信更加迅速,因为传递的数据更少。
  • 用户停留在同一页面,因为不再需要过多的页面间导航。
  • 重新加载的页面不会闪烁,因为仅仅是页面中的一小部分依据 Ajax 请求更新。

Ajax 背后的想法十分简单:在浏览器中监听事件,给服务器发送后台请求,当服务器响应时更新页面的一部分。但是实现过程是非常复杂的。它需要 JavaScript™,客户端-服务端通信协议,和服务端代码的深层知识。不同浏览器版本间的区别使得开发与调试更加困难。但是,IBM® Rational® Application Developer Version 7 提供了所有开发 Ajax Web 应用程序所需的工具,而 不必 实现所有底层细节。

Rational Application Developer V7 提供了:

  • 扩展的 JSF 允许在 JavaServer™ Framework (JSF)中处理 Ajax 请求
  • 一个可以在所有最新版本的主流浏览器中初始化 Ajax 请求,并在服务器端响应仅对部分页面进行更新的 JavaScript™ 程序库

Rational Application Developer V7中 Ajax 和JSF 实现的技术细节超出了本文的范围,但是让我们来看看您如何使用这些技术。


如何用JSF组件应用Ajax

为 JSF 页面增加 Ajax 需要四个步骤:

  1. 识别由 Ajax 请求更新的页面区域。 在 Rational Application Developer V7中, 您可以在几乎任何面板组件的内容中使用 Ajax。面板包括了从简单的容器,例如 <h:panelGroup><h:panelGrid>,到特性丰富的面板,例如菜单 (<hx:panelMenu>) 和对话框(<hx:panelDialog>)。
  2. 选择所使用的 Ajax 请求类型。 Rational Application Developer V7 JSF 库支持三种 Ajax 请求:
    • 对于同一页面的 GET 请求 (<hx:ajaxRefreshRequest>)
    • 对于同一页面的 POST 请求 (<hx:ajaxSubmitRequest>)
    • 对其他页面的 GET 请求 (<hx:ajaxExternalRequest>)
  3. 应用 Ajax 请求配置传递给服务器的参数。
    • 对于 GET 请求,您可以传递页面中不同输入栏的值。
    • 对于 POST 请求, 提交全部表格。
  4. 识别初始化 Ajax 请求的事件。 它可以是任何的客户端 JavaScript 事件,例如按钮的 onclick , 输入栏的 onblur, 或复选框的 onchange

让我们通过“Hello, world”这个简单的例子了解一下全部过程。建立一个页面包括两栏:输入和输出。在用户点中输入栏后,将利用 Ajax 技术发送用户输入数据到服务器,并且接收返回消息更新输出栏。

建立 Web 项目

首先,创建一个 Web 项目(参见 图 1):

  • 从菜单选择 File > New > Project > Dynamic Web Project
  • New Project 向导中:
    1. 输入项目名称(例如,HelloWorld)。
    2. 选择 Faces Project 配置。
    3. 选择 Add project to an EAR
  • 点击 Finish
图 1. New Dynamic Web Project 截屏
图 1. New Dynamic Web Project 截屏

创建一个 Web 页面(参见图 2):

  • Project Explorer 中右击 project name
  • 从上下文菜单选择 New > Web Page
  • New Web Page 向导中,输入页面名称(例如,hello)。
  • 点击 Finish
图 2. New Web Page 向导
图 2. New Web Page 向导

为页面增加组件

既然已经有了页面,您需要增加组件。您需要在文本栏使用 inputText 组件,outputText 组件显示返回内容。因为要用 Ajax 更新 outputText ,您需要将其放入面板组件。本页中要用到 panelGroup 组件。

增加组件的方法:

  • 从组件板的 Enhanced Faces Components中将 Input 组件拖到页面上。
  • 从组件板中将 Panel Group box 组件拖到 Input 组件下。当激发分组框时,选择 Group
  • 从组件板中将 Output组件拖到 Panel Group 框。

增加 Ajax 支持面板

为使面板内容可通过 Ajax 更新(本例中,是 Output 栏),您需要将面板标记为 "Ajaxable" ,并且为传递给服务器的用户请求配置参数。(参见 图 3。)

  1. 选择 outputText 组件,并切换回 Properties 视图。
  2. 在 Properties视图中,选择 h:panelGroup 标签,它处于左边导航栏 h:outputText标签正上部。
  3. 选择 Ajaxh:panelGroup 标签。
  4. 点击 Allow Ajax updates 复选框。
  5. 选择 Refresh 作为Ajax请求输入。
图 3. panelGroup 属性
图 3. panelGroup 属性

本例使用 Refresh 请求表示参数如何用 Ajax 请求传递。或者,Submit 请求将提交全部表格。由于例子页面的表格仅包括了一个输入栏,所以您无需为 Ajax 请求配置参数。

为了给 Ajax 请求配置参数(图 4):

  • 在 Ajax 属性页面选择 Click to edit Ajax request properties (参见 图 3, 如前所示)。
  • Properties 页面的 hx:ajaxRefreshRequest 标签:
    1. 点击 Add Parameter 从浏览器发送参数。
    2. 在组合框中选择 Input 组件的名称(本例中,text1)。
图 4. ajaxRefreshRequest 属性
图 4. ajaxRefreshRequest 属性

您已经配置了由 Ajax 请求更新的 panelGroup 标签,并使用 Input 栏的值作为请求参数。剩下的就是使得 outputText 组件使用这一参数显示返回(参见 图 5):

  • 选择 outputText组件。
  • Value 栏输入 Hello, #{param.text1}
图 5. outputText Properties
图 5. outputText Properties

初始化 Ajax 请求

如果您回顾应用 Ajax 所需的 四步,您会发现已经完成了前三步。现在,您仅需要识别可以触发 Ajax 需求的事件。为更新返回,您需要在 inputText 组件使用 onblur 事件(参见 图 6):

  • 选择 inputText 组件。
  • 返回 Quick Edit 视图。
  • 在 Quick Edit 视图中:
    1. 左面事件列表中选择 onblur 事件。
    2. 点击 Use predefined behavior 复选框。
    3. 选择 Invoke Ajax behavior on the specified tag 动作。
    4. 选择 panelGroup 的名称作为目标(本例中,group1)。
图 6. Quick Edit 视图
图 6. Quick Edit 视图

现在,您可以存储页面并在服务器上运行。当打开浏览器窗口时,可以在输入栏下面看到“Hello” 文字。一旦用户在其中输入任何字符,返回值将会更新输入栏中的内容。(参见 图 7。)

图 7. 服务器上运行 Web 页面
图 7. 服务器上运行 Web 页面

正如您所见到的,您可以不必书写任何 JavaScript 代码而利用标准 JSF 组件实现一个简单有效的 Ajax 页面。

下面,让我们再看一个复杂一些的例子。


如何为现有 JSF 应用程序增加 Ajax

考虑一个普通的现代电子商务 Web 应用系统:购物车。如果您曾经有过网上购物的经历,您一定已经看到过它。一个典型的购物车会显示用户浏览站点时加入的产品列表,可以改变数量的输入栏,结帐按钮等等。

如果您看过各种各样的购物车,您可能会发现至少有两点相同:

  • 总有一个根据用户输入的数量来更新的按钮,用以重新计算总量、税和价格。
  • 购物车中的商品条目都会提供一个链接以提供产品详细信息的页面。

这里就有文章开始我们所提到的缺陷的实例。购物车仅当用户点击按钮时才会更新,而用户不得不转入其他页面以浏览产品的详细描述。而在 Ajax 的帮助下,用户体验可以得到大幅改进,因为只要数量发生变化总量就会自动更新,而产品详细信息可以在同一页面中得到显示(也许在指定区域,也许在一个弹出窗口中)。

本文结尾部分的参考资源部分中,有一个 可下载的项目演示了如何轻松地将 Web 1.0 的购物车转变为一个 Web 2.0 的应用程序。现在我们来了解一下这个例子,从而可以看到它是如何被构建的,如何使用 Ajax 加以修改的。

注意: 从这时起,所有涉及的东西 (JavaServer Pages™ [JSP™] 名称、组件 ID, 等等) 都是您可以下载的 AjaxSample 的应用程序。请保存 AjaxSample.zip 文件,之后使用 Project Interchange 格式将它导入 Rational Application Developer V7 。

购物车:旧的方式

本例中的购物车使用三种 Java™ beans: Product、CartItem 和 ShoppingCart。 您能够在 Project Explorer 中的 Java Resources 类下的 beans 包找到它们。

  • Product 包括了站点销售的产品信息:ID、名称、描述、图片和价格。
  • CartItem 跟踪购物车中每件商品的数量。
  • ShoppingCart 记录了购物车条目列表(商品和数量对); 计算总数,税和价格; 并且帮助用户根据 ID 查找商品。

图 8 显示, cart.jsp 是一种极为简单的购物车页面的实现方法。

图 8. 购物车
图 8. 购物车

页面使用 dataTable 组件显示购物车中的所有条目。 Quantity 列使用 inputText 栏改变数量。在用户做出所有修改后,他们可以点击 Recalculate 按钮更新总量、税和价格。商品名称为用户提供了进入其他页面的链接以了解商品详细信息——product.jsp。 商品 ID 作为参数传递。(参见 图 9。)

图 9. 商品详细信息
图 9. 商品详细信息

购物车:新的方式

在这里,cartajax.jsp 是相同的购物车页面,但它被重新设计了,利用 Ajax 改善了用户体验。新 cartajax.jsp (图 10) 和老 cart.jsp 有三点明显的不同:

  • 没有 Recalculate 按钮。一旦用户焦点离开输入栏就会更新总量。
  • 当用户鼠标指针移到商品名上时,商品描述会显示在弹出窗口中。
  • 当用户点击商品链接时,商品的所有详细信息会显示在购物车下,而不用更新购物车本身。
图 10. 包含 Ajax 的购物车
图 10. 包含 Ajax 的购物车

让我们看看这种变化如何发生的。为了解释的目的,这一页面使用了所有三类 Ajax 请求。而且,与本文开始仅使用 IDE 属性建立 Ajax 功能不同,这里您可以看到 Source 模式中的 JSF 标签。

去掉 Recalculate 功能

不必使用 Recalculate 按钮,您将包括了总量、税、价格的面板使用 ajaxSubmitRequest 标签声明为 "Ajaxable" ,如 清单1 所示。

清单 1. ajaxSubmitRequest 代码
<h:panelGrid id="totals" styleClass="panelGrid"
  columns="2" style="text-align:right;">
    <h:outputText id="text4" styleClass="outputText"
      value="Sub-total:"></h:outputText>
    <h:outputText id="textTotalPrice" value="#{cart.totalPrice}"
      styleClass="outputText">
        <hx:convertNumber type="currency" />
    </h:outputText>
    ... other output components ...
</h:panelGrid>
<hx:ajaxRefreshSubmit id="refreshTotals"
  target="totals"></hx:ajaxRefreshSubmit>

您这里正使用 Ajax 的 Submit 类型,因此不需传递任何参数,因为全部购物车表格将被提交。这就使得服务器端代码可以使用所有输入栏中的数量。这一面板和 Ajax 标签由 ID 和目标属性连接在一起,它们在本例中是粗体突出的(清单1)。 一旦用户焦点离开输入栏,总数就会更新。因此,您可以使用 inputText 组件的 onblur 事件初始化请求:

清单 2. 通过 Ajax 初始化 Submit 请求的代码
<h:inputText id="textQuantity1" value="#{varproducts.quantity}" styleClass="inputText"
        size="3">
    <hx:behavior event="onblur" behaviorAction="get" targetAction="totals">
    </hx:behavior>
</h:inputText>

hx:behavior 标签是一种十分有效的连接预定义 JavaScript 功能和客户端 JSF 组件事件的方法。本例中,您使用的是 inputText 组件(hx:behaviorh:inputText 的子集)的 onblur 事件(事件属性), 并且希望执行 totals 组件的 get 行为。这里,Totals 是需要更新的面板,get 行为表示“获取内容”,因此:使用 Ajax 更新。

注意:选择 cartajax.jsp 然后 Run On Server 会发现浏览器中的这些标记工作在一起。

一旦焦点离开表中任意输入栏,就会运行与 onblur 事件联系的 JavaScript 代码。 它会发现页面上的 Totals 组件,核实具有与其联系的 ajaxRefreshSubmit 组件,之后通过给服务器发送表格的形式初始化 Ajax POST 请求。 当服务器响应时,Totals 面板会更新为来自于服务器的新内容。

增加弹出描述

下一个例子使用无模型 Dialog 组件(Rational Application Developer V7 中的新组件)显示购物车中的条目描述。由于 Dialog 是一个面板,它的内容更新可使用与 panelGroup 和 panelGrid 组件相同的 Ajax更新方式(清单3):

清单 3. 使用新的 Dialog 组件显示商品描述信息
<hx:panelDialog type="modeless" id="descriptionPopup" styleClass="panelDialog"
        style="background-color: #fff9ca" movable="false"
        align="relative" valign="relative" saveState="false"
        showTitleBar="false">
    <h:outputText id="textDescription1d"
        value="#{cart.selectedProduct.description}"
        styleClass="outputText">
    </h:outputText>
</hx:panelDialog>
<hx:ajaxRefreshRequest id="showPopup" target="descriptionPopup"
    params="$$AjaxROW$$form1:tableEx1:itemid"></hx:ajaxRefreshRequest>

这与之前的标签十分相似,除了这次使用 Ajax 的 Refresh 类型。 因此,您需要为服务器传递一个参数 -- 特别是您想看的商品信息的购物车条目 ID。因为您的条目在一个 dataTable 中, 而JSF仅记录表中每个组建的一个实例,因此必须让服务器端代码知道您需要使用 active 行中的组件值,表示这一行产生请求。为实现它, 需要在ID组件前放入 $$AjaxROW$$

为了当用户停留在条目上时显示或隐藏对话框,您可以使用 Link 组件的 onmouseoveronmouseout 事件, 正如 清单4所示:

清单 4. 当用户鼠标指针停留在条目上时显示或隐藏对话框的代码
<h:outputLink id="link1">
    <h:outputText id="textName1" value="#{varproducts.product.name}"
            styleClass="outputText">
    </h:outputText>
    <hx:behavior event="onmouseover" behaviorAction="get;show"
            targetAction="form1:descriptionPopup;form1:descriptionPopup"></hx:behavior>
    <hx:behavior event="onmouseout" behaviorAction="hide"
            targetAction="form1:descriptionPopup"></hx:behavior>
</h:outputLink>

当用户鼠标指针经过条目上时,你想更新对话框内容(get行为)并且显示这个对话框(show 行为)。 当鼠标指针离开条目时,你想隐藏这个对话框(hide 行为)。

在同一页面显示商品详细信息

最后一步改进就是在同一页面的购物车下显示商品详细信息。 您已有显示一个条目的详细信息的页面:product.jsp。 因此, 您不必重新设计与实现 cartajax.jsp 中相似的标记。 您可以在第三类 Ajax 请求的类型的帮助下使用现有的 JSP 文件:External 请求 (参见 清单5 ):

清单 5. 在同一页面显示商品详细信息的代码
 <h:panelGrid id="product" width="700" style="margin-top: 20px;"
        styleClass="panelGrid">
    <h:outputText id="text8" styleClass="outputText"
        value="Click on a product to see its details here."
        style="color: gray; font-size: 10pt">
    </h:outputText>
</h:panelGrid>
<hx:ajaxExternalRequest id="showDetails" target="product"
        href="product.faces" source="product"
        params="$$AjaxROW$$form1:tableEx1:itemid">
</hx:ajaxExternalRequest>

您可能已经意识到这种模式: 一个面板组件和一个关联的 Ajax 标记。 panelGrid 产品如同存储商品的详细信息一样。如果用户没有选择商品,她仅仅为用户解释页面中各部分的用途。当用户点击了一个商品,面板内容就会根据 panelGrid 更新,与 product.jsp 文件中定义的相似(href 属性)。

这时,Link 组件的onclick 事件初始化了 Ajax 请求,如 清单6 所示:

清单 6. 使用 onclick 事件初始化 Ajax 请求的代码
<h:outputLink id="link1">
    <h:outputText id="textName1" value="#{varproducts.product.name}"
        styleClass="outputText">
    </h:outputText>
    <hx:behavior event="onclick" behaviorAction="get;stop"
        targetAction="product">
    </hx:behavior>
</h:outputLink>

行为中的第二个动作 (stop) 是为了阻止浏览器中的泡沫事件。您不会想本例中看似正常的链接仅仅初始化了一个 Ajax 请求后就停止了处理事件。

您需要做的最后一点改变是如何使当前所选的商品 ID 传递到服务器端。在购物车的 cart.jsp 版本中(此时使用一个链接导向另一页面),您将 ID 定义为连接参数(清单7):

清单 7. 最初的购物车中 ID 定义为参数的连接
<h:outputLink id="link1" value="product.faces">
    <h:outputText id="textName1" value="#{varproducts.product.name}"
        styleClass="outputText">
    </h:outputText>
    <f:param name="itemid" id="param1" value="#{varproducts.product.id}" />
</h:outputLink>

但使用 Ajax 时, 您不再需要真实的连接,只需使用 Ajax 标记的 params 属性传递组件值。因此,不必在连接中包含参数,您只需创建一个包括了紧邻连接的 ID 的组件值,且将这一组件作为 Ajax 参数(参见 列表8):

清单 8. 创建一个隐藏的 ID 输入栏作为 Ajax 参数
<h:outputLink id="link1">
    <h:outputText id="textName1" value="#{varproducts.product.name}"
        styleClass="outputText">
    </h:outputText>
</h:outputLink>
<h:inputHidden id="itemid" value="#{varproducts.product.id}" />

<hx:ajaxExternalRequest id="showDetails" target="product"
        href="product.faces" source="product"
        params="$$AjaxROW$$form1:tableEx1:itemid">
</hx:ajaxExternalRequest>

完成了这个改变,您就完成了对购物车的修改。


其他使用 Rational Application Developer Ajax 工具的方式

虽然您所做的改变演示了 JSF 标签,但仅仅出于解释的目的,在 Rational Application Developer 中您可以很容易的通过拖拽,Properties 视图,Quick Edit 视图复制它们。 这与本文开始部分构建“Hello, world”的过程完全相同。因此,正如您看到的, 您可以改进应用程序的可用性,而不必扔掉任何已有的工作。


下载

描述名字大小
Sample Ajax application for this articleAjaxSample.zip4.7MB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational, Web development
ArticleID=208882
ArticleTitle=JSF 和 Ajax:使用 Rational Application Developer V7 轻松实现 Web 2.0 应用程序
publish-date=04122007