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 支持。

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”的過程完全相同。因此,正如您看到的, 您可以改進應用程序的可用性,而不必扔掉任何已有的工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章