【问题标题】:Primefaces CommandButton in DataTable transfers random objectDataTable 中的 Primefaces CommandButton 传输随机对象
【发布时间】:2013-11-28 08:03:27
【问题描述】:

总的来说,我对 PrimeFaces 和 JSF 还是很陌生,我找不到类似情况的主题。 我检查了我没有嵌套表格。似乎我遇到了一些奇怪的行为 - 至少我不知道它为什么会这样做;)

假设我有一个列表。 “模块”类有一个列表。我的目的是在表格中显示数据,并按模块和其下的行中的元素很好地分组。 似乎 subtable 是我的愿望成真了,但我发现它只是用于报告问题,所以它是不可选择的。不幸的是,我需要知道在模块上运行“删除”或“编辑”等操作的选择。 我尝试在子表方面放置一个按钮,其中一个 actionlistener 在支持 bean 中设置当前对象,然后打开一个对话框以提供进一步的操作(在选定的模块上)。我一开始以为它可以工作,但后来注意到按下同一个按钮,一个随机的模块对象被转移到了 backing bean。

<p:dataTable id="displayModules" var="displayModuleBean"
     value="#{displayModuleController.displayModuleBeans}"
     emptyMessage="Es existieren keine Module für diese Kampagne.">
     <p:row>
        <p:column headerText="Vermarkter" />
        <p:column headerText="Platzierung" />
        <p:column headerText="Umfeld" />
        <p:column headerText="Format(e)" />
        <p:column headerText="Startdatum" />
        <p:column headerText="Enddatum" />
        <p:column headerText="Abrechnungsart" />
     </p:row>

     <p:subTable id="displayModuleElements" var="displayModuleElement"
        value="#{displayModuleBean.moduleElements}"
        sortBy="#{displayModuleBean.module.name}" sortOrder="descending">
        <f:facet name="header" style="vertical-align: middle">
             #{displayModuleBean.module.name}

           <p:commandButton alt="Modul bearbeiten" icon="ui-icon-pencil"
              style="float:right"
              actionListener="#{displayModuleController.setSelectedDisplayModuleBean(displayModuleBean)}"
              oncomplete="displayModuleEditDialog.show();"
              update=":tabview:displayModulesDialogForm:editDisplayModules">
           </p:commandButton>
        </f:facet>

        <p:column headerText="Vermarkter">
           <h:outputText value="#{displayModuleElement.advertiserName}" />
        </p:column>
     </p:subTable>
  </p:dataTable>

Backing Bean (DisplayModuleController, SessionScoped):

public void setSelectedDisplayModuleBean(ModuleBean selectedDisplayModuleBean)
{
    this.selectedDisplayModuleBean = selectedDisplayModuleBean;
    this.setSelectedDisplayModuleName(this.selectedDisplayModuleBean != null ? this.selectedDisplayModuleBean.getModule().getName()
        : null);
    this.addFacesMessage(FacesMessage.SEVERITY_INFO, "Set selected ModuleBean: ",
        this.selectedDisplayModuleBean != null ? this.selectedDisplayModuleBean.getModule().getName() : "keins");
}

我什至尝试了在每个选项卡上使用 Accordion Panel 和 Datatables 的解决方法,但我以同样奇怪的行为结束。我还尝试在每一行中添加一个按钮来传输所选元素本身(不是父模块),但随机方式完全相同。

我到底错过了什么?如果有人可以帮助我走上正确的道路,那就太好了;)

我正在使用: PrimeFaces 4.0, 我的面孔 2.1.13, Apache Tomcat 7.0.42

LG, 阿尼

【问题讨论】:

  • 很抱歉把它放在评论中,但也许你会更幸运&lt;f:setPropertyActionListener&gt; - 就像这样:&lt;p:commandLink alt="Modul bearbeiten" icon="ui-icon-pencil" style="float:right" process="@this" oncomplete="displayModuleEditDialog.show();" update=":tabview:displayModulesDialogForm:editDisplayModules"&gt; &lt;f:setPropertyActionListener target="#{displayModuleController.selectedDisplayModuleBean}" value="#{displayModuleBean}" /&gt; &lt;/p:commandLink&gt;
  • 如果displayModuleElementdisplayModuleBean 是相反的关系,那么您可以尝试&lt;f:setPropertyActionListener target="#{displayModuleController.selectedDisplayModuleBean}" value="#{displayModuleElement.displayModuleBean}" /&gt;,但坦率地说,不认为它会改变任何东西。

标签: jsf primefaces datatable commandbutton


【解决方案1】:

EL 方法参数在表单提交期间解码操作方法时进行评估。在构建 JSF 组件树或生成其 HTML 输出时,它们评估。

为了确保评估正确的 EL 方法参数值,在这种特定情况下,您需要确保 #{displayModuleController.displayModuleBeans} 后面的模型在处理表单提交期间完全相同在呈现(显示)表单期间。

为了实现这一点,支持 bean 必须是视图范围的,并且该模型的 getter 必须完全没有任何业务逻辑。模型必须准备好并填写一次性初始化/操作方法,例如@PostConstruct,以防您想“在页面加载”时填充它,或者在操作(侦听器)方法中填充,以防您想预先“在提交时”填充它。

否则,模型可能会关闭,因为您首先通过 unnecessarily 在 getter 方法中像许多初学者一样执行 DB 调用,先对模型进行排序,然后从数据库中重新加载模型。


作为一种完全不同的选择,如果您无法确保模型的完整性,因此您可能会在显示表单和处理表单提交之间发生不兼容的更改,那么您可以使用&lt;f:param&gt; 传递一个静态参数。它将准确地表示该值,因为它在显示表单期间(在呈现响应期间)而不是在处理表单提交期间。

例如

<p:commandButton ... actionListener="#{displayModuleController.setSelectedDisplayModuleBean}">
    <f:param name="id" value="#{displayModuleBean.id}" />
</p:commandButton>

您可以通过&lt;f:viewParam name="id"&gt;@ManagedProperty("#{param.id}")externalContext.getRequestParameterMap().get("id") 设置它,然后根据ID 的DB find() 转换回具体的DisplayModuleBean。另见How can I pass selected row to commandLink inside dataTable?

【讨论】:

  • 感谢您的回复。我在 SessionScope 中有我的模型 bean,这不是比 ViewScope 更高的“级别”(只要用户会话处于活动状态,就会采用相同的实例)?我用 ViewScope 进行了测试,但我担心因为我使用 CDI,因此从 javax.enterprise.context 而不是 javax.faces.bean 包中选择范围定义,一切都像以前一样停止工作;)
  • 是的,但是如果模型在显示表单和处理表单提交之间通过其他方式进行了不兼容的更改,此构造仍然会失败。
  • 好的,所以会话范围没问题,我将确保交付完全相同的列表。谢谢你的建议,我会检查的。
  • 知道了。我的错是将 displayModuleBeans 保存在 TreeMap 和 getDisplayModuleBeans() 中(调用的次数比我想象的要多)返回 new ArrayList(moduleBeans.values())。将地图更改为曾经实例化的列表修复了整个问题。非常感谢 BalusC,我点击了接受按钮。
【解决方案2】:

如果您正在处理实体,您不妨将唯一 ID 作为参数传递给 BackingBean,编写一个选择方法,将 ID 作为参数并将所选对象设置为您的 @WindowScoped BackingBean 的实例。然后你像这样访问你的实例:

 #{myBean.instance.propertyName}

这样您就可以确保使用正确的实例。

【讨论】:

  • 你不是把EL方法参数和&lt;f:param&gt;这样的静态参数混淆了吗?静态参数使用toString() 进行评估,并在视图渲染期间内联到生成的 HTML 输出中。这确实不适用于“完整”实体。您的回答对于当前形式的问题没有多大意义。
  • 完全正确,以当前形式。她可以有一个 ModalPanel 来编辑选定的实例,并通过 EL 访问它。她必须使用&lt;f:setPropertyActionListener&gt; 设置实例的ID 并调用selectInstance() 方法。我希望这是有道理的。
  • 嗯,&lt;f:setPropertyActionListener&gt; 没有任何区别。在处理表单提交期间仍会对其进行评估。仅当您没有 EL 2.2 传递方法参数的功能时才有用。
  • 好的,我的逻辑是 1)。要在一个页面上有一个用于编辑当前实例的表单以及一个表格,编辑表单位于模式面板中。 2)。使用 ajax 在 Backingbean 中选择一个实例并更新编辑表单。 3)。进行一些编辑并使用另一个按钮保留编辑表单。
  • 我建议不要再胡思乱想,仔细阅读我的答案,包括它的链接,直到你肾上腺素激增“我终于明白了!”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 1970-01-01
  • 2021-10-23
  • 2016-01-16
  • 1970-01-01
相关资源
最近更新 更多