Mark A. Basler's Weblog

All | Java
20060424 Monday April 24, 2006

JSF 1.2 Checkbox in a dataTable populating a list of IDs ...


Recently, I have been working on an application that required a list of database primary keys (IDs) be returned from a search, so the items that were selected could be populated in a Google Map.  Returning a list of IDs to operate against is a common requirement and there a number of ways this could be handled.  The one thing that complicated the use of normal paradigms is that persistence entity beans were being used and I didn't want to introduce presentation implementation details in the bean.  Since we are using EJB 3.0 and JSF 1.2, I used the Glassfish Open Source Application Server to run my tests.

I have read a lot of interesting forum postings were developers are trying to find a lite weight approach to collecting IDs to operate against when they are developing using JSF dataTables.  These developers are familiar with the standard HTML checkbox String array approach and are looking for a similar approach using JSF.  Some of the write ups state the example usecase where items are selected from a cart for deletion.  Hopefully this investigation will help them and others.

There are a few different methodologies that can be used to resolve this problem, the following list are approaches that we investigated:

1) Wrap the entity bean with a class that also exposes the values ("SelectItem") for the checkbox.  Having all the values that are to be captured, represented in a bean is a standard approach when working with dataTables.  I didn't want to use this approach because it requires the introduction of a new wrapper object for each page that used entity beans to back the dataTable.

2) Bind the dataTable to a ManagedBean ("HTMLDataTable") so the children can be looped through to manually reconcile the items selected.  This could be done but all that was needed was a list of IDs.  I thought this approach was overkill and I wanted to use something more lite weight.

3) Send the selected values to a the managed bean using a HTML checkbox through a managed property.  This is more in line with what I was looking for, a lite weight approach the I can use in other situations without introducing a new object or binding to a JSF component.

The code segments below shows how to send a list of checked IDs to a managed bean and the associated setting in the faces-config.xml file.  Note that the SearchBean  is the entity bean used to hold the data being displayed.

search.jsp
 <h:form id="resultsForm">
    <h:dataTable id="results" border="1" value="#{SearchBean.hits}" var="item"
rendered="#{SearchBean.showResults}"        
style="border-style:double; width:600px; border-color:darkgreen">
                
...
<input type="checkbox" name="mapSelectedItems"
value="<h:outputText value='#{item.UID}'/>"/>    
...
<h:commandButton action="#{MapBean.findAllByIDs}" id="mapSubmit" type="submit"
value="Map Checked Item(s)" rendered="#{SearchBean.showResults}"/>
...


faces-config.xml
<managed-bean>
<managed-bean-name>MapBean</managed-bean-name>
<managed-bean-class>       
com.sun.javaee.blueprints.mapviewer.MapBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>items</property-name>
<value>#{paramValues.mapSelectedItems}</value>
</managed-property>           
</managed-bean>



MapBean.java
// search.jsp
public void setItems(String[] items) {
    this.items=items;
}


The only problem I found with this approach is that the Managed Bean has to be in the request scope.  If you wanted to put the bean in the session, a ServletException would be thrown stating "The scope of the referenced object: #{paramValues.mapSelectedItems} is shorter than the referring object".


4)  Since I wanted the MapBean to be in the session scope so the last map could be re-rendered, I ended up using a derivation of approach 3 and retrieved the IDs using the Expression Language Context to resolve a Value Expression.  This allowed the MapBean to be in the session scope and the IDs could be retrieved from the request.  One thing to note is that since is it quite possible that the request values may not be present when the bean is being access from other objects, some checking is required to keep the IDs available.

The code segments below shows how to retrieve a list of checked IDs from a managed bean and the associated setting in the faces-config.xml file.  Note that the SearchBean is the entity bean used to hold the data being displayed.

search.jsp
<h:form id="resultsForm">
    <h:dataTable id="results" border="1" value="#{SearchBean.hits}" var="item"
rendered="#{SearchBean.showResults}"
style="border-style:double; width:600px;border-color:darkgreen">

...
<input type="checkbox" name="mapSelectedItems"
value="<h:outputText value='#{item.UID}'/>"/>    
            
...
<h:commandButton action="#{MapBean.findAllByIDs}" id="mapSubmit" type="submit"
value="Map Checked Item(s)" rendered="#{SearchBean.showResults}"/>
...

faces-config.xml
<managed-bean>
<managed-bean-name>MapBean</managed-bean-name>
<managed-bean-class>
com.sun.javaee.blueprints.mapviewer.MapBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>


MapBean.java
public String findAllByIDs() {
// get selected items from search
    FacesContext context=FacesContext.getCurrentInstance();
    ValueExpression vex=context.getApplication().getExpressionFactory().
createValueExpression(context.getELContext(),"#{paramValues.mapSelectedItems}", String[].class);

String[] itemx=(String[])vex.getValue(context.getELContext());
// since looking up values from request, make sure the values exist before replacing old values
if(itemx != null) {
itemIds=itemx;
}
...

Many thanks to Ed Burns for taking the time to investigate these scenarios with me. His time and effort is deeply appreciated.


Posted by basler Apr 24 2006, 10:51:23 AM PDT Permalink