6 Replies Latest reply on Oct 20, 2006 10:11 AM by texan

    Implementing a multi-select table

    zzzz8

      I'm having trouble implementing a multi-select table in my project. By multi-select table, I mean a datatable where one can select via a checkbox one or more table rows. When a JSF action is invoked, the action operates on the chosen row(s).

      I'm implementing this in an ugly fashion right now. Within a JSF datatable, I add a column that is composed of a boolean checkbox (h:selectBooleanCheckbox). I also bind this datatable to a UIComponent on my backing bean. When the JSF action is invoked, I go through each datatable row, checking to see if the HtmlSelectBooleanCheckbox object has been selected for that row. If it has been selected, then I add the row data onto a list. I then move on to the next row and repeat... It's ugly, but it works.

      However, I'm moving over to Seam and it's failing. I'm getting the following exception:

      javax.faces.el.EvaluationException: Cannot set value for expression '#{selectJobList.removeUIData}' to a new value of type javax.faces.component.html.HtmlDataTable
      ...
      ...
      java.lang.IllegalStateException: No conversation context active


      I'm assuming this is due to the use of binding the datatable... However, I need the binding to figure out which boolean checkbox was selected.

      So my questions are -

      1. What am I doing wrong here?
      2. What is the best way of implementing a multi-select table in Seam?

      TIA.

        • 1. Re: Implementing a multi-select table
          texan

          I think that one way is to map your checkbox to an attribute on the entity. In your listener method, you iterate through the data model list and call something like "getSelected()" or whatever the boolean attribute is named.

          I'd would like to know if there is a way in Seam to use the @DataModelSelection annotation on a collection instance variable instead of a single bean, but I'm not far enough along in my project to need that (so I haven't tried it).

          • 2. Re: Implementing a multi-select table
            zzzz8

            Hi texan,

            That's one way of doing it, but I don't want to modify the entity (or class) to add a "selected" attribute in the entity. Moreover, there are cases when one cannot (easily) do this - e.g. the class has been created by JAX-RPC, etc. It would be cleaner if one did not have to pollute the entity with an extra attribute.

            • 3. Re: Implementing a multi-select table
              texan

              Did you find a workable solution to this?

              • 4. Re: Implementing a multi-select table
                jazir1979


                You may find this to be overkill, but rather than modifying your entity I suppose you could wrap it in a value object that contains the selected attribute.

                "zzzz8" wrote:
                Hi texan,

                That's one way of doing it, but I don't want to modify the entity (or class) to add a "selected" attribute in the entity. Moreover, there are cases when one cannot (easily) do this - e.g. the class has been created by JAX-RPC, etc. It would be cleaner if one did not have to pollute the entity with an extra attribute.


                • 5. Re: Implementing a multi-select table
                  zzzz8

                  What I did is basically added some code in the getters and setters methods that are bound to the boolean checkbox. I'm pretty sure there's a better way of doing this, and if anyone knows, please tell me. But I believe this is probably what jazir had in mind and here's what I implemented a while back:

                  @Name("testJobList")
                  @Stateful
                  public class TestBean implements TestLocal {
                   @DataModel("JobList")
                   private List<JobStatus> statusList = null;
                  
                   @DataModelSelection("JobList")
                   private JobStatus jobSelected;
                  
                   private Map<String, Boolean> selectedMap = null;
                  
                   private boolean selected;
                  
                   @Factory("JobList")
                   public final void retrieveJobs() {
                   selectedMap = new HashMap<String, Boolean>();
                  
                   List<JobStatus> jobStatus = ... ; // get jobStatus object
                   }
                  
                   public final String performAction() {
                   for (JobStatus tempJob : statusList) {
                   // Assuming a JobStatus job has fields (with getters) attr1
                   String attr1 = tempJob.getAttr1();
                   if (selectedMap.get(attr1)) {
                   // perform action code here...
                   }
                   }
                   return "goToPage";
                   }
                  
                   public final boolean isSelected() {
                   if (jobSelected != null && selectedMap != null) {
                   String key = jobSelected.getAttr1();
                   Boolean value = selectedMap.get(key);
                   if (value == null) {
                   return false;
                   } else {
                   return value;
                   }
                   } else {
                   return false;
                   }
                   }
                  
                   public final void setSelected(final boolean inputSelected) {
                   if (selectedMap != null && jobSelected != null) {
                   String key = jobSelected.getAttr1();
                   selectedMap.put(key, inputSelected);
                   }
                   }
                  
                   @Remove
                   @Destroy
                   public void destroy() {
                  
                   }
                  }


                  Here's the JSP markup:

                  <h:form>
                   <h:dataTable value="#{JobList}" var="selectJob">
                   <h:column>
                   <h:selectBooleanCheckbox value="#{testJobList.selected}" />
                   </h:column>
                   <h:column>
                   <f:facet name="header">
                   <h:outputText value="attr1" />
                   </f:facet>
                   <h:outputText escape="true" value="#{selectJob.attr1}" />
                   </h:column>
                   <h:column>
                   <f:facet name="header">
                   <h:outputText value="attr2" />
                   </f:facet>
                   <h:outputText value="#{selectJob.attr2}" />
                   </h:column>
                   <h:commandButton value="performAction"
                   action="#{testJobList.performAction}" type="submit" />
                   </h:form>


                  This code for the getters and setters is probably a little muddled because I'm using the Tomahawk extended datatable to sort things (I took out the Tomahawk extended datatable JSF markup from the JSP). In this case, my unique identifier for the object would be attr1. I believe the setters and getters could probably be simpler (and one should probably use a List instead of a Map), something like this:

                  List selectedList;
                  
                   public final boolean isSelected() {
                   return selectedList.contains(jobSelected); // or return selectedList.contains(jobSelected.getAttr1());
                   }
                  
                   public final void setSelected(final boolean inputSelected) {
                   if (inputSelected) {
                   selectedList.add(jobSelected); // or selectedList.add(jobSelected.getAttr1());
                   }
                   }


                  But the whole point here is that one needs to add some more code (more than the usual stuff) in the getters/setters in the value binding for the boolean checkbox - and make a note that JSF iterates through all the rows of the datatable - and that one can use this to your advantage in the getters/setters. This works for me and I didn't have to modify the entity... I was probably not clear in my explanation (I gotta go to sleep :) ), but I hope this helps.

                  • 6. Re: Implementing a multi-select table
                    texan

                    That's a novel approach that I wouldn't have thought of.

                    I also think that it wouldn't be too bad to wrap the bean in a decorator class, as Jazir suggested, except for the small overhead of creating a wrapper for each instance in your collection.

                    For myself, I went low-budget and just added a "selected" attribute to the domain object. While this in an old anti-pattern (failing to decouple your business logic from your UI logic), I've begun to have more tolerance for that in recent years, probably due to the easy refactoring capabilities of Eclipse. That is, most patterns like that exist for a couple of reasons: (1) avoid having to change too much code when the UI changes, and (2) allowing you to reuse your business objects in multiple UI implementations. We all got excited at the prospect of #2 several years ago, but after about 10 years of Java development, I have actually never had more than one UI for an application. I guess I've led a sheltered life.

                    Anyway, now I don't worry too much about that sort of thing, and Seam certainly makes it easy to violate a few (hopefully outdated) patterns like this.