这篇文章只限于在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 }