webtier@glassfish.java.net

h:dataTable w/contained input elements and state

From: <webtier_at_javadesktop.org>
Date: Thu, 22 Jul 2010 12:15:23 PDT

Hi

I hope someone can help me. I'm trying to create a form somewhat like the one in allEvents.xhtml of the Virtual Trainer app in Ed Burns' JSF 2.0 book. My form is similar in that it uses an h:dataTable to display a set of rows that include an h:selectBooleanCheckbox. (In my case, the row also contains an h:inputText). So far, that's all working fine.

However, to complicate things, I've added an <h:selectOneMenu immediate="true" onchange="this.form.submit"/> to change the contents of the datatable based on the menu selection. This is where things start to go wrong. When the menu selection changes, the dataTable does update to show the new set of rows. However, there seems to be some state in the dataTable or the view that does not get reset when the content changes.

This causes a couple of problems: When the initial menu selection is 'noSelectionOption' and the dataTable value is 'an empty list', then the menu selection changes, none of the input items' values are set when the form is submitted. It seems like dataTable remembered that the list is empty, and subsituting a longer list is confusing it. Or, is it the input items themselves? When I initialize the menu selection to one that has a non-empty dataTable list associated with it, things work as desired, but changing menu selections again confuse things (checkboxes remain checked, rows beyond those in the initial list don't get their values set.) Hopefully, you're still with me.

It seems like this should be do-able. What am I missing? I've boiled it down to a simple example. Your help is greatly appreciated. Here's the code.


selectitems.xhtml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:cc="http://java.sun.com/jsf/composite/components"
>

    <ui:fragment rendered="#{!empty facesContext.messageList}">
      <h:messages layout="list" />
    </ui:fragment>

    <h:form id="options-form">
<div>
      <h:outputLabel for="categories">Category</h:outputLabel>
      <h:selectOneMenu id="categories" tabindex="3" value="#{optionsBean.category}" immediate="true" onchange="this.form.submit()" required="true">
        <f:selectItem itemValue="#{optionsBean.categoryNotSelected}" itemLabel="-- select one --" noSelectionOption="true" />
        <f:selectItems value="#{optionsBean.categories}" />
      </h:selectOneMenu>
<div>
</div>
      <h:outputLabel for="optionlist">Options</h:outputLabel>
      <h:dataTable immediate="true" styleClass="option-table" id="optionlist" value="#{optionsBean.optionData}" var="data" binding="#{optionsBean.optionTable}">
        <h:column>
          <h:selectBooleanCheckbox id="ctselect" value="#{optionsBean.optionSelected}" />
          <h:outputLabel for="ctselect" value="#{data.label}" />
          <ui:fragment rendered="#{!empty data.detailLabel}">
            <h:inputText id="ctdetail" value="#{optionsBean.optionDetail}" />
            <h:outputLabel for="ctdetail" value="#{data.detailLabel}" />
          </ui:fragment>
        </h:column>
      </h:dataTable>
</div>
      <h:commandButton action="#{optionsBean.submit}" type="submit" value="Submit" class="button primary" tabindex="21" /> <h:button outcome="/index" value="Cancel" class="button" tabindex="22" />
      
    </h:form>

</html>



OptionsBean.java

package org.example;

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIData;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;

@ManagedBean
public class OptionsBean
  {
  int category = getCategoryNotSelected();
  private UIData optionTable;
  private DataModel<OptionTableRow> optionData = new ListDataModel<OptionTableRow>();
  private List<OptionTableRow> optionList = new ArrayList<OptionTableRow>();

  // static data for demonstration use
  private static List<SelectItem> categories = new ArrayList<SelectItem>();
  private static List<OptionTableRow> sizeOptionList = new ArrayList<OptionTableRow>();
  private static List<OptionTableRow> colorOptionList = new ArrayList<OptionTableRow>();
  private static List<OptionTableRow> styleOptionList = new ArrayList<OptionTableRow>();

  static
    {
    OptionsBean o = new OptionsBean();
    sizeOptionList.add( o.new OptionTableRow( "small" ) );
    sizeOptionList.add( o.new OptionTableRow( "medium" ) );
    sizeOptionList.add( o.new OptionTableRow( "large" ) );
    sizeOptionList.add( o.new OptionTableRow( "other", "specify" ) );

    colorOptionList.add( o.new OptionTableRow( "red" ) );
    colorOptionList.add( o.new OptionTableRow( "green" ) );
    colorOptionList.add( o.new OptionTableRow( "blue" ) );
    colorOptionList.add( o.new OptionTableRow( "other", "specify" ) );

    styleOptionList.add( o.new OptionTableRow( "modern" ) );
    styleOptionList.add( o.new OptionTableRow( "traditional" ) );

    categories.add( new SelectItem( 1, "size" ) );
    categories.add( new SelectItem( 2, "color" ) );
    categories.add( new SelectItem( 3, "style" ) );
    }

  public OptionsBean()
    {
    setCategory( 3 );
    }

  public int getCategoryNotSelected()
    {
    return -1;
    }

  public boolean isOptionSelected()
    {
    OptionTableRow row = (OptionTableRow) optionTable.getRowData();
    System.out.println( "is " + row.label + " selected? " + row.selected );
    return row.selected;
    }

  public void setOptionSelected( boolean selected )
    {
    OptionTableRow row = (OptionTableRow) optionTable.getRowData();
    System.out.println( "set " + row.label + " selected=" + selected );
    row.selected = selected;
    }

  public String getOptionDetail()
    {
    OptionTableRow row = (OptionTableRow) optionTable.getRowData();
    return row.detail;
    }

  public void setOptionDetail( String detail )
    {
    OptionTableRow row = (OptionTableRow) optionTable.getRowData();
    row.detail = detail;
    }

  public String submit()
    {

    int count = 0;
    for ( OptionTableRow opt : optionList )
      if ( opt.selected )
        count++;

    if ( count == 0 )
      {
      FacesContext fc = FacesContext.getCurrentInstance();
      fc.addMessage( null, new FacesMessage( FacesMessage.SEVERITY_ERROR, "At least one option must be selected", null ) );
      return null;
      }

    return "showoptions";
    }

  public UIData getOptionTable()
    {
    System.out.println( "getOptionTable()" );
    return optionTable;
    }

  public void setOptionTable( UIData optionTable )
    {
    System.out.println( "setOptionTable()" );
    this.optionTable = optionTable;
    }

  // public List<OptionTableRow> getOptionList()
  // {
  // return optionList;
  // }
  //
  // public void setOptionList( List<OptionTableRow> optionList )
  // {
  // this.optionList = optionList;
  // }

  public DataModel<OptionTableRow> getOptionData()
    {
    System.out.println( "getOptionData, category=" + category );
    optionData = new ListDataModel<OptionTableRow>( optionList );
    optionData.setRowIndex( -1 );

    return optionData;
    }

  public void setOptionData( DataModel<OptionTableRow> optionData )
    {
    System.out.println( "setOptionData" );
    this.optionData = optionData;
    }

  public List<SelectItem> getCategories()
    {
    return categories;
    }

  public void setCategories( List<SelectItem> categories )
    {
    OptionsBean.categories = categories;
    }

  public int getCategory()
    {
    return category;
    }

  public void setCategory( int category )
    {
    this.category = category;
    switch ( category )
      {
      case 1:
        optionList = sizeOptionList;
        break;
      case 2:
        optionList = colorOptionList;
        break;
      case 3:
        optionList = styleOptionList;
        break;
      }

    }

  public class OptionTableRow
    {
    private String label;
    private boolean selected = false;
    private String detailLabel = null;
    private String detail = null;

    public OptionTableRow()
      {
      }

    public OptionTableRow( String label )
      {
      this.label = label;
      }

    public OptionTableRow( String label, String detailLabel )
      {
      this.label = label;
      this.detailLabel = detailLabel;
      }

    public boolean isSelected()
      {
      return selected;
      }

    public void setSelected( boolean selected )
      {
      this.selected = selected;
      }

    public String getDetail()
      {
      return detail;
      }

    public void setDetail( String detail )
      {
      this.detail = detail;
      }

    public String getLabel()
      {
      return label;
      }

    public void setLabel( String label )
      {
      this.label = label;
      }

    public String getDetailLabel()
      {
      return detailLabel;
      }

    public void setDetailLabel( String detailLabel )
      {
      this.detailLabel = detailLabel;
      }

    public String toString()
      {
      StringBuffer sb = new StringBuffer();
      sb.append( label );
      if ( !selected )
        sb.append( " [not selected]" );
      else
        if ( detailLabel != null )
          sb.append( " " + detailLabel + "=" + detail );
      return sb.toString();
      }

    }

  }

showoptions.xhtml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:cc="http://java.sun.com/jsf/composite/components"
>

      <h:outputLabel for="optionlist">Options</h:outputLabel>
      <h:dataTable immediate="true" styleClass="option-table" id="optionlist" value="#{optionsBean.optionData}" var="data" >
        <h:column>
          #{data.selected}
          <h:outputLabel for="ctselect" value="#{data.label}" />
          <ui:fragment rendered="#{!empty data.detailLabel}">
            <h:outputText id="ctdetail" value="#{data.detail}" />
            <h:outputLabel for="ctdetail" value="#{data.detailLabel}" />
           </ui:fragment>
        </h:column>
      </h:dataTable>

</html>
[Message sent by forum member 'markroy']

http://forums.java.net/jive/thread.jspa?messageID=478310