问题
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.
- How do I do this?
- How to get the selected rows or their data in a backing bean?
- Or would it be better to do it with
<h:selectManyCheckbox>
?
回答1:
Your best bet is to bind the h:selectBooleanCheckbox
value with a Map<RowId, Boolean>
property 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
:
<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}" />
which is to be used in combination with:
public class Item {
private Long id;
// ...
}
and
public class Bean {
private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
private List<Item> items;
public void submit() {
List<Item> checkedItems = checked.entrySet().stream()
.filter(Entry::getKey)
.map(Entry::getValue)
.collect(Collectors.toList());
checked.clear(); // If necessary.
// Now do your thing with checkedItems.
}
// ...
}
You see, the map is automatically filled with the id
of all table items as key and the checkbox value is automatically set as map value associated with the item id
as key.
回答2:
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>
回答3:
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).
来源:https://stackoverflow.com/questions/2524514/how-to-use-hselectbooleancheckbox-in-hdatatable-or-uirepeat-to-select-m