Project Dynamic Faces是數個擴充JavaServer Faces技術的的項目之一。Project Dynamic Faces是一個創新型的項目,提供了向基於JavaServer Faces技術的應用軟體增添Ajax功能的方法。它使我們能夠讓應用軟體已經在使用的任何JavaServer Faces組件支援Ajax功能。我們無需對組件進行修改就能夠使它們支援Ajax,我們也無需對應用軟體進行任何修改就可以使它具有Ajax的魔力。
要使應用軟體具有Ajax魔力,我們必須首先確定應用中希望Ajax功能更新的網頁部分。象基於JavaServer Faces技術的開發人員瞭解的那樣,JavaServer Faces網頁是由組件樹表示的。利用Dynamic Faces,我們能夠確定組件樹中的哪個組件會受益於非同步更新。就象使用Ajax更新代表網頁的HTML DOM樹的一部分那樣,我們使用Dynamic Faces更新代表JavaServer Faces網頁的組件樹的一部分。因此,Dynamic Faces機制對於Ajax和JavaServer Faces開發人員而言是熟悉的。
更重要的是,Dynamic Faces使用JavaServer Faces組件模式,使我們能夠以一種更有效方式利用Ajax功能。由於組件模式的協作特性,一些網頁組件上的JavaScript事件能夠觸發該網頁上任何數量的其它組件的非同步更新。Dynamic Faces使得這些非同步更新只是向伺服器發送的一次Ajax請求的結果,而不是導致每次非同步更新的Ajax請求的結果。
Dynamic Faces還利用JavaServer Faces組件模式有效地管理客戶機端和伺服器端的狀態。當Dynamic Faces更新客戶機端上的組件狀態時,它更新的只是已經改變的組件而不是整個樹的狀態。最好的一點是Dynamic Faces在後台完成所有這些操作,而且是以一種與JavaServer Faces技術的生命週期完全一致的方式完成的。
除了簡化嚮應用軟體增添Ajax功能外,Dynamic Faces還向我們提供了增添Ajax功能的方法的靈活性。這篇文章將討論利用Dynamic Faces使應用軟體更具互動性和活力的三種方法:
·利用Dynamic Faces提供的定製ajaxZone標籤確定組件樹中需要被Ajax化的部分。
·利用Dynamic Faces提供的JavaScript庫向單個組件增添Ajax功能。
·在一個網頁中增添支援Ajax的組件,例如jMaki widget。
在學習這些技術前,我們先來看看應用軟體如何才能使用Dynamic Faces技術。
開發利用Dynamic Faces的應用軟體
通過向一個標準的JavaServer Faces 1.2實現中增添Ajax功能,Dynamic Faces利用了JavaServer Faces技術的已耗用時間庫的可擴充性。 Dynamic Faces的核心是定製的Lifecycle和ViewRoot實現。這二個實現是JavaServer Faces技術提供的標準Lifecycle和ViewRoot實現的擴充, 一個標準的Lifecycle對象代表JavaServer Faces生命週期的一個執行個體,一個標準的ViewRoot對象代表一個組件樹的根。聯合使用定製Lifecycle對象和定製ViewRoot對象,使JavaServer Faces生命週期能夠處理Ajax事務,在無需對整個網頁更新的情況下重新顯示組件樹的一部分。這些定製實現服從於不支援Ajax請求的標準實現。
為了使JavaServer Faces技術已耗用時間庫知道定製Lifecycle對象的存在,我們必須在配置描述器中利用一個初始化參數向FacesServlet執行個體報告該對象。
<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <init-param> <param-name>javax.faces.LIFECYCLE_ID</param-name> <param-value>com.sun.faces.lifecycle.PARTIAL</param-name> </init-param> <load-on-startup>1</load-on-startup> </servlet> |
此外,我們還必須將Dynamic Faces依賴的Java Archive(JAR)檔案添加到應用軟體的web archive(WAR)檔案的lib目錄中。因為Dynamic Faces是基於Java Platform Enterprise Edition 5(Java EE 5)的,我們所需要的幾乎所有依賴關係都已經存在。最後一個依賴是Shale Remoting,Dynamic Faces利用它從Java類路徑中載入JavaScript檔案和其它資源。 Shale Remoting依賴於commons-logging,因此我們必須嚮應用軟體提供commons-logging。
最後,我們必須在使用它的每個網頁中說明該Dynamic Faces標籤庫。 對於符合標準的非XML文法的JavaServer Pages(JSP)網頁而言,這種說明如下所示:
<%@ taglib prefix="jsfExt" uri="http://java.sun.com/jsf/extensions/dynafaces" %> |
對於符合XML文法的JavaServer Pages(JSP)網頁而言,這種說明如下所示:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2" xmlns:jsfExt="http://java.sun.com/jsf/extensions/dynafaces" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> |
如果使用Facelets而非JSP,文法與JSP XML的文法非常相似,如下所示:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:jsfExt="http://java.sun.com/jsf/extensions/dynafaces" xmlns:f="http://java.sun.com/jsf/core"> |
好了。我們可以開始利用Dynamic Faces嚮應用軟體中增添Ajax功能了。
作為一種手動設定應用軟體的替代性方案,我們可以使用Dynamic Faces下載包,其中包括面向JSP和Facelets的空白應用軟體。如果使用現成的空白應用軟體,所有的設定工作已經預先完成,我們就可以開始編寫網頁了。
利用ajaxZone標籤更新部分網頁
確定網頁上哪些組件將支援Ajax的一種方式是用Dynamic Faces提供的ajaxZone定製標籤封裝它們。當這樣做時,我們就告訴Dynamic Faces只非同步更新使用ajaxZone標籤確定的組件樹部分。
例如,假設我們有一個帶有一系列按鈕的網頁,客戶在網上購買汽車時,可以用這些按鈕選擇標準或豪華裝修。當客戶點擊一個按鈕時,一系列其它組件的值會發生變化,而不會造成整個網頁被更新。當客戶改變一個組件的值時,汽車的價格也會改變,但不會造成整個網頁的更新。
圖1顯示的是cardemo應用軟體中網頁的一個螢幕快照,它使客戶能夠選擇汽車的配置情況。
圖1顯示了二個由ajaxZone標籤劃分的地區。 當客戶點擊地區2中的一個按鈕時,該地區中組件的值會發生變化。當客戶改變地區2中的一個組件的值時,地區1中Your Price輸出組件的值也會隨之變化。
要實現這樣的功能,我們需要將所有組件封裝在ajaxZone標籤中,如下所示:
<jsfExt:ajaxZone id="zone1"> <h:panelGrid columns="2"> <h:outputText styleClass="subtitle" value="#{bundle.basePriceLabel}"/> <h:outputText binding="#{carstore.currentModel.components.basePrice}"/> <h:outputText styleClass="subtitle" value="#{bundle.yourPriceLabel}"/> <h:outputText value="#{carstore.currentModel.currentPrice}"/> </h:panelGrid> </jsfExt:ajaxZone> <jsfExt:ajaxZone id="zone2" action="#{carstore.currentModel.updatePricing}"> <h:commandButton id="Standard" value="#{bundle.Standard}" styleClass="#{carstore.customizers.Standard.buttonStyle}" actionListener="#{carstore.choosePackage}"/> <h:commandButton id="Deluxe" value="#{bundle.Deluxe}" styleClass="#{carstore.customizers.Deluxe.buttonStyle}" actionListener="#{carstore.choosePackage}"/> <h:outputText value="#{bundle.Engine}" styleClass="optionLabel"/> <h:selectOneMenu styleClass="optionValue" binding="#{carstore.currentModel.components.engine}"/> <h:outputText value="#{bundle.Speakers}" styleClass="optionLabel"/> <h:selectOneRadio styleClass="optionValue" binding="#{carstore.currentModel.components.speaker}"/> </jsfExt:ajaxZone> |
前面的代碼中包含名字分別為zone1和zone2的二個域。象代碼顯示的那樣,zone 1中只有一個輸出組件,因此它不會產生任何Ajax請求; Zone 2域中包含有輸入和輸出組件。當客戶點擊2個按鈕中的1個,或者選擇菜單或選項按鈕列表中的一個選項時,zone2中的組件會啟動一個Ajax請求。這一請求將引起方法運算式#{carstore.currentModel.updatePricing}中定義的操作被調用。
當使用域時,每個Ajax事務將使得網頁中的所有域被更新。前面例子的效果是,當使用者選擇zone 2中的任何輸入組件時,Ajax功能會自動地更新zone 1中的汽車價格資料。
值得指出的是,我們無需編寫一行JavaScript代碼就能夠實現這一例子,我們也無需任何定製組件。這一應用軟體使用了我們熟知的普通而簡單的JavaServer Faces組件,但它們已經能夠支援Ajax功能了。
ajaxZone標籤向網頁創作者提供了一種使用Dynamic Faces的簡單、熟悉、直觀的方式。在最簡單的例子中,ajaxZone標籤能夠向網頁創作者提供所需要的功能。ajaxZone標籤支援許多使我們能夠進一步定製其操作的其它屬性,ajaxZone文檔中包含有其屬性的完整清單。
下面的部分將討論使用Dynamic Faces的另一種方法,它使我們能夠細粒度地控制網頁中組件的Ajax化。
使用Dynamic Faces fireAjaxTransaction方法
為了對與Ajax相關的任務進行細粒度的控制,我們可以使用Dynamic Faces提供的內建JavaScript庫。通過使用現有組件標籤中合適的DynaFaces.fireAjaxTransaction JavaScript函數,我們可以對網頁中組件非同步更新方式有更細粒度的組件級控制。
例如,假設我們希望網頁中的一些組件對一種類型的JavaScript事件━━例如onclick作出響應,並希望該網頁中的其它組件對其它類型的JavaScript事件作出響應。又假設我們希望產生一個Ajax請求的每個組件能夠引起組件樹的不同部分被非同步更新。為了完成這些任務,我們需要使用fireAjaxTransaction函數。
為了使用fireAjaxTransaction函數,需要完成下面的準備工作:
·在一個組件標籤中增添一個JavaScript事件屬性,例如onclick。
·將該屬性的值設定成DynaFaces.fireAjaxTransaction函數。
·向該函數傳遞一系列參數。
下面的代碼是一個簡單的Hello World例子中一個網頁的一部分,使用者可以輸入他或她的名字,點擊一個按鈕,應用程式會用一條包含使用者名稱字的問候語響應使用者的輸入。
... <f:view> ... <h:form id="form" prependId="false"> ... <h:graphicImage value="wave.med.gif"/> <p> Hello, my name is Duke. What is your name? <p> <h:inputText id="input" value="#{testBean.name}"/> <h:commandButton id="button" actionListener="#{testBean.changeText}" onclick="DynaFaces.fireAjaxTransaction(this, {execute: 'input', 'button', render: 'input', 'text'}); return false;" value="click"/> <p> <h:outputText id="text" value="#{testBean.text}"/> </h:form> ... </f:view> |
在上面的例子中,inputText標籤代表一個輸入欄位。當使用者在該輸入欄位中輸入內容,並點擊commandButton標籤表示的按鈕,就會出現下面的情況:
1、DynaFaces.fireAjaxTransaction函數執行,使得Dynamic Faces向伺服器發送一個Ajax請求。
2、伺服器返回一個Dynamic Faces JavaScript庫處理的特別XML響應。
3、合適的庫函數用新的值更新HTML DOM樹。
為了告訴fireAjaxTransaction函數如何產生Ajax請求,我們向它傳輸一系列參數。在上面的例子中,我們向fireAjaxTransaction函數傳遞了2個參數。下面是調用fireAjaxTransaction函數的代碼:
onclick="DynaFaces.fireAjaxTransaction(this, {execute: 'input', 'button', render: 'input', 'text'}); return false;" |
This參數指的是代表觸發該事件的按鈕的標籤,其它參數由指示Dynamic Faces如何處理該請求的選項組成。在這個例子中,選項是execute和render。
Execute和render選項指的是JavaServer Faces生命週期的部分,2所示:
圖2: Dynamic Faces如何利用execute和render選項劃分JavaServer Faces技術的生命週期 |
Execute是在postback期間執行的生命週期部分。它包含有處理模式對象的資料轉換、驗證、更新階段; Render根據對網頁的請求顯示該網頁。
在對fireAjaxTransaction函數的調用中使用的execute選項列出了在JavaServer Faces生命週期的execute部分期間必須處理的組件的ID,Hello World例子中的這行代碼如下所示。
execute: 'input', 'button'
接受使用者名稱的input組件必須執行生命週期的execute部分,因為它的資料必須被儲存到模式對象中。Button組件也應當執行生命週期的execute部分,因為Invoke Application階段是生命週期execute片斷的一部分。
當生命週期的render片斷顯示一個使用Dynamic Faces的網頁時,作為一次Ajax請求的結果,它只顯示該網頁上被選定的組件。我們使用render選項顯示要重新顯示的組件的ID,Hello World例子中的render選項如下所示:
render: 'input', 'text'
在這一例子中,作為Ajax請求的結果,生命週期的render片斷會重新顯示網頁上的input和text組件。當使用者點擊該按鈕時,代表input組件的輸入欄位和代表text組件的輸出文本會被再次顯示。輸入欄位會被重新顯示,清除在點擊該按鈕前使用者輸入的值。輸出文本會被重新顯示,顯示包含在點擊該按鈕前使用者在輸入欄位中輸入的值的資訊。該網頁上的其它組件無需被重新顯示。
除了execute和 render選項外,我們還可以使用其它選項,進一步定製Dynamic Faces處理事件的方式。需要記住的是,使用fireAjaxTransaction函數使我們能夠對網頁中的哪些組件會得益於Ajax有更多的控制。事實上,fireAjaxTransaction函數讓我們能夠能夠使網頁中的任何組件支援Ajax,而無需編寫JavaScript或任何其它代碼。
聯合使用Dynamic Faces和jMaki
至此,我們已經學習了如何使用Dynamic Faces重新顯示支援Ajax的JavaServer Faces組件。但是,如何增添曾經在基於JavaServer Faces的應用軟體中看到的支援Ajax的widget呢?
我們可以利用Project jMaki將喜歡的widget封裝在JavaServer Faces組件中。這樣,我們既能夠享受到JavaServer Faces組件模式的好處,也能夠獲得使用被封裝為JavaServer Faces組件的widget的靈活性。 同時,我們無須編寫為現有組件實現Ajax功能的JavaScript代碼,以及為widget建立JavaServer Faces組件所要求的Java平台代碼。
如何聯合使用jMaki和Dynamic Faces呢?對於網頁創作者而言,這非常簡單,就是將與jMaki widget相關的標籤拖到網頁中。
為了聯合使用jMaki widget和Dynamic Faces,widget開發人員需要對jMaki widget的組件檔案作一些小小的修改。這些修改使jMaki widget能夠充分利用JavaServer Faces技術提供的組件狀態管理系統,正確地轉換Dynamic Faces要求的header。修改的細節超出了三篇文章的討論範圍,讀者可以參閱相關資料。
Dynamic FacesTeam Dev已經完成了轉換3個與Dynamic Faces.聯合使用的jMaki widget所需要的工作。被轉換的jMaki widget是script.aculo.us in-place editor widget、Dojo fisheye widget、Dojo inline-editor widget。
除了轉換fisheye widget,Team Dev還修改了jMaki API,使widget能夠觸發一個JavaServer Faces價值修改事件,如所示:
<a:ajax name="dojo.fisheye" value="#{fishEyeBean.selectedIndex}" valueChangeListener="#{fishEyeBean.valueChanged}" args="{items:[ {iconSrc:'images/150x126_jalopy.jpg',caption:'Jalopy',index:0}, {iconSrc:'images/150x126_luxury.jpg',caption:'Luxury',index:1}, {iconSrc:'images/150x126_roadster.jpg',caption:'Roadster',index:2}, {iconSrc:'images/150x126_suv.jpg',caption:'SUV',index:3} ]}" /> |
Dynamic Faces和jMakiTeam Dev正在加緊工作,確保所有的jMaki widgets支援Dynamic Faces。
現在,讓我們來站在網頁創作者的角度來討論這一問題。要想搞明白如何聯合使用jMaki script.aculo.us in-place editor widget和 Dynamic Faces,我們在一個JavaServer Faces資料表組件中包含該widget,使我們能夠編輯該表中一個單元的值。
在JSP網頁中,我們必須說明要求的標籤庫,以及Dynamic Faces和jMaki標籤,如下所示:
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> <%@taglib prefix="jsfExt" uri="http://java.sun.com/jsf/extensions/dynafaces"%> <%@taglib prefix="a" uri="http://java.sun.com/jmaki-jsf" %> |
下面的代碼添加的是<jsfExt:scripts />標籤:
<f:view> <html> <head> <title>Table with jMaki</title> <jsfExt:scripts /> </head> <body> |
這一標籤顯示Dynamic Faces所要求的JavaScript檔案的<script>元素。
最後,通過包含一個指定被封裝為jMaki widget 的in-place editor的 jMaki ajax標籤,我們就將該widget添加到了網頁上。
<h:form> <h:dataTable ... rows="10" binding="#{ResultSetBean.data}" value="#{ResultSetBean.list}" var="customer"> <h:column> <f:facet name="header"> <h:outputText value="Account Id"/> </f:facet> <h:outputText id="accountId" value="#{customer.accountId}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Customer Name"/> </f:facet> <a:ajax name="scriptaculous.inplace" value="#{customer.name}"/> </h:column> </h:dataTable> ... </h:form> </body> </html> </f:view> |
圖3顯示的是上述網頁:
圖4顯示的是當使用者點擊Customer Name列中一個單元的連結時的情況:
圖4: 使用者點擊Customer Name列中一個連結後的情況 |
需要注意的是,當使用者點擊Customer Name列中的一個元素時,客戶名字元素會被一個輸入組件、一個"OK"按鈕、一個"Cancel"連結所取代,使使用者能夠編輯當前的客戶名字。如果使用者點擊"Cancel"而不是"OK",該單元就會被重新顯示為原來的值;如果使用者輸入一個值,並點擊"OK"按鈕,新的值就會被利用Ajax技術發送到伺服器,使該模式更新為新的值。 然後,被編輯的單元就會被重新顯示。 圖5顯示的是是單元被修改後的網頁:
圖5:在使用者向伺服器提交新的值後,網頁重新顯示為新的值 |
結論
Project Dynamic Faces向我們提供了一種向基於JavaServer Faces的應用軟體中添加Ajax功能的靈活、有效方式,而無需放棄JavaServer Faces組件模式的任何優勢。在內建的JavaScript庫、Ajax實現、Dynamic Faces提供的組件互動模式的協助下,我們會發現利用Dynamic Faces添加Ajax功能更容易了。基於JavaServer Faces的應用軟體能夠得益於jMaki widget提供的更多的靈活性。
本文轉自: http://hi.baidu.com/shker/blog/item/a87a45da48a2a8dab7fd484d.html