ListModel and Databinding Enhanced Combobox

Jumper Chen, Engineer, Potix Corporation
January 4, 2008

Version

Applicable to ZK 3.0.2 Freshly (zk-3.0.2-FL-2008-01-04 and later)

Introduction

In the previous article(this), we have described how to realize the feature of auto-complete with Combobox. The solution is not very good because you need to manipulate the data set inside the view component directly. So this implementation breaks the traditional MVC pattern, which separates the data (model) from user interface (view) via controller to communicate between. However, from now on, ListModel now supports combobox so you no longer have to mix the model with view. Besides, ListSubModel also supports to implement auto-complete in an easier way.

In the article, we will demonstrate you how to use ListModel and ListSubModel to achieve auto-complete with combobox without additional implementation. org.zkoss.zul.ListSubModel interface is designed for Combobox, and the renderer of combobox will use ListSubModel to get the subset of data from list model to render the data in combobox according to users' input.


ListModel Supports Combobox

Here is a ListModel example. We use the SimpleListModel, which implements the ListSubModel interface, to filter the data model of combobox to achieve the feature of auto-complete.


The demo zul file is shown below.

<zk>
  <zscript>
    String[] data = new String[30];
    for(int j=0; j &lt; data.length; ++j) {
      data[j] = "option "+j;
    }
    ListModel strset = new SimpleListModel(data);
  </zscript>
  <combobox id="list" width="200px" model="&#36;{strset}"/> 
</zk>

As you can see, this example is similar to the live data demo from our demo site. The SimpleListModel will fetch a maximum of 10 rows from data model of combobox, but if you'd like to display more than 10 rows, you have to implement the ListModel by yourself.

Note: The concept of Combobox's list model is different from Listbox. In Listbox, the data of UI is load-on-demand from ListModel, but in Combobox, it loads all data from list model to UI at the first time, but if ListSubModel interface is also implemented, only those data which qualifies users input criteria will be loaded.

In the SimpleListModel class, we need to implement a method getSubModel() that returns a subset model from combobox's model as follows.

 public ListModel getSubModel(Object value, int nRows) {
    final String idx = value == null ? "" : objectToString(value);
    if (nRows < 0) nRows = 10;
    final LinkedList data = new LinkedList();
    for (int i = 0; i < _data.length; i++) {
      if (idx.equals("") || _data[i].toString().startsWith(idx)) {
        data.add(_data[i]);
        if (--nRows <= 0) break; //done
      }
    }
    return new SimpleListModel(data);
  }

As you can see, the getSubModel() will return the subset of the list model. It finds all data whose prefix is the same with the value. So, you could implement the method as your want.
Note: the nRows is that the number of rows are suggested to return (as the returned ListModel instance). It's a suggestion for developer to follow.

Databinding Supports Combobox

In this demo, we use the feature of Databinding to bind the data set to combobox. This is a simple example that we need to prepare a data set, e.g. a collection of VO (value object) or POJO, and to specify annotations as values within components’ declarations or their properties directly. If you have been exposed to databinding, you could skip this section detail.


This demo zul file is separated two parts as follows.

  • First: DataSet

    <zscript>
    public class Order {
      private String _orderName;
      private String _orderNumber;
      public void setOrderName(String o) {
        _orderName = o;
      }
      public String getOrderName() {
        return _orderName;
      }
      public void setOrderNumber(String n) {
        _orderNumber = n;
      }
      public String getOrderNumber() {
        return _orderNumber;
      }
    }   
        int count = 30;
        List orders = new LinkedList();
        for(int j= 0; j &lt; count; ++j) {
          Order o = new Order();
          o.setOrderName("OrderName - " + j );
          o.setOrderNumber("OrderNumber - " + j);
          orders.add(o);
        }   
        selected = orders.get(0);
      </zscript>

    As you can see, we declare an Order object and create it into the orders collection, which associates with the data binder via a target attribute "model", and we need to pre-declare the selected variable for data bound to the "selectedItem" attribute of the combobox.

  • Second: UI Component

    <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?> 
    <window width="500px">
    
     ... // DataSet above
    
    <combobox model="@{orders}" selectedItem="@{selected}" value="@{selected.orderName}">
           <comboitem self="@{each=order}" label="@{order.orderName}" value="@{order.orderNumber}"/>
       </combobox>
      <grid>
        <auxhead>
          <auxheader align="center" colspan="2">Order Information</auxheader>
        </auxhead>
        <columns>
          <column align="center" width="200px" label="Item"/>
          <column align="center" width="200px" label="Value"/>
      </columns>  
        <rows>
          <row>OrderNumber: <label value="@{selected.orderNumber}"/></row>
          <row>OrderName: <label  value="@{selected.orderName}"/></row>
        </rows>
      </grid>
    </window>

    In the example above, the Combobox "model" attribute is data bound to a Java List "orders". When loading, the data binder would convert the Java collection list into ListModelList, a "live collection" ListModel, and "loads" it into "model" attribute of the Combobox. The special "each" attribute that bound to the Comboitem is the entry variable used in constructing the real Comboitem. That is, when the BindingComboitemRenderer construct the real Comboitem, the pulled in data would be associated with this variable name and used in the Comboitem. In the example, the each "order" variable would be used for each entry in the collection variable "orders" and used in constructing its label and value attribute, "order.orderName" and "order.orderNumber". The special "each" attribute is very important here since Data Binder use it to distinguish a "template" Comboitem from "real" Comboitem.

    The "selectedItem" attribute of the Combobox is data bound to a variable "selected". The Combobox "selecteItem" attribute is supposed to accept a Comboitem. However, again, ZK apply a SelectedComboitemConverter that would convert the "selected Comboitem" to the associated entry in the ListModel only. (Because Combobox don't support the concept of setting selected item) In the example, it would be the selected "order". Then, you can advance databound the "selected" variable to show the details of the selected "order" and a List-Detail type of application is easily implemented.

Conclusion

These two features of ListModel and Databinding enhance the capability of Combobox, and it becomes easier to realize the MVC pattern. If you have any question about how to use two features with Combobox, please feel free to leave comment here or post to ZK forum

Comments
 
Marcos de Sousa
2008-01-06

Jumper,


Could you take an look to this feature request: [ 1796281 ] Improve Working with Combobox?!

I submit it a long time: 2007-09-17 17:18. It is strange why it is not ready until today.

Today 2007-01-06 10:24, I will submit more information and maybe problems regarding not have that request ready.

Regards,

Marcos de Sousa

23131321
2008-01-08

Name validation is not working.

robertpic
2008-01-10

Hi Jumper,

first, thanks for improving annotation and MVC. The annonation becomes more complete.

There is one point i miss: format options

i.e:
Annotations in Listbox. I don't want the normal tostring-converts from date and boolean (i.e. true/false in german or better yes/no in german). Of course, i could change the model..but this are classic viewerworks/settings.

I found an CONVERTER Option in the sourcecode from the annotationbinder, but no docs. Maybe this is the solution for my problem.

/Robert

Jumper Chen
2008-01-11

Hi Robert,

The document only in JavaDoc.
If you want to use the converter option, you could follow the following example.
For example,

In Zul file,

<combobox model="@{orders, converter='FullPackageAndName'}" selectedItem="@{selected}">

In the FullPackageAndName.java, you must implement two method.

public class FullPackageAndName implements TypeConverter {

	public Object coerceToBean(Object val, Component comp) {
			return null; // Convert an value object to bean property type.
	}
	public Object coerceToUi(Object val, Component comp) {
	  	return null; // Convert an value object to UI component attribute type.
	}

}

Hope that this can help you.
Jumper

robertpic
2008-01-11

Strike!

Thanks. I was very close to the resolution. I wrote a bad syntax in the annonation...
After this, my Converter start his work.

I am very pleased with the results!

Here is a small demo:
http://online.odoerfer.com/onlineeh/bilder/Screen.jpg

Here a some source:

<grid id="personsGrid" model="@{simpleWindow.allPersons}">
...
<rows id="personsGridRows">
<row self="@{each=person}">
<label value="@{person.firstname}"/>
<label value="@{person.lastname}"/>
<label value="@{person.address}"/>
<label value="@{person.account, converter='com.ebpm.webdemo.zk.ColorNumberConv'}"/>
<label value="@{person.birthdate, converter='com.ebpm.webdemo.zk.DateConv'}"/>
<label value="@{person.color}"/>
<image src="@{person.married, converter='com.ebpm.webdemo.zk.BooleanConv'}"/>
<label value="@{person.rating}"/>
</row>
</rows>
</grid>

The best of all: I can use the converter for listbox also. i.e. red color for negativ account, images for boolean ...

Of course, i have some questions (i.e. how the get the browserlanguage into the Converter).
I ask them next week in the forum.

/Robert

robertpic
2008-01-11

Sorry, link is wrong.

Click on my name or the new Link:

right Link

Erick
2008-01-30

Hi robertpic,

You could write a smalltalk for us, about using the Converters... ;)
Convert a Boolean type to an image is cool....

If possible, post your TypeConverter body!

robertpic
2008-02-08

Sorry, for long response.

1. I have same deadline-troubles, after an influenza.

And better news:
2. I spend my time with converter from part II: the ResultSetconverter = JDBC-Databinding

However, i will release Part I in the next days.

Jdbc-databinding could be smarter, when it intregrated with ZK. Now i need my onw AnnotationDatabinder and some copies from the databinding package.

Erick
2008-04-02

How can I use the Databinding in my Java Class?

I've a custom window and I would like use the Databinding, but I don't know how can I retrieve the selected Item.

<grid id="grid">
<rows>
<row>Code: <intbox id="codigo" value="@{city.id}" disabled="true"/></row>
<row>Name: <textbox id="nome" value="@{city.name}" /></row>
<row>State:
<combobox model="@{states}" selectedItem="@{selected}" value="@{selected.name}">
<comboitem self="@{each=state}" label="@{state.name}" value="@{state.id}"/>
</combobox>
</row>
</rows>
</grid>

City is a java class with the following structure:

class City {
private String id;
private String name;
private State state;
}

I need creating a CRUD window. The user have to type the City name and select a State from a Combobox. So, I need binding the combobox with the state from City. How can I do it withou zscript code (in a Java class)?

toutou
2008-06-10

Hi all,
i have a problem with the conversion of the linkedList to listModel. this the error :

Failed to load /zul/client/adherent.zul

Cause: class java.util.ArrayList ne peut être converti en interface org.zkoss.zul.ListModel.
java.lang.ClassCastException: class java.util.ArrayList ne peut être converti en interface org.zkoss.zul.ListModel.
at org.zkoss.lang.Classes.coerce(Classes.java:1273)
at org.zkoss.lang.reflect.Fields.set(Fields.java:135)
at org.zkoss.zkplus.databind.Binding.myLoadAttribute(Binding.java:266)
at org.zkoss.zkplus.databind.Binding.loadAttribute(Binding.java:234)
at org.zkoss.zkplus.databind.DataBinder.loadAttrs(DataBinder.java:467)
at org.zkoss.zkplus.databind.DataBinder.loadComponent(DataBinder.java:410)
...

please help
thx

Leandro Lancelotti
2008-08-04

If I have a Window with
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
and I open a new Window with Executions.createComponents(), that too have a <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
occurs an error and I lost data of all attributes in main Window.

what's wrong?

Thanks.

Vikas
2008-10-23

Hi,
I am trying to use combobox in .zul file where in I am giving binding to the Java class file like this.
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<window id="filterWindow" width="500px" use="com.putnam.mops.ui.controller.WorkSpaceFilterController">
<hbox>
<combobox id="brokerFilter1" width="200px" model="@{filterWindow.brList}">
<comboitem id="brokerListItem" label="@{filterWindow.brList}" />
</combobox>
<combobox id="traderFilter1" width="200px" model="@{filterWindow.trList}">
<comboitem id="traderListItem" label="@{filterWindow.trList}" />
</combobox>
<combobox id="securityFilter1" width="200px" model="@{filterWindow.securityList}">
<comboitem id="secListItem" label="@{filterWindow.securityList}" />
</combobox>
</hbox>
</window>
I need to iterate over lists like brList, trList and securityList which are attributes of the class WorkSpaceFilterController.
Please Help

henrichen
2008-10-24

<combobox id="brokerFilter1" width="200px" model="@{filterWindow.brList}">
   <comboitem id="brokerListItem" self="@{each=br} label="@{br}" />
</combobox>
   <combobox id="traderFilter1" width="200px" model="@{filterWindow.trList}">
<comboitem id="traderListItem" self="@{each=tr}" label="@{tr}" />
</combobox>
<combobox id="securityFilter1" width="200px" model="@{filterWindow.securityList}">
   <comboitem id="secListItem" self="@{each=sr}" label="@{sr}" />
</combobox>

 
 
Leave a Reply
 
Name (required)
Mail (will not be published) (required)
Website
(Case Insensitive)
Bold textItalic textUnderLine textSource CodeHorizontal rulerExternal Link
Post
Preview