如何使用中或选择多个项目?

发布于 2024-08-26 21:26:01 字数 311 浏览 4 评论 0原文

我有一个带有 的 Facelets 页面。每行都有一个 。如果选中该复选框,则应在 bean 中设置相应行后面的对象。

  1. 我该怎么做?
  2. 如何在支持 bean 中获取选定的行或其数据?
  3. 或者使用 会更好吗?

I have a Facelets page with a <h:dataTable>. In each row there is a <h:selectBooleanCheckbox>. If the checkbox is selected the object behind the corresponding row should be set in the bean.

  1. How do I do this?
  2. How to get the selected rows or their data in a backing bean?
  3. Or would it be better to do it with <h:selectManyCheckbox>?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

絕版丫頭 2024-09-02 21:26:01

最好的选择是将 值与 Map 属性绑定,其中 Item 代表后面的对象相应的行。

<h:dataTable value="#{bean.items}" var="item">
    <h:column>
        <h:selectBooleanCheckbox value="#{bean.checked[item]}" />
    </h:column>
    ...
</h:dataTable>
<h:commandButton value="submit" action="#{bean.submit}" />
public class Bean {
    private Map<Item, Boolean> checked = new HashMap<Item, Boolean>();
    private List<Item> items;

    public void submit() {
        List<Item> selectedItems = checked.entrySet().stream()
            .filter(Entry::getValue)
            .map(Entry::getKey)
            .collect(Collectors.toList());

        checked.clear(); // If necessary.

        // Now do your thing with selectedItems.
    }

    // ...
}

您会看到,地图会自动填充所有表项目作为键,并且复选框值会自动设置为与作为键的项目关联的地图值。

这只需要 Item#equals()Item#hashCode()按照合同正确实施

如果您不能保证这一点,那么您最好使用 Map 而不是,其中 RowId 表示行标识符的类型。举个例子,您有一个 Item 对象,其标识符属性 idLong

public class Item {
    private Long id;
    // ...
}
<h:dataTable value="#{bean.items}" var="item">
    <h:column>
        <h:selectBooleanCheckbox value="#{bean.checked[item.id]}" />
    </h:column>
    ...
</h:dataTable>
<h:commandButton value="submit" action="#{bean.submit}" />
public class Bean {
    private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
    private List<Item> items;

    public void submit() {
        List<Item> selectedItems = items.stream()
            .filter(item -> checked.get(item.getId()))
            .collect(Collectors.toList());

        checked.clear(); // If necessary.

        // Now do your thing with selectedItems.
    }

    // ...
}

Your best bet is to bind the <h:selectBooleanCheckbox> value with a Map<Item, Boolean> property where Item represents the object behind the corresponding row.

<h:dataTable value="#{bean.items}" var="item">
    <h:column>
        <h:selectBooleanCheckbox value="#{bean.checked[item]}" />
    </h:column>
    ...
</h:dataTable>
<h:commandButton value="submit" action="#{bean.submit}" />
public class Bean {
    private Map<Item, Boolean> checked = new HashMap<Item, Boolean>();
    private List<Item> items;

    public void submit() {
        List<Item> selectedItems = checked.entrySet().stream()
            .filter(Entry::getValue)
            .map(Entry::getKey)
            .collect(Collectors.toList());

        checked.clear(); // If necessary.

        // Now do your thing with selectedItems.
    }

    // ...
}

You see, the map is automatically filled with all table items as key and the checkbox value is automatically set as map value associated with the item as key.

This only requires that the Item#equals() and Item#hashCode() is properly implemented as per their contracts.

If you can't guarantee that, then you'd better use a Map<RowId, Boolean> instead where RowId represents the type of the row identifier. Let's take an example that you've a Item object whose identifier property id is a Long:

public class Item {
    private Long id;
    // ...
}
<h:dataTable value="#{bean.items}" var="item">
    <h:column>
        <h:selectBooleanCheckbox value="#{bean.checked[item.id]}" />
    </h:column>
    ...
</h:dataTable>
<h:commandButton value="submit" action="#{bean.submit}" />
public class Bean {
    private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
    private List<Item> items;

    public void submit() {
        List<Item> selectedItems = items.stream()
            .filter(item -> checked.get(item.getId()))
            .collect(Collectors.toList());

        checked.clear(); // If necessary.

        // Now do your thing with selectedItems.
    }

    // ...
}
冧九 2024-09-02 21:26:01

在下面的示例中,我使用复选框选择两个或多个产品,以允许用户使用 JSF 2.0 在新网页上比较产品规格。

我花了很长时间才发现以下问题(当然现在完全明显了),所以认为对于那些尝试使用上面的 BalusC 代码使用分页的人来说值得一提(BalusC 的答案很好,比我想象的要简单得多)。

如果您使用分页,您将在该行获得空指针:

if (checked.get(item.getId()))

- 在上面 BalusC 的代码中。

这是因为只有显示的复选框才会添加到地图中(doh;拍拍额头)。
对于那些从不显示复选框的产品,由于分页的原因,此行将导致空指针错误,需要添加检查以忽略这些空指针(假设页面加载时所有复选框均未选中)。为了让用户勾选复选框,他们需要显示分页页面,以便之后一切正常。

如果需要在首页加载时勾选部分或全部复选框,那么这对您没有任何帮助...您必须手动将它们添加到地图中,以便它们在页面加载时正确显示。

注意:因为我使用的是 JPA“数据库中的实体类”对象,所以我还需要在 ProductTbl 实体类中使用 @Transient 作为 id,因为默认情况下,所有变量都被 JPA 视为数据库中的列,除非以 @Transient 为前缀。此外,我还使用第二个链接来重置复选框,该链接调用clearSelections(),而我的“提交”是调用compareSelectedProducts()而不是提交按钮的链接。

完整代码如下:

在从数据库派生的“ProductTbl”实体类中:

@Transient
private Long id;

public Long getId()
{
    return id;
}

public void setId(Long id)
{
    this.id = id;
}

在支持 bean“ProductSelection”中:

private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
private String errorMessage = "";
// List of all products.
private List<ProductTbl> products;
// List of products to compare.
private List<ProductTbl> compareProducts;

// Setters and getters for above...

public String compareSelectedProducts() 
{
    // Reset selected products store.
    compareProducts = new ArrayList();

    for (ProductTbl item: products) 
    {
        // If there is a checkbox mapping for the current product then...
        if(checked.get(item.getId()) != null)
        {
           // If checkbox is ticked then...
           if (checked.get(item.getId())) 
            {
                // Add product to list of products to be compared.
                compareProducts.add(item);
            } 
        }
    }

    if(compareProducts.isEmpty())
    {
        // Error message that is displayed in the 'ErrorPage.xhtml' file.
        errorMessage = "No Products selected to compare specifications. Select two or more products by ticking the check box in the second column 'Cmpr'";
        return "process_ErrorPage";
    }

    // Rest of code to get product specification data ready to be displayed.

    return "process_CompareSelected";
}

public String clearSelections()
{
    // Untick all checkbox selections.
    checked.clear();

    return "process_MainSearchResult";
}

在 JSF 网页“MainSearchResult.xhtml”中:

<h:commandLink action="#{productSelection.compareSelectedProducts()}" value="Cmpr Specification Comparison Table" /> 
<h:commandLink action="#{productSelection.clearSelections()}" value="Clear Selected" />

<h:dataTable value="#{productSelection.products}" rows="#{productSelection.numberRowsToDisplay}" first="#{productSelection.rowStart}" var="item" headerClass="table-header" >
    <h:column>
       <f:facet name="header">
          <h:outputText style="font-size:12px" value="Cmpr" />
       </f:facet>
       <div style="text-align:center;" >
          <h:selectBooleanCheckbox value="#{productSelection.checked[item.id]}" />
       </div>
    </h:column>
</h:dataTable>

在“faces-config.xml”文件中:

<navigation-rule>
    <navigation-case>
        <from-outcome>process_MainSearchResult</from-outcome>
        <to-view-id>/MainSearchResult.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
<navigation-rule>
    <navigation-case>
        <from-outcome>process_CompareSelected</from-outcome>
        <to-view-id>/CompareSelected.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
<navigation-rule>
    <navigation-case>
        <from-outcome>process_ErrorPage</from-outcome>
        <to-view-id>/ErrorPage.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>

In the following example I am using checkboxes to select two or more products to allow the user to compare product specifications on a new web page using JSF 2.0.

It took me a good while to find the following problem (totally obvious now of course) so thought it worth a mention for those trying to use pagination with BalusC's code above (nice answer BalusC, much simpler than I ever imagined it would be).

If you are using pagination you will get nullpointers at the line:

if (checked.get(item.getId()))

-in BalusC's code above.

This is because only displayed check boxes are added to the Map (doh; slap forehead).
For those products whose check boxes are never displayed, due to pagination, this line will result in a null pointer error and a check needs to be added to ignore these null pointers (assuming that all check boxes are unchecked on page load). In order for the user to tick a check box then they need to display the pagination page so all works well there after.

If some or all of the check boxes are required to be ticked on first page load then this will be of no help to you...you will have to manually add those to the Map in order for them to be displayed correctly on page load.

Note: because I am using a JPA 'Entity class from database' object I also needed to use @Transient for the id in my ProductTbl Entity Class as all variables are considered columns in the database by JPA, by default, unless prefixed with @Transient. Also I am using a second link to reset the check boxes, which calls clearSelections(), and my 'submit' is a link calling compareSelectedProducts() rather than a Submit button.

The full code is as follows:

In the 'ProductTbl' Entity class derived from the database :

@Transient
private Long id;

public Long getId()
{
    return id;
}

public void setId(Long id)
{
    this.id = id;
}

In the backing bean 'ProductSelection':

private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
private String errorMessage = "";
// List of all products.
private List<ProductTbl> products;
// List of products to compare.
private List<ProductTbl> compareProducts;

// Setters and getters for above...

public String compareSelectedProducts() 
{
    // Reset selected products store.
    compareProducts = new ArrayList();

    for (ProductTbl item: products) 
    {
        // If there is a checkbox mapping for the current product then...
        if(checked.get(item.getId()) != null)
        {
           // If checkbox is ticked then...
           if (checked.get(item.getId())) 
            {
                // Add product to list of products to be compared.
                compareProducts.add(item);
            } 
        }
    }

    if(compareProducts.isEmpty())
    {
        // Error message that is displayed in the 'ErrorPage.xhtml' file.
        errorMessage = "No Products selected to compare specifications. Select two or more products by ticking the check box in the second column 'Cmpr'";
        return "process_ErrorPage";
    }

    // Rest of code to get product specification data ready to be displayed.

    return "process_CompareSelected";
}

public String clearSelections()
{
    // Untick all checkbox selections.
    checked.clear();

    return "process_MainSearchResult";
}

In the JSF Web page 'MainSearchResult.xhtml':

<h:commandLink action="#{productSelection.compareSelectedProducts()}" value="Cmpr Specification Comparison Table" /> 
<h:commandLink action="#{productSelection.clearSelections()}" value="Clear Selected" />

<h:dataTable value="#{productSelection.products}" rows="#{productSelection.numberRowsToDisplay}" first="#{productSelection.rowStart}" var="item" headerClass="table-header" >
    <h:column>
       <f:facet name="header">
          <h:outputText style="font-size:12px" value="Cmpr" />
       </f:facet>
       <div style="text-align:center;" >
          <h:selectBooleanCheckbox value="#{productSelection.checked[item.id]}" />
       </div>
    </h:column>
</h:dataTable>

In the 'faces-config.xml' file:

<navigation-rule>
    <navigation-case>
        <from-outcome>process_MainSearchResult</from-outcome>
        <to-view-id>/MainSearchResult.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
<navigation-rule>
    <navigation-case>
        <from-outcome>process_CompareSelected</from-outcome>
        <to-view-id>/CompareSelected.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
<navigation-rule>
    <navigation-case>
        <from-outcome>process_ErrorPage</from-outcome>
        <to-view-id>/ErrorPage.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
七分※倦醒 2024-09-02 21:26:01

通过 发送参数的一种方法是通过复选框的标题发送。在 ValueChangeListener 中,您可以使用 getAttributes().get("title") 从组件获取它。这在您想要发送 id 值作为参数(而不是选定的行索引)的情况下很有帮助。

One way to send in a parameter via <h:selectBooleanCheckbox> is to send it in via the title of the Checkbox. In the ValueChangeListener, you can get it from the component using a getAttributes().get("title"). This helps in cases where you want to send an id value as a parameter (as opposed to the selected row index).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文