JSF value binding


Today i will explain three types of value binding:

  • „direct“ value binding (one setter / getter for each value)
  • „list based“ value binding (one setter / getter for a list of values)
  • „map based“ value binding (one setter / getter for a map of values)

Direct value binding

This is the classic usage of value binding in jsf, the following facelets-snippet is self-explanatory:

<h:inputText value="#{bean.foo}"/>
<h:inputText value="#{bean.bar}"/>

Our bean have a setter and a getter for foo and bar:

class Bean {
  private String foo, bar;
  public String getFoo() ...
  public void setFoo(String) ...
  public String getBar() ...  
  public void setBar(String) ...
}

List based value binding

I use this to avoid setter and getter for similar values/properties, i.e.

<h:inputText value="#{bean.product[0]}"/>
<h:inputText value="#{bean.product[1]}"/>
<h:inputText value="#{bean.product[2]}"/>
...

With the „classic“ value binding i have to write a lot of getter/setter (product0, product1, product2,…). But with list based value binding its much easier.

class Bean {
  private static final int MAX_NUMBER_OF_PRODUCTS = 5;
  private final Product[] products = new Product[MAX_NUMBER_OF_PRODUCTS];
  public Bean () {
    for (int i = 0; i < MAX_NUMBER_OF_PRODUCTS; i++) {
    products[i] = null;
  }
 }
 public List<Product> getProduct() {
   return Arrays.asList(Product);
 }
}

The initialization of the internal array/list is necessary for correct working! During runtime jsf will access each value via index (0, 1, 2, …), a uninitialized list will throw a IndexOutOfBounds Exception.

Map based value binding

I used this type of value binding in a generic „CRUD list“ view to implement filtering in a rich:dataTable. I want a selectbox for each „filterable“ column:

<rich:column>
 <f:facet name="header">
   <h:selectOneMenu value="#{bean.filterValue['foo']}">
      <f:selectItems value="#{bean.filterValues['foo']}" />
   </h:selectOneMenu>
 </f:facet>
 ...
</rich:column>

<rich:column>
 <f:facet name="header">
   <h:selectOneMenu value="#{bean.filterValue['bar']}">
     <f:selectItems value="#{bean.filterValues['bar']}" />
   </h:selectOneMenu>
 </f:facet>
 ...
 </rich:column>

To get this to work we need a „customized“ map implementation:

class ArrayMap<K, T> extends HashMap<K, T> {
   
   private static final long serialVersionUID = -5766406097936988242L;
   
   @Override
   public T get(final Object theKey) {
      if (!containsKey(theKey)) {
       // to avoid typos in the ui we throw a exception if a "unknown" value-name is used
        throw new IllegalArgumentException(String.format("The given value-name '%s' is not available", theKey));
      }
      return super.get(theKey);
   }
   public void put(final K key, final T value) {
      super.put(key, value);
   }
 }

This ArrayMap have a getter and a setter (the „normal“ Map have only get and put), for jsf value binding we need a set and a get.

Here is the final filter – bean:

class FilterBean {

  /**
   * Contain all available filter-values for each supported filter-name.
   */
   private final Map<String, List<SelectItem>> filterValues = new ArrayMap<String, List<SelectItem>>();

   /**
    * Contain the current filter-value for each supported filter-name.
    */
   private final ArrayMap<String, Object> filterValue = new ArrayMap<String, Object>();

   public FilterBean() {
      initFilterValue();
   }

   /**
    * @return a map of lists with {@link SelectItem}s
    */
   public Map<String, List<SelectItem>> getFilterValues() {
     return filterValues;
   }

   /**
    * @return the map of filter values
    */
   public ArrayMap<String, Object> getFilterValue() {
     return filterValue;
   }

   /**
    * Prepare <tt>filterValue</tt>, add all supported filter-names to the list.
    */
    private void initFilterValue() {
      filterValue.set("foo", null);
      filterValue.set("bar", null);
    }

   /**
    * Prepare <tt>filterValues</tt>, the given list contains all rows for the datatable. 
    * Each filter-selectbox must show a unique list of available filter-values 
    * (per supported filter column).
    */
   public synchronized void initFilterValues(final List<T> theInitialFilterList) {
     filterValues.clear();
     filterValues.put("foo", getFilterSelectItemsFoo(theInitialFilterList));
     filterValues.put("bar", getFilterSelectItemsBar(theInitialFilterList));}
   }
}

The method initFilterValues(final List<T>) will be triggered from a external bean. Then the filter-bean can create a list of SelectItems for each filter-column.

If the User select a value from such a filter-list the value binding will call our ArrayMap.set method.

Try it😀