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 😀
Antworten
Du musst angemeldet sein, um einen Kommentar abzugeben.