集合操作的非法语法:如何告诉JSF我不"想要"一个setter

Osc*_*rEi 9 jsf el primefaces jsf-2 jsf-2.2

这个问题可能更像"概念"或"我不懂JSF".

我的场景:我有一个JSF页面(index.xhtml)我在哪里使用p:accordionPanel(但我认为它不重要的是它是什么组件).我想要做的是设置activeIndexes它.

<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}">
// bla bla...
</p:accordionPanel>
Run Code Online (Sandbox Code Playgroud)

和支持bean中的(简化)方法:

public String getActiveIndexesForSections(String holderName){
    String activeSections = "";
    for(Section s : sectionMap.get(holderName)){
        if (s.isActive())
        //add to the string
    }
    return activeSections;
}
Run Code Online (Sandbox Code Playgroud)

现在这在正常页面加载时工作得很好.

但是,如果我点击p:commandButton(with 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
Run Code Online (Sandbox Code Playgroud)

在谷歌搜索/阅读错误消息后,我发现我需要一个setter.

首先:我不想要一个setter - 我真的需要一个或者有没有办法告诉JSF我不想要这个"行为".

其次我意识到提供一个setter并不"那么容易",因为我的方法有一个参数(这样public void setActiveIndexesForSections(String name, String activeIndexes)public void setActiveIndexesForSections(String name)不起作用).我最终想出的是:

创建(通用)"伪属性类":

// 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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

更改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);
}
Run Code Online (Sandbox Code Playgroud)

从以下地方调用它index.xhtml:

<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}">
// bla bla...
</p:accordionPanel>
Run Code Online (Sandbox Code Playgroud)

这有效,但显然是一个丑陋的黑客/解决方法.

处理这种情况的正确方法是什么?或者我正在做的完全错了?

Bal*_*usC 23

需要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>
Run Code Online (Sandbox Code Playgroud)

在封面下,它基本上会externalContext.getRequestMap().put("activeIndex", value)作为setter操作,显然只会工作.


更新:在检查组件源代码时AccordionPanel,我看到了另一个解决方法,因为activeIndexrendered属性求值时不会设置false.因此,只需更改rendered属性即可正常运行:false在更新模型值阶段(第4阶段)进行评估.

<p:accordionPanel multiple="true" 
    activeIndex="#{myController.getActiveIndexesForSections('whatever')}"
    rendered="#{facesContext.currentPhaseId.ordinal ne 4}">
    <!-- bla bla... -->
</p:accordionPanel>
Run Code Online (Sandbox Code Playgroud)

  • 别客气.我不能在自定义`Map`实现上充实答案,因为问题不包含关于`Section`事情的所有必要信息.但到了这一点,你应该能够以这种形式使用它``activeIndex ="#{myController.activeIndexesForSections [whateverVariable]}"`其中`getActiveIndexForSections()`返回一个`Map <String,String>`和`whateverVariable`因此,表示密钥的变量.在get和set操作中,将分别调用`Map#get()`和`Map#put()`.您需要覆盖这些方法以返回并设置所需的值. (2认同)