<h:selectOneMenu with conditional <f:selectItems 显示选项两次

<h:selectOneMenu with conditional <f:selectItems shows options twice

我想使用 selectOneMenu 让用户选择一个值。在某些情况下,我想禁用菜单中显示的值之一。我尝试在 selectItems 和 selectOneMenu 上使用渲染,并在菜单周围添加了一个 ui:fragment,但我总是从显示的两个列表中获取所有值。任何想法如何防止这种情况发生? 在这里,我最近的最后一次尝试再次导致列表和有问题的项目在其中启用和禁用一次:

<ui:fragment rendered="#{cc.attrs.showP==true}">
    <h:selectOneMenu id="type" binding="#{cc.type}">
        <f:selectItems value="#{typeDAO.findAll()}"/>
    </h:selectOneMenu>
</ui:fragment>
<ui:fragment rendered="#{cc.attrs.showP==false}">
    <h:selectOneMenu id="type" binding="#{cc.type}">
        <f:selectItems value="#{typeDAO.findAll()}" var="item" itemDisabled="#{item=='P'}"/>
    </h:selectOneMenu>
</ui:fragment>

我猜我找到了 - 虽然回答我的问题有点奇怪。我认为这是因为菜单的可能值是在评估呈现的属性之前创建的,并且因为我确实将两个菜单绑定到相同的 variable/id 我得到了两个菜单的所有项目。因此,我现在使用了不同的名称,然后在我的复合组件中有一些逻辑来检查使用了哪个名称并继续使用正确的值。有效:-)

对我来说有点奇怪的是,作为开发人员,您必须知道与渲染发生的时间相比,何时构建属性列表。我最近遇到了与 foreach 和 repeat 类似的问题。有什么方法可以将这些事情作为我能记住的一些总体概念的一部分来了解,或者这真的是个案吗? 谢谢大家!

你的具体问题是因为你在物理上将多个组件绑定到同一个变量。

<h:selectOneMenu ... binding="#{cc.type}" />
<h:selectOneMenu ... binding="#{cc.type}" />

如果binding后面的getterreturns非null,那么JSF将使用它而不是创建一个新的。基本上,第二个标签将重用在第一个标签中创建的组件,并且 set/add 全部 attributes/items 到它。

您的具体情况至少可以通过两种方式解决:

  1. 使用 JSTL 有条件地构建 JSF 组件树,而不是使用 JSF 有条件地呈现 HTML 输出。您不应该在 JSF 组件树中有多个物理组件共享相同的 binding 更不用说相同的 id.

    <c:if test="#{cc.attrs.showP}">
        <h:selectOneMenu id="type" binding="#{cc.type}">
            ...
        </h:selectOneMenu>
    </c:if>
    <c:if test="#{not cc.attrs.showP}">
        <h:selectOneMenu id="type" binding="#{cc.type}">
            ...
        </h:selectOneMenu>
    </c:if>
    
  2. 编写代码 DRY。 IE。摆脱所有代码重复。

    <h:selectOneMenu id="type" binding="#{cc.type}">
        <f:selectItems value="#{typeDAO.findAll()}" var="item" itemDisabled="#{not cc.attrs.showP and item eq 'P'}" />
    </h:selectOneMenu>
    

另请参阅:

  • How does the 'binding' attribute work in JSF? When and how should it be used?
  • JSTL in JSF2 Facelets... makes sense?