具體的現(xiàn)象是這樣的,一個(gè)輸入框需要設(shè)置為不能讓用戶更改,但可以通過(guò)javascript改變,改變后提交,則在服務(wù)器端得到改變后的值進(jìn)行處理。這種需求應(yīng)該也是非常常見(jiàn)的,可是在用JSF實(shí)現(xiàn)時(shí),卻碰到了問(wèn)題。具體JSF代碼如下:
1 <h:form id="theForm">
2 <h:inputText id="theValue" readonly="true" value="#{readonlyBean.theValue}"/>
3 <h:commandButton value="Submit" action="#{readonlyBean.submit}"/>
4 <af:outputText value="Click" onclick="editReadonly();"/>
5 </h:form>
第一個(gè)<h:inputText>就是我們要研究的對(duì)象,第二個(gè)<h:commandButton>是提交的按鈕,第三個(gè)<af:outputText>是為了有一個(gè)地方可以點(diǎn)擊,點(diǎn)擊后調(diào)用javascript來(lái)改變第一個(gè)組件的值,至于af是來(lái)自于ADF,ADF是Oracle的JSF實(shí)現(xiàn),提供了更多的功能,為什么不用<h:outputText>呢?因?yàn)楹芷婀郑瑯?biāo)準(zhǔn)的JSF組件<h:outputText>竟然不接受onclick事件。總之,調(diào)用的editReadonly()的javascript代碼如下:2 <h:inputText id="theValue" readonly="true" value="#{readonlyBean.theValue}"/>
3 <h:commandButton value="Submit" action="#{readonlyBean.submit}"/>
4 <af:outputText value="Click" onclick="editReadonly();"/>
5 </h:form>
1 function editReadonly() {
2 document.getElementById('theForm:theValue').value = "ABCD";
3 }
在服務(wù)器端,Managed Bean中的代碼如下:2 document.getElementById('theForm:theValue').value = "ABCD";
3 }
1 public class ReadonlyBean {
2 private String theValue = "XYZ";
3
4 public void setTheValue(String aValue) {
5 this.theValue = aValue;
6 }
7
8 public String getTheValue() {
9 return theValue;
10 }
11
12 public String submit() {
13 System.out.println( this.getTheValue() );
14
15 return null;
16 }
17 }
以上代碼就是開(kāi)始給theValue以初始值XYZ,然后在提交時(shí)打印出theValue的值。2 private String theValue = "XYZ";
3
4 public void setTheValue(String aValue) {
5 this.theValue = aValue;
6 }
7
8 public String getTheValue() {
9 return theValue;
10 }
11
12 public String submit() {
13 System.out.println( this.getTheValue() );
14
15 return null;
16 }
17 }
運(yùn)行時(shí),首先點(diǎn)擊"Click",看到頁(yè)面上的只讀輸入組件的值變成了ABCD,然后提交,發(fā)現(xiàn)服務(wù)器端打印出來(lái)的值仍然是XYZ,沒(méi)有得到該輸入框的新值。
然而如果直接用HTML來(lái)完成這一功能時(shí),頁(yè)面上有一個(gè)readonly的輸入框,提交到一個(gè)servlet,在servlet中從request中得到該輸入框的值打印出來(lái),那么在用javascript改變了該輸入框的值后,打印出來(lái)的值是變化之后的值,說(shuō)明標(biāo)準(zhǔn)的HTML的readonly的值是會(huì)提交的。
怎么會(huì)這樣呢?JSF的輸入組件設(shè)為readonly之后似乎不提交自己的值?為了解決這一問(wèn)題,首先查看JSF最后生成的HTML頁(yè)面的源代碼,似乎沒(méi)有任何問(wèn)題,也是解析成一個(gè)標(biāo)準(zhǔn)的HTML的<input>,那說(shuō)明在頁(yè)面這個(gè)值是確實(shí)存在,也被提交給JSF框架,只是在后臺(tái)處理時(shí)JSF忽略了該組件,這非常奇怪,為什么要采取這個(gè)和標(biāo)準(zhǔn)HTML不一致的行為?是不是當(dāng)前使用的JSF的實(shí)現(xiàn)的問(wèn)題?在換了好幾個(gè)JSF的實(shí)現(xiàn)之后(Oracle的實(shí)現(xiàn),MyFaces的實(shí)現(xiàn),Sun的RI實(shí)現(xiàn)),結(jié)果都是一樣,看來(lái)只有看看實(shí)現(xiàn)的源代碼才能找到原因了。
下載了MyFaces 1.2的源代碼,然后首先找到HtmlInputText的renderer:HtmlTextRenderer,因?yàn)樘峤粫r(shí)首先調(diào)用組件的renderer的docode方法來(lái)解析request的參數(shù)。然后發(fā)現(xiàn)該類(lèi)是擴(kuò)展了HtmlTextRendererBase,之后發(fā)現(xiàn)是調(diào)用了HtmlRendererUtils.decodeUIInput()來(lái)解析的,在該方法的一開(kāi)始,就發(fā)現(xiàn)如下語(yǔ)句:
1 if(isDisabledOrReadOnly(component))
2 return;
這里充分說(shuō)明了在JSF中readonly和disable一樣,都是不會(huì)將參數(shù)提交給后臺(tái)的。2 return;
原因找到了,那為什么JSF會(huì)采用這種方式。解釋可能要回到JSF是一個(gè)組件的框架這一點(diǎn)上來(lái)。因?yàn)?lt;h:inputText>是一個(gè)組件,那一個(gè)只讀的組件是不是就應(yīng)該不能改變其值呢?
最后則說(shuō)一下怎么繞過(guò)這一限制,從而實(shí)現(xiàn)我們一開(kāi)始想要的功能。實(shí)現(xiàn)的方法則只能是加一個(gè)隱藏的輸入組件,在javascript改變只讀輸入框時(shí),也同時(shí)改變?cè)撾[藏的輸入組件的值,那么提交后就能得到這個(gè)隱藏輸入組件的值。