这个问题可能更像是"概念性的"或"我不懂JSF"。
我的场景:我有一个JSF页面(index.xhtml
),我使用p:accordionPanel
(但我不认为它是什么组件)。我要做的是设置它的activeIndexes
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}">
// bla bla...
</p:accordionPanel>
和后台bean中的(简化)方法:
public String getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return activeSections;
}
现在这在正常的页面加载中工作得很好。
但是,如果我点击一个p:commandButton
(与ajax=false
)(或任何其他"发送"数据回服务器我猜)-我得到以下例外:
/WEB-INF/tags/normalTextSection.xhtml @8,112 activeIndex="#{myController.getActiveIndexesForSections(name)}": Illegal Syntax for Set Operation
// bla..
Caused by: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation
在谷歌搜索/阅读错误信息后,我发现我需要一个setter
。
首先:我不想要setter——我真的需要setter吗?或者是否有办法告诉JSF我不想要这个"行为"?
其次,我意识到提供setter并不是那么"容易",因为我的方法有一个参数(所以public void setActiveIndexesForSections(String name, String activeIndexes)
或public void setActiveIndexesForSections(String name)
不起作用)。最后我想到的是:
创建一个(泛型)"Pseudo-Property-class":
// just a dummy class since the class is recreated at every request
public class Property<T> implements Serializable {
private T val;
public Property(T val) {
this.val= val;
}
public T getVal() {
return val;
}
//no need to do anyhting
public void setVal(T val) {
}
}
更改bean方法:
public Property<String> getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return new Property<String>(activeSections);
}
并从index.xhtml
调用它:
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}">
// bla bla...
</p:accordionPanel>
这可以工作,但显然是一个丑陋的hack/变通方法。
处理这种情况的正确方法是什么?或者我所做的是完全错误的?setter需要记住表单提交时的活动索引。基本上,您需要将其绑定为值表达式(带有属性),而不是方法表达式(如操作方法),也不是不可修改的集合(如activeIndex="#{param.tab}"
)。就像输入值一样。从技术上讲,你确实做得"完全错误";)
这个要求是可以理解的。如果您真的对更改的活动索引不感兴趣,因此希望在每次表单提交时将它们重置为默认值,那么您可以通过使用<c:set>
将结果存储为请求属性来绕过它。这样,您将欺骗EL在请求属性映射中设置它,而不是在预期的bean属性中设置它。
<c:set var="activeIndex" value="#{myController.getActiveIndexesForSections('whatever')}" scope="request" />
<p:accordionPanel multiple="true" activeIndex="#{activeIndex}">
<!-- bla bla... -->
</p:accordionPanel>
在幕后,它基本上将externalContext.getRequestMap().put("activeIndex", value)
作为setter操作,这显然是可行的。
Update:在检查AccordionPanel
组件的源代码时,我看到了另一个解决方案,即当rendered
属性评估false
时,activeIndex
不会被设置。因此,只需更改rendered
属性,使其行为完全如下:在更新模型值阶段(第四阶段)评估false
。
<p:accordionPanel multiple="true"
activeIndex="#{myController.getActiveIndexesForSections('whatever')}"
rendered="#{facesContext.currentPhaseId.ordinal ne 4}">
<!-- bla bla... -->
</p:accordionPanel>