JSF: How to bind many of h:selectBooleanCheckbox?
I have a problem to bind list of h:selectBooleanCheckbox to my bean. Anybody helps ?
This is not working:
<ui:repeat value="#{cartBean.productsList}" var="cartProduct" varStatus="i">
<h:selectBooleanCheckbox binding="#{cartBean.checkboxes[i.index]}" />
</ui:repeat>
public class CartBean extends BaseBean {
public List<Product> getProducts开发者_Go百科List() {...}
private HtmlSelectBooleanCheckbox[] checkboxes;
public HtmlSelectBooleanCheckbox[] getCheckboxes() {
return checkboxes;
}
public void setCheckboxes(HtmlSelectBooleanCheckbox[] checkboxes) {
this.checkboxes = checkboxes;
}
}
I get error:
javax.faces.FacesException: javax.el.PropertyNotFoundException: /WEB-INF/flows/main/cart.xhtml @26,97 binding="#{cartBean.checkboxes[i.index]}": Target Unreachable, 'checkboxes' returned null
I solved my problem. I used code like below and get what i want (thanks to BalusC blog - http://balusc.blogspot.com/2006/06/using-datatables.html#SelectMultipleRows):
<ui:repeat value="#{cartBean.productsList}" var="cartProduct" varStatus="i">
<h:selectBooleanCheckbox value="#{cartBean.selectedIds[cartProduct.id]}" />
</ui:repeat>
public class CartBean extends BaseBean {
private Map<Integer, Boolean> selectedIds = new HashMap<Integer, Boolean>();
public Map<Integer, Boolean> getSelectedIds() {
return selectedIds;
}
}
Your concrete problem is caused because the binding
attribute is evaluated during view build time, that moment when the XHTML source code is turned into a JSF UI component tree, while the <ui:repeat>
runs during view render time, that moment when the JSF UI component tree needs to produce HTML.
In other words, the #{i.index}
is only available during view render time and evaluates as null
during view build time. In effects, you're doing a binding="#{cartBean.checkboxes[null]}"
There's another conceptual mistake here: you seem to expect that the <ui:repeat>
produces physically multiple <h:selectBooleanCheckbox>
components. This is untrue. There's physically only one <h:selectBooleanCheckbox>
which is reused multiple times to produce HTML based on the currently iterated variable. Actually, binding="#{cartBean.checkbox}"
was been sufficient. However, collecting the values is a story apart. I won't go in detail, but you can find several hints in this answer: Validate order of items inside ui:repeat.
In order to achieve the (apparent) concrete functional requirement of generating physically multiple <h:selectBooleanCheckbox>
components and binding each to a separate array item, you should be using an iteration component which runs during view build time instead of view render time. That's the JSTL <c:forEach>
:
<c:forEach items="#{cartBean.productsList}" var="cartProduct" varStatus="i">
<h:selectBooleanCheckbox binding="#{cartBean.checkboxes[i.index]}" />
</c:forEach>
But, after all, using binding
on a bean property should be avoided as much as possible. Use instead exactly that attribute which you ultimately need: the value
attribute. This way you don't need to do a HtmlSelectBooleanCheckbox#getValue()
everytime. You already figured the right solution with a Map<Integer, Boolean> selectedIds
:
<ui:repeat value="#{cartBean.productsList}" var="cartProduct">
<h:selectBooleanCheckbox value="#{cartBean.selectedIds[cartProduct.id]}" />
</ui:repeat>
See also:
- JSTL in JSF2 Facelets... makes sense?
I don't know if you can bind elements stored in an array. But in your code, the problem is that your HtmlSelectBooleanCheckbox[]
is null
. So maybe change your Java code to:
public HtmlSelectBooleanCheckbox[] getCheckboxes() {
if (checkboxes == null) {
checkboxes = new HtmlSelectBooleanCheckbox[getProductsList().size()];
}
return checkboxes;
}
but I am really not sure if it will work... Maybe the solution is to not bind your HtmlSelectBooleanCheckbox
elements in the Java code. Why do you need to bind them?
精彩评论