這篇文章只限於在JSF1.2 下的一種特殊情況。 由於JSF2.0 以後EL支持用戶在method里加argument, 所以不可以使用<f:attribute>的辦法可以直接換成在EL中直接用argument pass那個特定的attribute.
problem define:
在使用jsf的時候常常我們需要自己cunstomize tag, 但是問題是在customize tag的時候不容易給<f:attribute>留接口, 因爲很多情況下一個customized tag裏會包含好幾個其他的tag, 所以讓<f:attribute>這種在每個tag中都會出現的tag變得很難定義。所以在這種情況下,這種customized tag裏是不會給<f:attribute>這樣的tag留接口的。
但是這並不意味着我們在用customized tag的時候不可以使用<f:attribute>。
解決辦法:
假設現在我們需要在JSF page中做個表格,表格中每一行顯示某一個項目從第一年到第九年的花費, 而每個列顯示若干個項目在某一特定年份的花費 。 可能只顯示數字看着比較單調, 所以我們在表格中的每個column裏使用我們自己定義的一個tag, 例如 <ctm:customizedInputText>,來顯示出具體的金額並且顯示金額符號。如下
description | Year1 | Year2 | ... | Year9 |
---|---|---|---|---|
food expense | 2000$ | 1800$ | ... | 1700$ |
drink expense | 1600$ | 1400$ | ... | 1500$ |
total | 3600$ | 3200$ | ... | 3200$ |
爲了區分total 那個row 和 其他的row, 那麼我們也需要一個變量來定義這個row, 假設我用下邊的class來給表格賦值:
Class Order{
String description ;
Map<String, BigDecimal> amount;
boolean total;
}
然後List 裏儲存的數據會是這樣的
{
description: "food expense" , amoutMap:{ row1:2000 , row2:1800 ... row9: 1700}, total:false
description: "drink expense" , amoutMap:{ row1:1600, row2:1400... row9: 1500}, total:false
description: "total" , amoutMap:{ row13600 , row2:3200 ... row9:3200}, total:true
}
假設現在我們需要在用戶在表格中任意一個customizedInputText中從新輸入值的時候計算出這個列的總價格,那我們需要在我們自己的customizedInputText裏extend原本<h:inputText> 的 valueChangeListener, 然後在backing bean中定義這個listener用來計算total row 的值,所以用戶並不需要填寫total那行的值, 而那一行會被自動計算出來
那我們的代碼大概會是這樣的
<h:dataTable value="#{order.orderList}" var="row">
<h:column id = "col_discription">
<f:facet name="header">description</f:facet>
<ctm:customizedTextField value="#{row.description}"/> </h:column>
<h:column id = "col1">
<f:facet name="header">year1</f:facet>
<ctm:customizedTextField value="#{row.amountMap["row1"]}" readOnly="row.total" valueChangeListener="calculateColumn1"/>
</h:column>
<h:column id = "col2">
<f:facet name="header">year2</f:facet>
<ctm:customizedTextField value="#{row.amountMap["row2"]}" readOnly="row.total" valueChangeListener="calculateColumn2"/>
</h:column> .....
<h:column id = "col9">
<f:facet name="header">year9</f:facet>
<ctm:customizedTextField value="#{row.amountMap["row9"]}" readOnly="row.total" valueChangeListener="calculateColumn9"/>
</h:column>
</h:table>
上邊的代碼是完全可行的, 但是有個很嚴重的問題, 就是我們有幾個列就必須定義幾個valueChangeListener, 這樣會使代碼變的難以維護。 如果不是使用customized tag, 我們可以用以下方法給valueChangeListener一個值, 這樣一來我們就只用定義一個valueChangeListener:
<h:inputText value="#{row.amountMap["row1"]}" readOnly="row.total" valueChangeListener="calculateColumnTotal">
<f:attribute name="columnName" value="column1" >
</h:inputText>
然後在我們的backing bean 裏, 用下面的方法就能把設置的attribute拿出來
public void calculateColumnTotal(ValueChangeEvent event){ String attribute = (String) event.getComponent().getAttributes().get("attributeName"); //handle change via attribute retrieved }
但是由於customized tag沒辦法用上述方法直接得到attribute的值, 所以得需要特殊一點的方法來獲取。 下邊是這個問題的解法:
1) 把customized tag 放到一個<h:panelGroup> 裏,然後在那個panelGroup裏定義你想要的attribute 如下:
<h:dataTable value="#{order.orderList}" var="row">
<h:column id = "col_discription">
<f:facet name="header">description</f:facet>
<h:panelGroup id="col1pl">
<ctm:customizedTextField value="#{row.description}"/> </h:column>
<h:column id = "col1">
<f:facet name="header">year1</f:facet>
<h:panelGroup id="col1pl">
<ctm:customizedTextField value="#{row.amountMap["row1"]}" readOnly="row.total" valueChangeListener="calculateColumn"/>
<attribute name="column" value="COL1"/>
</h:panelGroup>
</h:column>
<h:column id = "col2">
<f:facet name="header">year2</f:facet>
<h:panelGroup id="col2pl">
<ctm:customizedTextField value="#{row.amountMap["row2"]}" readOnly="row.total" valueChangeListener="calculateColumn"/>
<attribute name="column" value="COL2"/>
</h:panelGroup>
</h:column> .....
<h:column id = "col9">
<f:facet name="header">year9</f:facet>
<h:panelGroup id="col9pl">
<ctm:customizedTextField value="#{row.amountMap["row9"]}" readOnly="row.total" valueChangeListener="calculateColumn"/>
<attribute name="column" value="COL9"/>
</h:panelGroup>
</h:column>
</h:table>
然後在valueChangeListener裏用如下方法就能獲取到attribute的值:
public void calculateColumnTotal(ValueChangeEvent event){ String attribute = (String) event.getComponent().getParent().getParent().getAttributes().get("attributeName"); //handle change via attribute retrieved }