我该如何将选定的行传递给dataTable或ui:repeat中的commandLink?

104

我在一个JSF 2应用程序中使用Primefaces。我有一个<p:dataTable>,而不是选择行,我希望用户能够直接在单个行上执行各种操作。为此,我在最后一列中有几个<p:commandLink>

我的问题是:如何将行ID传递给由命令链接启动的操作,以便我知道要对哪一行进行操作?我尝试使用<f:attribute>

<p:dataTable value="#{bean.items}" var="item">
    ...
    <p:column>
        <p:commandLink actionListener="#{bean.insert}" value="insert">
            <f:attribute name="id" value="#{item.id}" />
        </p:commandLink>
    </p:column>
</p:dataTable>

但它总是返回0 - 显然在渲染属性时变量f不可用(如果使用固定值则可以工作)。

有没有其他的解决方案?

4个回答

217

至于原因,<f:attribute> 标签是针对组件本身的(在视图构建时生成),而不是针对迭代行的(在视图渲染时生成)。

有几种方法可以实现需求:

  1. 如果您的 Servlet 容器支持最低 Servlet 3.0 / EL 2.2,则只需将其作为 UICommand 组件或 AjaxBehavior 标记的 action/listener 方法的参数传递即可。例如:

     <h:commandLink action="#{bean.insert(item.id)}" value="insert" />
    

    结合以下内容:

     public void insert(Long id) {
         // ...
     }
    

    这仅需要在表单提交请求时保留数据模型。最好的方法是通过@ViewScoped将bean放置在视图范围内。

    你甚至可以传递整个item对象:

     <h:commandLink action="#{bean.insert(item)}" value="insert" />
    

    使用:

     public void insert(Item item) {
         // ...
     }
    

    如果您提供支持此功能的EL实现,例如JBoss EL,则在Servlet 2.5容器上也可以实现这一点。有关配置详细信息,请参见此答案


  2. UICommand组件中使用<f:param>,它会添加一个请求参数。

  3.  <h:commandLink action="#{bean.insert}" value="insert">
         <f:param name="id" value="#{item.id}" />
     </h:commandLink>
    

    如果你的bean是请求作用域,让JSF通过@ManagedProperty来设置它。

     @ManagedProperty(value="#{param.id}")
     private Long id; // +setter
    

    或者,如果您的Bean具有更广泛的范围或者您想要更细粒度的验证/转换,请在目标视图上使用<f:viewParam>,另请参见f:viewParam vs @ManagedProperty

     <f:viewParam name="id" value="#{bean.id}" required="true" />
    

    无论如何,这样做的好处是数据模型不一定需要在表单提交时保留(当您的bean是请求范围时)。


  4. UICommand组件中使用<f:setPropertyActionListener>。优点是当bean具有比请求范围更广泛的范围时,这可以消除访问请求参数映射的需求。

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:setPropertyActionListener target="#{bean.id}" value="#{item.id}" />
     </h:commandLink>
    

    与...相结合

     private Long id; // +setter
    

    在操作方法中,它将仅通过属性 id 可用。这只需要在表单提交请求中保持数据模型的完整性。最好使用 @ViewScoped 将 bean 放入视图作用域。


  5. 将数据表值绑定到 DataModel<E>,然后再将其包装为项目。

     <h:dataTable value="#{bean.model}" var="item">
    

    使用

     private transient DataModel<Item> model;
    
     public DataModel<Item> getModel() {
         if (model == null) {
             model = new ListDataModel<Item>(items);
         }
         return model;
     }
    

    (如果你在视图或会话作用域的Bean中使用,则必须将其设置为transient并在getter方法中延迟实例化,因为没有实现Serializable)

    然后,你可以通过DataModel#getRowData()访问当前行,无需传递任何内容(JSF根据点击命令链接/按钮的请求参数名称确定行)。

     public void insert() {
         Item item = model.getRowData();
         Long id = item.getId();
         // ...
     }
    

    这还需要保留数据模型以进行表单提交请求。最好使用 @ViewScoped 将Bean放在视图范围内。


  6. 使用 Application#evaluateExpressionGet() 方法以编程方式评估当前的 #{item}

  7.  public void insert() {
         FacesContext context = FacesContext.getCurrentInstance();
         Item item = context.getApplication().evaluateExpressionGet(context, "#{item}", Item.class);
         Long id = item.getId();
         // ...
     }
    

选择哪种方式取决于功能需求以及其他用途是否提供更多优势。我个人会选择 #1,或者当您想要支持servlet 2.5容器时,选择 #2。


1
+1,尽管我更喜欢第二个选项(如果必须支持2.5)。 - Bozho
感谢您详尽的回答。不幸的是,我必须报告,在过滤后的PrimeFaces数据表中(这正是我需要它的场景),只有#1起作用。其他所有方法都只适用于未经过滤的表格。虽然如此,我认为这更多是PrimeFaces的错误,而不是您的答案。 - Michael Borgwardt
这个Bean是请求作用域还是视图作用域? - BalusC
2
“Filtered”指的是像这个展示例子中所示的吗?症状表明过滤操作仅在客户端执行,而服务器端的模型没有被维护。不确定这是否是有意为之。您可以随时提交问题报告。 - BalusC
你的帖子是我读过的最有用的帖子之一。我使用了方法5,因为我被迫使用servlet 2.5。我的问题现在是,是否可能使用ajax发送一个参数与commandLink(就像你的示例中)? - Aditzu

12
在JSF 1.2中,这是通过<f:setPropertyActionListener>(在命令组件内部)完成的。在JSF 2.0中(准确地说是EL 2.2,感谢BalusC),可以像这样完成:action="${filterList.insert(f.id)}

6
该功能并非仅适用于JSF 2.0(它本身可以在Servlet 2.5容器中运行),而是适用于EL 2.2(它是Servlet 3.0的一部分)。 - BalusC

11

在我的视图页面中:

<p:dataTable  ...>
<p:column>
<p:commandLink actionListener="#{inquirySOController.viewDetail}" 
               process="@this" update=":mainform:dialog_content"
           oncomplete="dlg2.show()">
    <h:graphicImage library="images" name="view.png"/>
    <f:param name="trxNo" value="#{item.map['trxNo']}"/>
</p:commandLink>
</p:column>
</p:dataTable>

后端Bean

 public void viewDetail(ActionEvent e) {

    String trxNo = getFacesContext().getRequestParameterMap().get("trxNo");

    for (DTO item : list) {
        if (item.get("trxNo").toString().equals(trxNo)) {
            System.out.println(trxNo);
            setSelectedItem(item);
            break;
        }
    }
}

0

感谢Mkyong的这个网站,这是唯一一个真正为我们解决了传递参数问题的解决方案。

<h:commandLink action="#{user.editAction}">
    <f:param name="myId" value="#{param.id}" />
</h:commandLink>

使用

public String editAction() {

  Map<String,String> params = 
            FacesContext.getExternalContext().getRequestParameterMap();
  String idString = params.get("myId");
  long id = Long.parseLong(idString);
  ...
}

从技术上讲,你不能直接将参数传递给方法本身,而是要传递到JSF请求参数映射中。

1
你的问题与此处所问的不同。你想要保留#{param}映射中的请求参数以供后续请求使用,而不是传递任意参数。你的问题已经在https://dev59.com/Y2Mm5IYBdhLWcg3wRdbe得到了解答。 - BalusC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接