This is one of those problems that I couldn’t find a complete example for when I needed it, so hopefully this will save somebody else the extra time it took me to piece it together.
We frequently need to have data tables in our UI, and allow the user to select a subset of those items for action. In JavaServer Faces, this means having a DataTable, each row having its own checkbox. But when the action is triggered, how to we find which items the user has selected.
The first step is to add a boolean property to your objects that can represent the selection. If you have a lot objects in your domain that will need this property, you may want to consider adding this to an interface or parent bean, otherwise, you can add it directly to your domain object. As a caveat, I don’t like adding properties to my domain objects that are for UI use only, but in this case, I’m keeping this as simple as possible. Sometimes pragmatism wins.
package com.stevideter.domain; public class SelectableItem { private Integer id; private String name; private boolean selected; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } } |
In your view, you’ll use the dataTable to display the items, including the checkbox:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<f:view><html><body>
<h:form>
<h:dataTable id="itemsTable"
value="#{SelectionBean.selectedItems}" var="item" >
<f:facet name="header">
<h:outputText value="Items" />
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
<h:selectBooleanCheckbox value="#{item.selected}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="name" />
</f:facet>
<h:outputText value="#{item.name}"></h:outputText>
</h:column>
</h:dataTable>
<h:commandButton title="selectItems"
value="Select Items"
actionListener="#{SelectionBean.submitSelections}" />
</h:form>
</body></html></f:view> |
Now when you click the command button to trigger the submitSelections event, the boolean values for selected items will be set, and you can iterate through your list to find the selected items and act on them as needed.
package com.stevideter.BackingBean; import java.util.List; import javax.faces.event.ActionEvent; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import com.stevideter.domain.SelectableItem; import com.stevideter.manager.DomainManager; public class SelectionBean { private DataModel selectableItems; private DomainManager manager; public void populateSelectableItems(ActionEvent event) { selectableItems = new ListDataModel(); selectableItems.add(manager.getSelectableItems()); } public void submitSelections(ActionEvent event) { List<SelectableItem> items = (List<SelectableItem>)selectableItems.getWrappedData(); List<SelectableItem> selectedItems = new ArrayList<SelectableItem>(); for (SelectableItem item : items) { if (item.isSelected()) { selectedItems.add(item); } } // do what you need to do with selected items } public DataModel getSelectableItems() { return selectableItems; } public void setSelectableItems(DataModel selectableItems) { this.selectableItems = selectableItems; } } |
As you can see, this is a very simple solution. Have you seen any other approaches that work?
Hi,
verry nice and simple solution.
Thanks a lot!
Andy
Hi :
What is the purpose of DomainManager class?
Would you please paste the source code of that class?
Thanks
thank you
yummy,
The DomainManager represents the business layer of the application in this example.
That’s where you’d put the logic for getting your items from the database, for example, to abstract it from the UI/view layer.
I didn’t include it in the post since it would be specific to your needs. I should have explicitly said that in the post, so thanks for pointing out that I didn’t.
Yep it was good….keep write articles about JSF
will be helpful for freshers.
Hai Stevi Deter,
Im new to JSF im developing an applicatiopn in that I’ve a problem:
Actually i want to edit the user registered values dynamically i used datatable to retrive the values from database and i used radio button to select the perticular record, but it was not working properly so i used java script for selecting the radio-button . now problem is if table having only one row then only it took the values dynamically to the next page to edit if table contains more records then it will not working properly please help me out ..
Hope u will reply me..
Very Nice Article !!!
Ujjwal B Soni
Simple and Useful,
Thanks, very nice article.
There is a bug in the jsp code, in the part:
where value should have the following value:
value=”#{backing_selectionBean.selectableItems}”,
as in the backing bean there is no property of type selectedItems, and there is no sense of presenting selectedItems on GUI, but DataModel selectableItems, for which there are setter/getter and it represents the datatable actually.
Whit this correction the code works fine.
Best regards,
Ana
I’ve tested your code and have some questions. Please correct me if I’m wrong.
1. what is the bean scope of your “SelectionBean”?
It seams to me that your code is working only
if the bean scope is “session”.
If this is the case, the solution can be simpler:
Instead of using DataModel, you can just use
ArrayList as “selectableItems” in the class “SelectBean”.
and in the function “submitSelections(..)”, simply walk through selectableItems
and you will get the same results.
2. Suppose the bean scope of “SelectionBean” is “session”. That would imply that
all the selectable items will be sitting in the memory on the server side during
the session period. If the data amount is big and if multiple users are using the same page
at the same time, it would lead memory problems of the server.
3. Suppose the bean scope is “request”. Then the function “populateSelectableItems(..)” would have
to be called in the constructor of the class “SelectionBean” or in a similar way to make sure that once
“SelectionBean” is instantiated, “selectableItems” is always initialized, or
“submitSelections(..)” will not work.
Also, when “populateSelectableItems(..)” is called, “selectableItems” has to refer to the same
selectable items as they were displayed on the page. Otherwise, “populateSelectableItems(..)” would
give wrong results.
Your help will be very appreciated.
Ana and Jianshun;
I’ve been too busy at work to give your comments the fair review they reserve, but I will try soon!
I did intentionally use the DataModel on purpose to show its use in this context. There’s frequently more than one way to do things, so thanks for showing some alternatives!
very useful !
thanks a lot
Piece of code was very useful, could you plz also tell, way to refersh data table with new data.
please tell me the way to do datatable contain together.
can you tell me how to display list on jsf page. I have the list String and want to display each element on jsf throught datatable
We are also using the same approach. But, this approach has a problem
e.g. you have a list of 10 items, and you used same approach to select the fist time and then perform a delete operation on that.
Now, once this is done and we have left 9 items in the list. Suppose, you press F5 from IE browser, now image what will be happen?
It sends, the request again to the server and tables sets again the first time selected because it is on the same index and gets deleted as well.
I am trying to figure out how to resolve this problem, within same approach.
I use a similar aproach. But, like you, I’m also not very fond of messing with my domain objects, introducing an artificial atribute that doesn’t belong there.
Maybe it’s possible to extend the DataModel abstract class as a MultipleSelectionEntityDataModel or something like that. The default DataModel (and its derived concrete classes) models a single selection collection, so we must add methods to get and set the selected rows indexes, and the implementation of the getRowIndex() and setRowIndex() abstract methods may return, for example, the first selected one.
It’s just a thought. I will play with this idea, when I have a spare time, and I inform here if I have any success sith this aproach.
Hi Stevi,
I tried the same but I’m getting following Exception. Do you have any suggestion on this.
Thanks.
java.lang.NullPointerException
at com.geae.empis.bean.UserBean.deletePan(UserBean.java:298)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.el.parser.AstValue.invoke(AstValue.java:131)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
at org.apache.jasper.el.JspMethodExpression.invoke(JspMethodExpression.java:68)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:387)
at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:184)
at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:162)
at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:350)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:141)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:281)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:228)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:216)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:634)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:445)
at java.lang.Thread.run(Thread.java:595)
novino,
I’d look at your UserBean.java file at line 298 and figure out why it’s throwing a null pointer exception.
Thank you! Saved me probably 25 minutes of thinking.
Thanks to all articles and comments , it is very much useful for fresh developers and most of others
thanks a lot , keep posting like wise articles
How to get the jar com.stevideter.manager.DomainManager?????
I really liked the example. It helped me a lot when i was stuck in that piece of code
Hi,
When i tried to add items to javax.faces.model.ListDataModel object, it is showing a compile time error saying ‘add’ method is not supported.
Can you please let me know how can items be added to ListDataModel object.
Thanks
its really good ……..
but i want to delete and update selected items from database using <h:selectBooleanCheckbox
in <h:datamodel tel me how i do that……..
(refering to comment no. 18)
Novino,
i have also got the same problem, i am working on it…if anyone else knows the solution then please help.
This is Exception:
Dec 15, 2011 1:53:31 PM javax.faces.event.MethodExpressionActionListener processAction
SEVERE: Received ‘java.lang.NullPointerException’ when invoking action listener ‘#{Shop.submitSelections}’ for component ‘j_id_jsp_1988918580_18′
Dec 15, 2011 1:53:31 PM javax.faces.event.MethodExpressionActionListener processAction
SEVERE: java.lang.NullPointerException
at com.Shop.submitSelections(Shop.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.el.parser.AstValue.invoke(AstValue.java:172)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
at org.apache.jasper.el.JspMethodExpression.invoke(JspMethodExpression.java:68)
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:99)
at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:771)
at javax.faces.component.UICommand.broadcast(UICommand.java:372)
at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:321)
at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:296)
at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:253)
at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:466)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:619)
And This is my Shop.java
package com;
import java.util.ArrayList;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
public class Shop {
private DataModel selectableItems;
ArrayList items;
ArrayList selectedItems;
public String addToCart()
{
items = (ArrayList)selectableItems.getWrappedData();
selectedItems = new ArrayList();
for (ProductModel item : items) {
if (item.getChecked()) {
selectedItems.add(item);
System.out.print(“\n\nproduct added to cart=”+item.desc+”\n\n”);
}
else{
selectedItems.remove(item);
System.out.print(“\n\nproduct removed from cart=”+item.desc+”\n\n”);
}
}
return “add_to_cart”;
}
public ArrayList getSelectedItems() {
return selectedItems;
}
public void setSelectedItems(ArrayList selectedItems) {
this.selectedItems = selectedItems;
}
}