集合操作的非法语法:如何告诉 JSF 我不"want"二传手



这个问题可能更像是"概念性的"或"我不懂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>

最新更新