JSF 的事件驅動模式雖優雅,但 1.2 版本在細節處理上仍有不足。通常頁面驗證失敗時,JSF 會將用戶剛輸入的錯誤值(如在綁定 int 屬性的輸入框輸入 "abcd")重新渲染顯示,而非後台原值,這符合預期。然而若頁面包含可編輯的 <h:dataTable>,驗證失敗時,DataTable 內輸入組件卻會顯示後台綁定的原值,而非用戶剛輸入的值——除非 DataTable 內部自身有錯誤輸入。這顯然違背用戶體驗。
<h:form id="mainForm">
<h:messages style="color:red" />
名字:<h:inputText id="listName" value="#{Test1.listName}">
<f:validateLength maximum="5"/>
</h:inputText>
<h:commandButton value="Submit" action="#{Test1.save}"/>
測試用:<h:inputText id="testName" value="#{Test1.testName}"/>
<h:dataTable headerClass="header" rowClasses="header" bgcolor="blue" cellspacing="1" cellpadding="0" border="0" value="#{Test1.listDataModel}" id="data" var="p">
<h:column>
<f:facet name="header"><h:outputText value="Name"/></f:facet>
<h:outputText style="background-color:white;width:100%" id="name" value="#{p.name}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Sex"/></f:facet>
<h:inputText id="sex" value="#{p.sex}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Age"/></f:facet>
<h:inputText id="age" value="#{p.age}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Email"/></f:facet>
<h:inputText id="email" value="#{p.email}"/>
</h:column>
</h:dataTable>
</h:form>
查閱 JSF 源碼發現,DataTable 有特殊渲染機制:只有當它被嵌套在另一個 DataTable 內部,或自身內部值有錯誤時,才會渲染 submittedValue(用戶提交的值);否則直接渲染後台綁定值。
在目標 DataTable 外層再包一個無實際數據的 DataTable(value="1"),強制觸發 submittedValue 渲染邏輯。雖稍顯不雅,但 JSF 1.2 下無更優雅辦法,期待 JSF 2.0 改進。
<h:dataTable value="1"><!-- 外層空 DataTable -->
<h:column>
<h:dataTable headerClass="header" rowClasses="header" bgcolor="blue" cellspacing="1" cellpadding="0" border="0" value="#{Test1.listDataModel}" id="data" var="p">
<!-- 原有列定義保持不變 -->
</h:dataTable>
</h:column>
</h:dataTable>
完整 JSP 代碼見原文,Java 端僅含 Getter/Setter,此處不贅述。若有更好方案歡迎分享。