Apache MyFaces項目包含了幾個關於JavaServer技術的子項目如果需要了解更多關於JavaServer Faces的知識請參考MyFaces對於JSF的介紹
Apache MyFaces項目提供了以下的功能
JavaServer Faces的實現(MyFaces API和MyFaces Impl模塊)
用於構建JSF相關的web應用程序的組件庫例如MyFaces TomahawkMyFaces TrinidadMyFaces Tobago
JavaServer Faces擴展包例如MyFaces Orchestra
與其他技術和標准的集成模塊(用於開發移動客戶端在本文和後續文章中對這部分功能不進行介紹)
可以在如下位置下載和MyFaces Impl及其各個子項目的發布包及樣例程序MyFaces API
在JSF的子項目中Core子項目視JSF規范的一個實現其他的項目實現了相關的規范例如Portlet Bridge或者為其他的JSF實現添加了擴展注意不僅僅是MyFaces Core同時MyFaces的擴展例如Tomahawk可以和任意的JSF實現協同工作例如Sun Reference Inplementation
MyFaces Corex可以在Tomcat 下工作但是MyFaces Core x要求 Tomcat 環境
MyFaces項目不依賴於其他的項目它是一個獨立的程序
JSF FAQ
javaxfacesSTATE_SAVING_METHOD的值為client和server時的區別?
簡而言之server端狀態降將UI組件的持有的信息在HTTP Session中保存而client狀態將UI組件持有的信息保存在返回給用戶的頁面的隱藏域(hidden field)
client端狀態對於非常大數量的用戶來說比較實用因為不需要在服務器端保存用戶的狀態從而節省了內存但是缺點是在網絡中為每個請求傳送了更多的數據
即使選擇的是client端狀態任何一個sessionscoped實體仍然存在於http session中這個標記只是影響JSF實現將UI組件持有的數據存放在什麼位置
任何實現了StateHolder的組件必須實現saveState(FacesContext)和restoreState(FacesContext Object)方法來幫助JavaServer Faces Implementation來保存和恢復跨越多個請求的組件的狀態
為了設置值必須實現saveState方法這個方法在render響應階段被調用在這期間響應的狀態被設置用來處理後需的請求下面是MapComponent組件的該方法的實現
public Object saveState(FacesContext context) {
Object values[] = new Object[];
values[] = supersaveState(context);
values[] = current;
return (values);
}
該方法初始化了一個數組用來持有保存了的狀態然後保存所有與MapComponent關聯的狀態的所有內容
實現StateHolder的組件必須提供restoreState方法該方法用來恢復保存在saveState方法中保存的組件的狀態restoreState方法在恢復視圖階段被調用在此期間JavaServer Faces Implementation會檢查是否在最後render response階段保存了狀態並判斷是否需要恢復以便進行下次的後退下面是MapComponent組件的restoreState方法
public void restoreState(FacesContext context Object state) {
Object values[] = (Object[]) state;
superrestoreState(context values[]);
current = (String) values[];
}
該方法的參數為FacesContext和Object實例表示存儲組件狀態的數組這個方法將Object數組中保存的數據進行設置
當在自定義的組件類中實現這些方法時確保在部署描述符中指定了需要將這些狀態保存在哪裡即client還是server如果狀態時保存在client整個視圖的狀態都被提供給頁面的一個hidden field
可以通過設置javaxfacesSTATE_SAVING_METHOD上下文參數(contextparameter)
浏覽器總是顯示一個到上一個頁面的鏈接?
默認情況下JSF在內部使用forward操作來在頁面之間導航
所以當一個用戶首先訪問頁面A然後取回一些內容這些內容會被包裝在表單中表單以頁面A作為提交的地址
當用戶接著執行操作提交頁面這時JSF收到post請求回復頁面A的視圖並執行postback處理作為postback處理的一部分頁面A的邏輯可能通知JSF框架應該顯示頁面B
默認情況下JSF會執行一個內部forward到頁面B使頁面B被取回作為用戶提交表單的結果
這時用戶在屏幕上看到頁面B但是浏覽器只知道它將數據發送給了頁面A所以浏覽器導航欄上顯示的URL是頁面A的URL雖然顯示的內容是頁面B不幸的是HTTP/HTML不能使浏覽器知道應該顯示頁面B的URL因此作為JSF的默認行為浏覽器的URL經常是實際顯示內容的頁面的上一頁
不僅僅對於用戶來說是奇怪的同時加入收藏夾的時候也比較困難注意收藏夾的位置一般來說是沒有關系的因為JSF應用程序通常是可交互的並且是狀態豐富的
另外一個缺點是在JSF視圖中使用關聯鏈接是不安全的(例如樣式表的相對路徑或者圖標)當浏覽器提交內容到A並獲取數據共頁面B使用時在返回頁面中的relative links都會被作為相對於它(浏覽器)所知道的最後的url所以第一次查看頁面B時所有相對鏈接都是相對於頁面A的如果A和頁面B不在一個目錄中那麼就會出現問題
這個問題的一個解決辦法就是使用<redirect/>標記來定義導航規則這樣會使用http redirect命令來告訴浏覽器獲取新頁面從而取代執行內部的forward 但是這樣做效率比較低因為浏覽器需要使用第二次請求來獲取它的內容而不是在針對初始的post請求在響應時立即獲取它的內容另外一個重要的問題就是獲取頁面B的內容是在一個單獨的請求中這樣就沒有辦法從A頁面中傳遞任何的請求范圍(requestscoped)數據在這樣的目的使用request范圍的變量非常方便也是很常用的
Tomahawk sandbox庫中包含了s:redirectTracker組件可以臨時將requestscoped變量保存在http session中這樣使用<redirect/>規則就可以在不丟失request范圍變量的情況下使用但是這要求存在http session並且效率低下
MyFaces FAQ
什麼是shared項目?
如果myfacescoretomahawktobagotrinidad都是完全獨立的項目那麼就不需要shared代碼項目每個項目都在各自的名稱空間內維護代碼但是會存在大量的重復代碼並且工作量的浪費因為它們都屬於myfaces項目所以多個子項目可以共用的代碼放在shared項目中以便減少開發和維護的工作量
但是這些子項目又都有各自的獨立釋放周期並且tomahawk/tobago/trinidad都應該運行在任意的JSF實現上而不僅僅是myfacescore目前使用的解決辦法是重命名shared的類的報名代碼被重命名成orgapachemyfacesshared_implorgapachemyfacesshared_tomahawk等等這樣每個子項目就可以在發布的時候包含所有的支持類而不是一個獨立的共享jar文件也不需要關心相同類的定義的沖突對於特定項目的升級不會影響到相同環境下的其他項目
注意最近的MyFaces項目的釋放都是用了shared庫並在源代碼的jar文件中包含了shared項目的源代碼所以不需要添加額外的源代碼jar文件
如何從MyFaces獲取格式良好的HTML輸出?
JTidy項目提供了一個ServletFilter可以將響應的消息在輸出前進行重新的格式化Mozilla Firefox浏覽器提供了一個擴展的View Formatted Source功能
在一些版本的MyFaces中可以通過設置orgapachemyfacesPRETTY_HTML來在webxml文件中啟用pretty輸出但是這個選項從來都沒有被很好的支持因為需要所有的renderer來支持以便其工作可能在以後的MyFaces發布中移除
MyFaces Core和tomahawk發布包中的版本號表示什麼?
MyFaces Core使用三個部分來表示版本例如但是這個值和普通的版本號計數是不同的強兩個數字表示了JSF規范的版本因為二進制的JSF規范的API沒有改變前兩位數字相同的發布包被認為是兼容的所有使用JSF指定特征的既存代碼會同樣可以使用
Tomahawk庫也適用相同格式的版本號但是因為JSF 規范是向後兼容的即兼容JSF 規范所有Tomahawk發布的版本中x同樣可以工作在JSF 上注意tomahawk釋放不保證二進制向後兼容
為什麼DataModel不是可序列化的?
DataModel類(在UIData組件中使用)在顯示和恢復視圖階段不需要保存任何任何狀態因此不需要將它定義為可序列化的
如果需要定義可序列化的managed bean並且它包含一個DataModel類型的成員變量那麼將成員變量定義為transient
為什麼時間顯示不正確?
JSF規范要求默認的date>String轉換器使用標准的UTC時區也叫做GMT時區
MyFaces 或者早期的發布並沒有遵循JSF規范它們默認的使用了服務器的時區
可以通過顯示試用轉換器來進行時區的控制例如
<f:convertDateTime timeZone=Antarctica/South_Pole />
或者
<f:convertDateTime timeZone=#{beantimeZone} />
#{beantimeZone}返回字符串id或者TimeZone實例
當然也可以注冊自定義的converter來覆蓋標准的converter使自定義代碼適用於所有的時間到字符串的轉換
如何在一個managed bean中訪問另外一個managed bean?
有兩種方法來實現訪問同一個webapp中的其他managed bean
使用依賴注入在faces配置文件中定義managed beansmanaged bean的屬性可以被聲明成到其他managed bean的引用
<managedbean>
<managedbeanname>neededBean</managedbeanname>
<managedbeanclass>fqntoNeededBean</managedbeanclass>
<managedbeanscope>session</managedbeanscope>
</managedbean>
<managedbean>
<managedbeanname>usingBean</managedbeanname>
<managedbeanclass>fqntoUsingBean</managedbeanclass>
<managedbeanscope>request</managedbeanscope>
<managedproperty>
<propertyname>neededBean</propertyname>
<value>#{neededBean}</value>
</managedproperty>
</managedbean>
限定條件如下
using bean必須的生命周期小於或者等於被引用的needed bean
using bean必須具有setter方法以needed bean作為參數
beans不能管理彼此的依賴
使用查詢機制下面的代碼可以在MyFaces 中來顯示的通過名字查詢任意的managed bean
FacesContext facesContext = FacesContextgetCurrentInstance();
NeededBean neededBean
= (NeededBean) facesContextgetApplication()
getVariableResolver()resolveVariable(facesContext neededBean);
在MyFaces 中不使用上述的方法而是推薦使用
ELContext elContext = FacesContextgetCurrentInstance()getELContext();
NeededBean neededBean
= (NeededBean) FacesContextgetCurrentInstance()getApplication()
getELResolver()getValue(elContext null neededBean);
同樣可以使用這個代碼來計算任意的JSF表達式
FacesContext facesContext = FacesContextgetCurrentInstance();
NeededBean neededBean
= (NeededBean)facesContextgetApplication()
createValueBinding(#{neededBean})getValue(facesContext);
如何得知一個managed bean的屬性是否都被設置?
managed bean必須具有一個默認的無參構造器所有的managedproperty聲明會調用合適的setter方法但是通常在所有的bean屬性被定義後進行一些初始化的操作
Spring提供了postinistialisation回調功能任何實現InitializingBean的bean會調用afterPropertiesSet方法在JSF中沒有准確的等價操作但是有一些接近的方式
定義bean的setter方法例如public void setInitialized(boolean state)
將下面的屬性作為managed bean的最後一個屬性
<managedbean>
<managedproperty>
<propertyname>initialized</propertyname>
<value>true</value>
</managedproperty>
</managedbean>
JSF規范要求managed properties根據它們聲明的順序進行初始化所以setInitialized方法會在所有其他屬性被調用後進行設置
PhaseListener為什麼會被調用兩次?
JSF規范要求任何JSF實現框架在啟動時自動加載/WEBINF/facesconfigxml所以沒有必要添加如下的context參數
<contextparam>
<paramname>javaxfacesCONFIG_FILES</paramname>
<paramvalue>/WEBINF/facesconfigxml</paramvalue>
</contextparam>
如果在webxml文件中配置了上述信息會迫使JSF實現加載配置文件兩次所以注冊了每個phase listener兩次
dataTables的Action listener和action命令沒有被調用?
如果action源(h:commandLinkh:commandButton)沒有被提供那麼Action Listeners和actions就不會被激活當action源在dataTable上時並且dataTable的value屬性指向一個請求范圍的數據源那麼action源在接下來的請求中就沒有被提供例如
<h:dataTable value=#{requestScopedBeandataModelwrappedData} />
<h:column>
<h:commandLink value=click here action=#{backingBeanwillNotFire} />
</h:column>
</h:dataTable>
action源沒有被rendered是因為數據源在隨後的請求中不存在了(在第一次響應完成後被垃圾回收器進行了回收)
為了解決這個問題使用t:saveState標記或者將request范圍的bean放在session范圍內
<t:saveState value=#{myRequestScopedBeandataModelwrappedData} />
日歷樹等不能工作並發生javascript腳本錯誤?
這是需要配置MyFacesExtensionFilter
一些MyFaces組件不僅僅包含了HTML可能需要額外的支持腳本樣式表圖片等等這些資源包含在MyFaces的jar文件中Extensions Filter添加所需的代碼和URL來提供這些資源給生成的HTML
一些其他的組件例如文件上傳需要解析Multipart請求這也是由Extensions Filter來完成的
使用ExtensionFilter有如下的好處
將MyFaces的組件和應用程序良好的隔離
不需要在頁面或者webapp中添加MyFaces其他的組件相關的代碼或者資源
為MyFaces開發組提供了靈活的更新組件保持透明及向後兼容的功能
為頁面開發人員減輕了壓力
只加載對使用組件的資源
處理MyFaces資源緩存
可以通過如下方式配置extension filter
在webxml文件中將filter映射到JSF頁面例如*jsp以便使filter可以更新頁面中的資源鏈接同時映射filter到/faces/myFaces/ExtensionResources/*路徑這樣可以處理頁面獨立的資源例如圖片javascript腳本文件以及樣式表等等下面是一個配置的例子
<filter>
<filtername>MyFacesExtensionsFilter</filtername>
<filterclass>orgapachemyfaceswebappfilterExtensionsFilter</filterclass>
<initparam>
<paramname>maxFileSize</paramname>
<paramvalue>m</paramvalue>
<description>Set the size limit for uploaded files
Format: bytes
k KB
m MB
g GB
</description>
</initparam>
</filter>
<! extension mapping for adding <script/> <link/> and other resource tags to JSFpages >
<filtermapping>
<filtername>MyFacesExtensionsFilter</filtername>
<! servletname must match the name of your javaxfaceswebappFacesServlet entry >
<servletname>Faces Servlet</servletname>
</filtermapping>
<! extension mapping for serving pageindependent resources (javascript stylesheets images etc) >
<filtermapping>
<filtername>MyFacesExtensionsFilter</filtername>
<urlpattern>/faces/myFacesExtensionResource/*</urlpattern>
</filtermapping>
同樣也可以將使用urlpattern來代替serlvetname元素但是仍然需要/faces/myFacesExtensionResource/*這個映射
<! extension mapping for adding <script/> <link/> and other resource tags to JSFpages >
<filtermapping>
<filtername>MyFacesExtensionsFilter</filtername>
<urlpattern>*jsf</urlpattern>
</filtermapping>
使用這個過濾器不會對性能造成太大的影響即不會延遲響應時間但是由於需要在將響應寫出到客戶端之前在內存中將整個response進行緩存會增加內存的使用
如果只是使用標准的JSF組件而不是用MyFaces擴展的組件以t:打頭的那麼就不需要使用這個filter否則的話就需要進行配置
ExtensionFilter not correctly configured Error?
如果發生如下的錯誤
javalangIllegalStateException: ExtensionsFilter not correctly configured JSF mapping missing JSF pages not covered
並且所有的配置已經正確的設置那麼檢查以下內容
確保配置正確參考
如果使用Servlet 那麼不能使用jsp:forward或者requestgetDispatcher()forward來跳轉到某個頁面因為沒有執行extensions filter作為替代方法要使用responsesendRedirect方法
使用tomahawk:popup標記時發生NullPointerException在HtmlPopupRendererencodedEnd
facet的名字需要硬編碼成popup
庫的依賴情況
MyFaces核心包和組件庫
當發布MyFaces類庫的時候也就是說不是Core框架它會和當前的MyFaces CoreSun Mojarra(即Sun JSF RI)的釋放版本兼容
同時支持一些其他的版本但是不做任何保證
Getting Started
開始Apache MyFaces的第一步應該是查看一下樣例應用程序可以在查看這些程序或者可以通過自定義部署來完成可以通過以下方式來部署
下載Tomcatx/Tomcatx
MyFaces例子下載最新的webapp文件(tomahawkXXXexampleszip)
將MyFaces樣例文件解壓縮到指定目錄
將任何之前在Tomcat中發布過的MyFaces應用程序清除同時清空Tomcat的work目錄並且保證類路徑或者Tomcat的lib(common/lib or shared/lib)中不存在jsfapijar或者jsfimpljar(也就是說Sun API和實現)將simplewar文件或者其他的例子拷貝到Tomcat安裝目錄的webapps目錄啟動Tomcat
也可以使用MyFaces 運行Sun JSF RI樣例程序具體介紹參考這裡
如何在自定義的web應用程序中使用MyFaces?建議步驟
在MyFaces Wiki網站查看與開發環境中servlet容器的兼容性
然後可以使用樣例程序來開始開發例如blankwar將blankwar解壓縮後就構成了需要工作的目錄結構
安裝和配置
在沒有網絡連接的情況下使用Tomahawk
tomahawkjar文件包含了METAINF/facesconfigxml該文件使用了到webfacesconfig__dtd的PUBLIC引用這使得在應用程序服務器啟動的時候訪問來參考該DTD文件這個問題的顯示如下(從Tomcat日志中獲取)
javaxfacesFacesException:Cant parse configuration file:jar:file:/<webcontextpath>/WEBINF/lib/tomahawkjar !/METAINF/facesconfigxml
解決這個問題的辦法如下
在tomahawkjar文件中添加METAINF/webfacesconfig__dtd
修改同目錄的facesconfigxml的到DTD的引用
<!DOCTYPE facesconfig SYSTEM webfacesconfig__dtd>
Apache Tomcat作為Servlet容器
Apache Tomcat x(不包括)
Apache Tomcat x可以和MyFaces共同工作所有需要的jar文件已經在MyFaces提供的war文件中包含
如果在啟動時看到空白頁面那麼需要移除jspjar和commonsel因為Tomcatx將這些文件放在容器外造成了和MyFaces的war文件提供的jar文件沖突
專題項目
JSF狀態管理
StateManager管理狀態但是作為終端用戶必須告訴它需要保存什麼這可以通過實現Serializable接口(正確的使用transient變量)或者實現StateHolder來完成
數據在JSF中以兩種不同的方式存儲在特定范圍的bean中或者在組件樹中特定范圍的bean經常是自解釋的組件的狀態保存在響應中然後再請求到達時被恢復有效的方式是讓數據存儲在組件的page范圍只要頁面不改變的話注意組件保存值的綁定和方法的綁定通過使用EL表達式(#{})所以他們指向的beans不在頁面范圍內存儲到組件樹中
MyFaces有一個SaveState組件叫做<t:saveState>允許將數據作為組件樹的一部分存儲
Immediate屬性是如何工作的?
immediate屬性可以被用來達成如下的功能
允許commandLink或者commandButton來將用戶導航到其他頁面而不需要處理當前頁面的輸入域中的數據特別是這允許在發生驗證錯誤的情況下進行導航典型的例子就是cancel按鈕
允許commandLink或者commandButton在忽略一些輸入域的驗證來觸發後台邏輯這是上面所說的更普遍說法
使一個或多個輸入組件具有驗證高優先級這樣的話如果同一個頁面中有些低優先級的輸入包含不合法的輸入時驗證也不會進行這樣可以減少錯誤消息的顯示
在討論immediate之前首先來看一下JSF請求處理生命周期
Restore View – 創建或者回復前一個頁面
Apply Request Values – 將組件的提交值設置為請求值
Process Validations – 轉換和驗證組件的值如果提交的值是合法的那麼將組件的值設置為提交的值
Update Model Values – 將後台bean的值設置為組件的值
Invoke Application – 執行actionListeners和actions
Render Response – 返回響應
很多開發人員認為使用immediate標記是用來使組件跳過Process Validations階段使用immediate屬性的目的是使組件在Apply Request Values階段被處理
使用immediate屬性意味著組件的值會在applyrequestvalues階段被驗證也就是說在其他的nonimmediate組件的值之前(這些驗證是在processvalidators階段被驗證)任何被標記為immediate的輸入組件如果發生炎症錯誤都會導致處理在完成applyrequestvalues後跳轉到render階段也就是說如果任何immediate組件驗證失敗那麼nonimmediate組件的錯誤消息不會被顯示另外如果immediate組件的新的值和既存的value屬性的值不同那麼會激活一個ValueChangedEvent但是這個事件會在ApplyRequestValues階段最後執行而不是在ProcessValidations階段最後特別的這意味著任意關聯到這個組件的ValueChangeListener都會在其他的immediate !UICommand組件的ActionListener之前執行(假設command組件在頁面的後面發生)
將輸入組件設置為immediate並不影響模型的更新任何的新的值仍然會在Update Model階段被注入(也就是說在任何immediate命令組件執行之後)注意也可以使用ValueChangeListener直接更新模型
使用immediate屬性使組件ActionListener或者action方法在applyrequestvalues階段的最後被執行也就是說在任意非immediate值的驗證和後台bean被更新之前執行
如果是返回一個導航字符串的form的action方法那麼
任意非null字符串會使得生命周期直接運行到renderresponse階段意味著任意非immediate組件的驗證永遠不會被執行這就是為什麼immediate命令組件會以自然的方式來實現cancel操作它甚至在頁面中輸入域驗證失敗的情況當然也就沒有什麼update model階段也就是說用戶輸入的數據被丟棄
null返回值會導致處理正常進行也就是說非immediate組件被驗證然後執行update model(如果不發生驗證錯誤)
如果想讓actionListener方法返回void必須調用
facesContextrenderResponse();
在使用immediate輸入組件時最重要的問題就是用戶新輸入的數值並不是總能在model中訪問因為updatemodel階段還沒有執行
對於頁面中的非immediate輸入組件immediate命令組件的action方法訪問用戶輸入數據的唯一方式就是通過使用組件綁定和通過名稱查詢來獲取指定的UIComponent對象然後調用getSubmittedValue方法來獲取用戶提供的原始字符串這個值沒有被轉換成它的目標類型也不會被驗證
對於immediate輸入組件進行了轉換和驗證的步驟使用對應的UIComponnet組件是可能獲取轉換後的值如果組件在頁面中位於UICommand組件的前面並且觸發了ValueChangeListener這樣就會執行ValueChangeListener
警告如果action方法更新模型但是不進行導航那麼在輸入組件的值通過驗證並更新模型時都會覆蓋後台bean的值
任何immediate 組件的驗證失敗都不會停止immediate命令組件的執行這和nonimmediate輸入組件和命令組件大不相同
學習指南
當學習JSF的時候不需要仔細查看具體的實現下面建議了一個類/方法的列表對學習JSF和MyFaces如何實際工作的很有幫助同時提到了對典型方法的一個簡短描述
javaxfaceswebappFacesServlet
init方法用於啟動基本的faces它演示了如何使用FactoryFinder來創建LifeCycle和FacesContext工廠
service方法演示了LifeCycle對象控制整個JSF處理
ponentUIViewRoot
queueEvent方法在組件決定激活一個valuechange事件(或者其他類型的事件)時被調用事件隊列的有意思的地方是
ponentUIInput的驗證方法
orgapachemlHtmlButtonRendererBase的decode方法
ponentUIComponentBase
getRenderer方法演示了如何使用當前視圖中的renderkitid組件聲明的組件家族和組件聲明的renderertype名稱來決定使用哪一個renderer
javaxfaceswebappUIComponentTag
createComponentInstance演示了在JSP標記引用UIComponent組件並且組建不存在於view眾時如何實例化這個組件通過調用ApplicationcreateComponent(String)注意該createComponentInstance方法調用自己的getComponentType()方法這個方法典型的實現了JSF終端標記類例如orgapachlHtmlCommandButtonTag
ponnetUIInput
processDecodesprocessValidatorsprocessUpdates方法演示了表單數據是如何變成模型的數據通過轉換和驗證的處理對於多數的組件實際的工作過程為Apply ValuesProcess Validators和Update Model階段注意數據從表單中開始加載到組件的提交值字段轉換成組件的本地值字段然後拷貝到後台bean很多條件影響到process的出口例如rendered stateimmediate stateconversion errors和validation errors
結合DataTable和ActionListeners
如果在dataTable的某一行包含command link或者command button可以從javaxfaceseventActionListener中輕松的獲取bean
<h:dataTable value=#{ResultsBeanhitSethits} var=hit>
<h:column>
<h:commandLink>
<f:actionListener type=netjavaOrderActionListener />
<h:outputText value=Order />
</h:commandLink>
</h:column>
</h:dataTable>
可以通過下面的java代碼來獲取bean
public class OrderActionListener implements ActionListener {
public void processAction(ActionEvent anEvent) throws AbortProcessingException {
YourBeanClass tmpBean = null;
// 事件的getComponent方法返回command link或者command button
UIComponent tmpComponent = anEventgetComponent();
// 遍歷command link或者command button的父組件出口父組件為UIData
while (null != tmpComponent && !(tmpComponent instanceof UIData)) {
tmpComponent = tmpComponentgetParent();
}
// 如果事件組件不為空並且父組件為UIData那麼取每行的數據如果是Bean實例那麼強制轉換
if (tmpComponent != null && (tmpComponent instanceof UIData)) {
Object tmpRowData = ((UIData) tmpComponent)getRowData();
if (tmpRowData instanceof YourBeanClass) {
tmpBean = (YourBeanClass) tmpRowData;
//TODO Implementation of your method
}
}
//TODO Exception Handling if UIData not found or tmpRowBean of wrong type
}
}
通過Link或者Button的參數來執行方法
一個典型的情景
表格顯示了對象的集合需要通過點擊edit link或者button來跳轉到要編輯的記錄的詳細頁面
如果熟悉Struts或者其他的MVC框架那麼可能會考慮到傳輸一個主鍵來作為請求參數在請求頁面的URL中被包含
<a /appContext/someActiondo?id=&userAction=prepareEdit>Edit</a>
可以使用JSTL來生成上面的內容
<c:url value=someActiondo var=url>
<c:param name=id value= />
<c:param name=userAction value=prepareEdit />
</c:url>
<a ${url}>Edit</a>
在JSF中有很多方法來處理這種情況下面列舉了三種關於第一種解決方式還存在爭議這裡列舉出來的目的是這可能是開發人員最先想到的
> 使用<f:factionListener >和UIData的getRowData()
可以參考上面的集成DataTable和ActionListeners來完成
> 使用f:param來傳遞參數
使用JSF完成的第一個思路可能就是模擬已經將參數傳遞給了鏈接可以通過在commandButton或者commandLink中使用f:param標記
<t:dataTable var=emp >
<h:commandLink id=editLink action=#{employeeActionprepareEdit}>
<h:outputText value=#{msgedit}/>
<f:param name=id value=#{empid}/>
</h:commandLink>
然後獲取處理請求參數的句柄
FacesContext context = FacesContextgetCurrentInstance();
Map map = contextgetExternalContext()getRequestParameterMap();
String employeeID = (String) mapget(id);
上述的方式是可行的但是需要開發人員處理String可能需要自己編寫轉換器另外需要添加額外的代碼來獲取map保存的參數有更簡潔的方法來完成
> 使用<t:updateActionListener />
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26067.html