From Documentation

Jump to: navigation, search






Contents

Listbox

Employment/Purpose

Components: listbox, listitem, listcell, listhead and listheader.

A list box is used to display a number of items in a list. The user may select an item from the list.


Example

Single-Column Listboxes

The simplest format is as follows. It is a single-column and single-selection list box.

ZKComRef Listbox SingleColumn.png

 
<zk>
	<listbox width="200px">
	    <listitem label="Butter Pecan"/>
	    <listitem label="Chocolate Chip"/>
	    <listitem label="Raspberry Ripple"/>
	</listbox>
</zk>

Multi-Column Listboxes

The list box also supports multiple columns. When a user selects an item, the entire row is selected.

To define a multi-column list, the number of listcells must match the number of columns with a row. For example if there are 3 columns then each row must contain 3 listcells.


ZKComRef Listbox Example Simple.png

<zk>
	<window title="Search" width="600px" border="normal">
		<listbox id="carListbox" height="160px">
			<listhead>
				<listheader label="Name" />
				<listheader label="Company" />
				<listheader label="Price" width="20%" />
			</listhead>
			<listitem>
				<listcell label="Primera"/>
				<listcell label="Nissan"/>
				<listcell label="$23320"/>
			</listitem>
			<listitem>
				<listcell label="Camry"/>
				<listcell label="Toyota"/>
				<listcell label="$24170"/>
			</listitem>
			<listitem>
				<listcell label="Century"/>
				<listcell label="Toyota"/>
				<listcell label="$28730"/>
			</listitem>
			<listitem>
				<listcell label="Sigma"/>
				<listcell label="Mitsubishi"/>
				<listcell label="$54120"/>
			</listitem>
		</listbox>
	</window>
</zk>

Select Mold

The Listbox has two molds: default and select. If the select mold is used, the HTML's SELECT tag is generated instead.

1000000000000085000000343B08C7D1.png

 
<listbox mold="select">...</listbox>

Note: if the mold is "select", rows is "1", and none of the items are marked as selected, the browser will display the listbox as if the first item is selected. Worst of all, if user selects the first item in this case, no onSelect event is sent. To avoid this confusion, developers should select at least one item when using mold="select" and rows="1".

In addition to each items label, you can assign an application-specific value to each item using the setValue method.

[Since 6.0.0]

ZK 6 provide another component named Selectbox, it is not to create Listitem for each data, so the memory usage is much lower at the server.

Mouseless Entry Listbox

  • Press UP and DOWN to move the selection up and down by one list item.
  • Press PgUp and PgDn to move the selection up and down by one page.
  • Press HOME to move the selection to the first item, and END to move to the last item.
  • Press Ctrl+UP and Ctrl+DOWN to move the focus up and down by one list item without changing the selection.
  • Press SPACE to select the item in focus.

Scrollable Listboxes

Specify a fixed height

A Listbox will be scrollable if you specify the rows attribute or the height attribute and there is not enough space to display all the Listitems.

ZKComRef Listbox Scrollable Height.png

 
<listbox id="carListbox" height="100px">
	<!-- omit child components -->
</listbox>

Rows

The rows attribute is used to control how many rows are visible. By setting it to zero, the Listbox will resize itself to hold as many as items if possible. ZKComRef Listbox Scrollable Rows.png

 
<listbox id="carListbox" rows="3">
	<!-- omit child components -->
</listbox>

Vflex

The vflex property controls whether the listbox will grow or shrink vertically to fit the given space. It is named vertical flexibility. For example, if the list is too big to fit in the browser window, it's height will decrease to make the whole list control visible in the browser window.

This property is ignored if the rows attribute is specified.


Live Data

Like Girds[1], Listboxes support live data. With live data, developers can separate data from the view. In other words, developers need only to provide the data by implementing the ListModel interface, rather than manipulating the Listbox directly.


The benefits are twofold:

  • It is easier to use different views to display the same set of data.
  • The Listbox sends the data to the client only if it is visible. This saves a lot of network traffic if there is a large amount of data.

  1. The concept is similar to Swings (javax.swing.ListModel).

MVC

The template used to control the rendering of each item must be named model and declared right inside the listbox element.

<zk>
	<window title="Search" width="600px" border="normal"
		apply="tutorial.SearchController">
		<listbox id="carListbox" height="160px">
			<listhead>
				<listheader label="Name" />
				<listheader label="Company" />
				<listheader label="Price" width="20%" />
			</listhead>
			<template name="model">
				<listitem>
					<listcell label="${each.name}"></listcell>
					<listcell label="${each.company}"></listcell>
					<listcell>$<label value="${each.price}" /></listcell>
				</listitem>
			</template>
		</listbox>
	</window>
</zk>

The template's name is important because users are allowed to associate multiple templates to one component, and listbox's default renderer looks only for the template called model.

When the template is rendered, a variable called each is assigned with the data being rendered. Thus, you could retrieve the information to render with EL expressions, such as ${each[0]}, if it is an array, or ${each.name}, if it is a bean with a getter called name.

SearchController.java

package tutorial;


import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zul.*;

public class SearchController extends SelectorComposer<Component> {

	private static final long serialVersionUID = 1L;
	
	@Wire
	private Listbox carListbox;
	
	private CarService carService = new CarServiceImpl();
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		List<Car> result = carService.findAll();
		carListbox.setModel(new ListModelList<Car>(result));
	}
}

To assign data to a Listbox, you have to prepare the data in certain data model.

ListitemRenderer

A renderer is responsible for rendering the layout of certain model. Define a class which implements the interface ListitemRenderer, it's method ListitemRenderer.render(Listitem, T, int) is required to be implemented.

CarListItemRenderer.java

package tutorial;

import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;

public class CarListItemRenderer implements ListitemRenderer<Car>{

	public void render(Listitem item, Car car, int index) throws Exception {
		item.appendChild(new Listcell(car.getName()));
		item.appendChild(new Listcell(car.getCompany()));
		item.appendChild(new Listcell("$" + car.getPrice()));
	}

}
<zk>
	<window title="Search" width="600px" border="normal"
		apply="tutorial.SearchController">
		<listbox id="carListbox" height="160px">
			<listhead>
				<listheader label="Name" />
				<listheader label="Company" />
				<listheader label="Price" width="20%" />
			</listhead>
		</listbox>
	</window>
</zk>

SearchController.java

package tutorial;


import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zul.*;

public class SearchController extends SelectorComposer<Component> {

	private static final long serialVersionUID = 1L;
	
	@Wire
	private Listbox carListbox;
	
	private CarService carService = new CarServiceImpl();
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		List<Car> result = carService.findAll();
		carListbox.setModel(new ListModelList<Car>(result));
		carListbox.setItemRenderer(new CarListItemRenderer());
	}
}

The properties of model and itemRenderer could be specified directly in the Listbox.


MVVM

The way to display a collection of data with data binding is very similar to the way in MVC approach. The only difference is we should use data binding expression instead of EL and bind listbox's "model" to vm.carList.

<zk>
	<window title="Search" width="600px" border="normal"
		apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('tutorial.SearchViewModel')">
		<listbox height="160px" model="@bind(vm.carList)">
			<listhead>
				<listheader label="Name" />
				<listheader label="Company" />
				<listheader label="Price" width="20%" />
			</listhead>
			<template name="model">
				<listitem>
					<listcell label="@bind(each.name)"></listcell>
					<listcell label="@bind(each.company)"></listcell>
					<listcell>$<label value="@bind(each.price)" /></listcell>
				</listitem>
			</template>
		</listbox>
	</window>
</zk>

SearchViewModel.java

package tutorial;

import java.util.List;
import org.zkoss.bind.annotation.*;

public class SearchViewModel {
	
	private List<Car> carList;
	
	private CarService carService = new CarServiceImpl();
	
	@Init
    public void init() {
		carList = carService.findAll();
    }

	public List<Car> getCarList(){
		return carList;
	}
}


Selection

When you need to make the Listitem got selected at beginning, you can refer to the following samples:

Single Selection

Set selection to Listitem

If you create Listitems as Listbox's children, you can set selected attribute to true, or use Listitem.setSelected(boolean).

<zk>
	<window title="Search" width="600px" border="normal">
		<listbox id="carListbox" height="160px">
			<listitem selected="true">
				<listcell label="Century"/>
				<listcell label="Toyota"/>
				<listcell label="$28730"/>
			</listitem>
			<!-- omit other components for brevity -->
		</listbox>
	</window>
</zk>

Set selection to model

If you use model, the selected Object should set to model in stead of set the selected attribute to the Listitem.

public class SearchController extends SelectorComposer<Component> {

	//omit other codes for brevity
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		List<Car> result = carService.findAll();
		ListModelList<Car> model = new ListModelList<Car>(result);
		model.addToSelection(model.get(0));
		
		carListbox.setModel(model);
	}
}

Bind selected item to ViewModel

Bind seletcedItem to a ViewModel's property of same object type ( Car ) in a collection. When each time a user select an item of the Listbox, binder will save the selected object to ViewModel. Therefore, we can get user's selection by this way.

<listbox height="160px" model="@bind(vm.carList)" 
	selectedItem="@bind(vm.selectedCar)">
	<!-- omit child components -->
</listbox>
public class SearchViewModel {
	
	private List<Car> carList;
	private Car selectedCar;
	
	private CarService carService = new CarServiceImpl();
	
	@Init
    public void init(){
		carList = carService.findAll();
		selectedCar = carList.get(0);
    }

	public List<Car> getCarList(){
		return carList;
	}
	
	public Car getSelectedCar() {
		return selectedCar;
	}
	
	public void setSelectedCar(Car selectedCar) {
		this.selectedCar = selectedCar;
	}
}

Multiple Selection

Set selection to Listitem

You are able to control whether a Listbox allows multiple selections by setting the multiple attribute to true. The default value is false.

<listbox id="carListbox" height="160px" multiple="true">
	<!-- omit child components -->
	<listitem selected="true">
		<listcell label="Primera"/>
		<listcell label="Nissan"/>
		<listcell label="$23320"/>
	</listitem>
	<listitem selected="true">
		<listcell label="Camry"/>
		<listcell label="Toyota"/>
		<listcell label="$24170"/>
	</listitem>
</listbox>

Set selection to model

[since 6.0.0]

If you use model, the multiple attribute should set to model in stead of Listbox it self:

public class SearchController extends SelectorComposer<Component> {

	//omit other codes for brevity
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		List<Car> result = carService.findAll();
		ListModelList<Car> model = new ListModelList<Car>(result);
		model.setMultiple(true);
		model.addToSelection(model.get(0));
		model.addToSelection(model.get(3));
		
		carListbox.setModel(model);
	}
}

Bind selected items to ViewModel

Bind seletcedItems to a ViewModel's property of same object type ( Car ) of a Set. When each time a user select several items of the Listbox, binder will save the selected object Set to ViewModel.

<listbox height="160px" model="@bind(vm.carList)" 
	multiple="true"	selectedItems="@bind(vm.selectedCars)">
	<!-- omit child components -->
</listbox>
public class SearchViewModel {
	
	private List<Car> carList;
	private Set<Car> selectedCars;
	
	private CarService carService = new CarServiceImpl();
	
	@Init
    public void init(){
		carList = carService.findAll();
		selectedCars = new HashSet<Car>();
		selectedCars.add(carList.get(0));
		selectedCars.add(carList.get(3));
    }

	public List<Car> getCarList(){
		return carList;
	}
	
	public Set<Car> getSelectedCars() {
		return selectedCars;
	}
	
	public void setSelectedCars(Set<Car> selectedCars) {
		this.selectedCars = selectedCars;
	}
}

The Checkmark Property

The checkmark attribute controls whether to display a checkbox or a radio button in front of each list item.

Single selection

<listbox id="carListbox" height="160px"
	checkmark="true">
	<!-- omit child components -->
</listbox>

ZKComRef Listbox Checkmark Single.png

Multiple selection

<listbox id="carListbox" height="160px" 
	checkmark="true" multiple="true">
	<!-- omit child components -->
</listbox>

ZKComRef Listbox Checkmark Multiple.png

Note: If the multiple attribute is false, radio buttons are displayed instead.

Since ZK 5.0.11

Note: Select all checkbox in listheader is only available if ROD is false. ZKComRef Listbox Checkmark Multiple offROD.png

Deselect Others when Clicking an Item with Checkmark

[Since 5.0.5]

If a listbox's checkmark (Listbox.isCheckmark()) is enabled, the selection will be toggled when an user clicks an item. In other words, all other items will remain the same.

If you prefer to deselect all other items and select the item being clicked (which the behavior of ZK 5.0.4 and earlier), you could specify true to this library property called org.zkoss.zul.listbox.checkmarkDeselectOthers in WEB-INF/zk.xml:

<library-property>
	<name>org.zkoss.zul.listbox.checkmarkDeselectOthers</name>
	<value>true</value>
</library-property>

Toggle Selection when Right Clicking an Item with Checkmark

[Since 5.0.5]

If a listbox's checkmark (Listbox.isCheckmark()) is enabled, the selection will be toggled when user right click on item.

If you prefer not to select/deselect item on right click, you could specify false to this library property called org.zkoss.zul.listbox.rightSelect in WEB-INF/zk.xml:

<library-property>
	<name>org.zkoss.zul.listbox.rightSelect</name>
	<value>false</value>
</library-property>

Nonselectable Tags

[Since 5.0.5]

By default, when an user clicks on a BUTTON, INPUT, TEXTAREA or A tag, the selection state of the item won't be changed. For example, when an user clicks the textbox in the following example, the selection state of the item won't be changed (only the textbox gains the focus).

<listitem>
	<listcell>
		<textbox/>
	</listcell>
</listitem>

Sometimes it is not intuitive, such as using with inplace editing (InputElement.isInplace()). If you want to have more control of whether to select an item, you could specify a list of tags in the nonselectableTags property (Listbox.setNonselectableTags(String)). For example, if you want to select the item, no matter what tag the user clicks, you could specify an empty string as follows.

<listbox nonselectableTags="">
    <listitem><listcell><textbox/></listcell></listitem>
    <listitem><listcell><button label="button"/></listcell></listitem>
    <listitem><listcell><h:input xmlns:h="native"/></listcell></listitem>
    <listitem><listcell><datebox/></listcell></listitem>
</listbox>

If you only want to ignore BUTTON and INPUT only, you could specify nonselectableTags="button, input".

If you want to toggle the selection only when the user clicks on the checkmark, you could specify * ([since 5.0.6]). Notice that you have to specify checkmark="true" as well (otherwise, no item is selectable).

<listbox checkmark="true" nonselectableTags="*">

Sort

Auto-sorting

Sort by label attribute of Listcell (without model)

Listbox support sorting of Listitems directly. There are a few ways to enable the sorting of a particular column. The simplest way is to set the sort attribute of the Listheader to auto as demonstrated by the code below. Then, the column that the Listheader is associated with is sort-able based on the label of each Listcell of the that column.

ZKComRef Listbox Sorting SortByLabel.png

 
<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name" sort="auto" />
		<listheader label="Company" sort="auto" />
		<listheader label="Price" width="20%" sort="auto" />
	</listhead>
	<listitem>
		<listcell label="Primera"/>
		<listcell label="Nissan"/>
		<listcell label="$23320"/>
	</listitem>
	<!-- omit child components -->
</listbox>

Client Sort (without model)

If the Listbox is not fill the data with a model, you can use client sort feature by specify client to the sort attribute,

 
<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name" sort="client" />
		<listheader label="Company" sort="client" />
		<listheader label="Price" width="20%" sort="client" />
	</listhead>
	<listitem>
		<listcell label="Primera"/>
		<listcell label="Nissan"/>
		<listcell label="$23320"/>
	</listitem>
	<!-- omit child components -->
</listbox>
  • "client" : it is treated by a string.
  • "client(number)" : it is treated by a number.

Sort by data field (use model)

You can specify a filed name of data to the sort attribute, then the column will sort the field of data.

 
<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name" sort="auto(name)" />
		<listheader label="Company" sort="auto(company)" />
		<listheader label="Price" width="20%" sort="auto(price)" />
	</listhead>
	<!-- omit child components -->
</listbox>

Car.java

 
public class Car {
    private Integer id;
    private String name;
    private String company;
    private String preview;
    private String description;
    private Integer price;
    //omit getter and setter for brevity
}

If you need to sort name and then id, you can specify auto(name,id) to the sort attribute.

 
<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name" sort="auto(name,id)" />
		<listheader label="Company" sort="auto(company)" />
		<listheader label="Price" width="20%" sort="auto(price)" />
	</listhead>
	<!-- omit child components -->
</listbox>

Sort by array data (use model)

[since 5.0.6]

If the data is an array, you can specify index to the sort attribute, then the column will sort the data in the specified index of array.

 
<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name" sort="auto(0)" />
		<listheader label="Company" sort="auto(1)" />
		<listheader label="Price" width="20%" sort="auto(2)" />
	</listhead>
	<!-- omit child components -->
</listbox>


The SortAscending and SortDescending Properties

If you prefer to sort Listitems in different ways, you can assign a java.util.Comparator instance to the sortAscending and/or sortDescending attributes. Once assigned, the Listitems can be sorted in the ascending and/or descending order with the specified comparator.

The invocation of the sort attribute with auto automatically assigns two comparators to the sortAscending and sortDescending attributes. You can override any of them by assigning another comparator.

For example, assume you want to sort based on the value of Listitems, rather than Listcell's label, then you assign an instance of ListitemComparator to these attributes as follows.

<zscript>
	Comparator asc = new ListitemComarator(-1, true, true);
	Comparator dsc = new ListitemComarator(-1, false, true);
</zscript>
<listbox>
    <listhead>
        <listheader sortAscending="${asc}" sortDescending="${dsc}"/>
 ...

The SortDirection Property

The sortDirection attribute controls whether to display an icon to indicate the order of a particular column. If list items are sorted before adding to the Listbox, you should set this attribute explicitly.

<listheader sortDirection="ascending"/>

Sorting is maintained automatically by the Listboxes as long as you assign the comparator to the corresponding list header.

The onSort Event

When you assign at least one comparator to a list header, an onSort event is sent to the server if user clicks on it. The Listheader implements a listener to automatically the sorting.

If you prefer to handle this manually, you can add your own listener to the Listheader for the onSort event. To prevent the default listener invoking the sort method, you have to call the stopPropagation method. Alternatively, you can override the sort method.

The Sort Method

The sort method is the underlying implementation of the default onSort event listener. It is also useful if you want to sort the Listitems using Java code. For example, you may have to call this method after adding items (assuming that they are not added in the proper order).

new Listem("New Stuff").setParent(listbox);
if (!"natural".header.getSortDirection())
	header.sort("ascending".equals(header.getSortDirection()));

The default sorting algorithm is quick-sort. You can override it with your own implementation or listen to the onSort event as described in the previous section.

Tip: Sorting a large amount of live data could degrade the performance significantly. It is better to intercept the onSort event or the sort method to handle it effectively.

Sorting with Live Data

If you allow users to sort a grid with live data, you have to implement the interface, ListModelExt, in addition to the ListModel.

class MyListModel implements ListModel, ListModelExt {
	public void sort(Comparator cmpr, boolean ascending) {
		//do the real sorting
		//notify the listbox (or grid) that data is changed by use of ListDataEvent
	}
}

When a user wants to sort the Listbox, the Listbox will invoke the ListModelExt.sort(Comparator, boolean) method to sort the data. In other words, the sorting is done by the ListModel, rather than the Listbox.

After sorting, the ListModel will notify the Listbox by invoking the ListDataListener.onChange(ListDataEvent)method of the Listbox's registered ListDataListener instances. These are registered by the ListModel.addListDataListener(ListDataListener) method. In most cases, all the data is changed, so the ListModel usually sends the following event:

new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1)

Note: the implementation of the ListModel and ListModelExt is independent of the visual presentation. In other words, they can be used with Girds, Listboxes and any other components supporting ListModel.

If you require maximum flexibility, you should not assume the component to used, instead use ListDataEvent to communicate.


Sort when data update

By default, when you add a new data to a sorted Listbox, the data will place in the latest row, if you want to sort the data when data list update, you can specify org.zkoss.zul.listbox.autoSort attribute.

 
<listbox id="carListbox" height="160px">
	<custom-attributes  org.zkoss.zul.listbox.autoSort="true"/>
	<!-- omit child components -->
</listbox>

Group

*Available in ZK PE and EE only

For more information, please take a look at these smalltalks,

Or refer to Listgroup component directly.

Listgroup Component

Both Grid, and Listbox support Grouping concept, it enables developers to display data in an advanced way. ZKComRef Listbox Group Listgroup.png

 
<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name"/>
		<listheader label="Company"/>
		<listheader label="Price" width="20%"/>
	</listhead>
	<listgroup label="Nissan"/>
	<listitem>
		<listcell label="Primera"/>
		<listcell label="Nissan"/>
		<listcell label="$23320"/>
	</listitem>
	<listgroup label="Toyota"/>
	<listitem>
		<listcell label="Camry"/>
		<listcell label="Toyota"/>
		<listcell label="$24170"/>
	</listitem>
	<listitem>
		<listcell label="Century"/>
		<listcell label="Toyota"/>
		<listcell label="$28730"/>
	</listitem>
	<listgroup label="Mitsubishi"/>
	<listitem>
		<listcell label="Sigma"/>
		<listcell label="Mitsubishi"/>
		<listcell label="$54120"/>
	</listitem>
</listbox>

GroupModel

A groups model is used to drive components that support groups of data. The groups of data is a two-level tree of data: a list of grouped data and each grouped data is a list of elements to display. Here is a live demo. Currently, both Listbox and Grid support a list of grouped data.

Instead of implementing GroupsModel, it is suggested to extend from AbstractGroupsModel, or to use one of the default implementations as following:

SimpleGroupsModel GroupsModelArray
Usage The grouping is immutable, i.e., re-grouping is not allowed Grouping is based on a comparator (java.util.Comparator)
Constructor The data must be grouped, i.e., data[0] is the first group, data[1] the second, etc. The data is not grouped, i.e., data[0] is the first element. The constructor requires a comparator that will be used to group them.
Version

Since 3.5.0

Since 5.0.5; For 5.0.4 or prior, please use ArrayGroupsModel (the same).
 
public class CarListController extends SelectorComposer<Component> {

	@Wire
	private Listbox carListbox;
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		List<Car> result = carService.findAll();		
		Car[] careArray = result.toArray(new Car[result.size()]);		
		GroupsModelArray<Car, Object, Object, Object> groupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, new FieldComparator("company", true));
		
		carListbox.setModel(groupModel);
	}

	//omit other codes for brevity

}

MVC

When used with GroupsModel, Listboxes will use the template called model:group for rendering the grouping object. If it is not defined, it will look for the template called model instead (i.e., the same template is used for rendering the grouping and non-grouping objects).

<listbox id="carListbox" height="160px">
	<listhead>
		<listheader label="Name" />
		<listheader label="Company" />
		<listheader label="Price" width="20%" />
	</listhead>
	<template name="model:group">
		<listgroup label="${groupingInfo.groupIndex}-${each.company}"/>
	</template>
	<template name="model">
		<listitem>
			<listcell label="${each.name}"></listcell>
			<listcell label="${each.company}"></listcell>
			<listcell>$<label value="${each.price}" /></listcell>
		</listitem>
	</template>
	<template name="model:groupfoot">
		<groupfoot>....</groupfoot>
	</template>
</listbox>
  • Note the groupingInfo is used to get the information of the grouping data. Please refer to GroupingInfo

ListitemRenderer

 
public class CarGroupListItemRenderer implements ListitemRenderer<Car>{

	public void render(Listitem item, Car car, int index) throws Exception {
		if (item instanceof Listgroup) {
			item.setLabel(car.getCompany());
		} else {
			item.appendChild(new Listcell(car.getName()));
			item.appendChild(new Listcell(car.getCompany()));
			item.appendChild(new Listcell("$" + car.getPrice()));
		}
	}
}

Then you need to specify the CarGroupListItemRenderer to the listbox.

 

	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		//omit other codes for brevity
		
		carListbox.setModel(groupModel);
		carListbox.setItemRenderer(new CarGroupListItemRenderer());
	}

MVVM

<zk>
	<window title="Search" width="600px" border="normal"
		apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('tutorial.SearchViewModel')">
		<listbox height="160px" model="@bind(vm.carGroupModel)">
			<listhead>
				<listheader label="Name" />
				<listheader label="Company" />
				<listheader label="Price" width="20%" />
			</listhead>
			<template name="model:group">
				<listgroup label="@bind(each.company)" />
			</template>
			<template name="model">
				<listitem>
					<listcell label="@bind(each.name)"></listcell>
					<listcell label="@bind(each.company)"></listcell>
					<listcell>$<label value="@bind(each.price)" /></listcell>
				</listitem>
			</template>
		</listbox>
	</window>
</zk>

SearchViewModel.java

package tutorial;

import java.util.List;

import org.zkoss.bind.annotation.Init;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.GroupsModelArray;

public class SearchViewModel {
	
	private GroupsModelArray<Car, Object, Object, Object> carGroupModel;
	private CarService carService = new CarServiceImpl();
	
	@Init
    public void init() {
		List<Car> result = carService.findAll();		
		Car[] careArray = result.toArray(new Car[result.size()]);		
		carGroupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, new FieldComparator("company", true));
    }
	
	public GroupsModelArray<Car, Object, Object, Object> getCarGroupModel() {
		return carGroupModel;
	}
}

Openable

By default, all Listgroup nodes are opened. To control whether to open a Listgroup node, the application shall access the model's GroupsModelArray.addOpenGroup(int) and GroupsModelArray.removeOpenGroup(int) API, rather than accessing Listgroup directly.

	@Init
    public void init() {
		List<Car> result = carService.findAll();		
		Car[] careArray = result.toArray(new Car[result.size()]);		
		carGroupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, new FieldComparator("company", true));
		groupModel.removeOpenGroup(0);
    }

Selection

Set selection to model

If you use model, the selected Object should set to model in stead of set the selected attribute to the Listitem.

public class SearchController extends SelectorComposer<Component> {

	//omit other codes for brevity
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		List<Car> result = carService.findAll();
		Car[] careArray = result.toArray(new Car[result.size()]);
		
		GroupsModelArray<Car, Object, Object, Object> groupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, new FieldComparator("company", true));
		groupModel.addToSelection(result.get(0));
		
		carListbox.setModel(model);
	}
}

Bind selected item to ViewModel

<listbox height="160px" model="@bind(vm.carList)" 
	selectedItem="@bind(vm.selectedCar)">
	<!-- omit child components -->
</listbox>

SearchViewModel

package tutorial;

import java.util.List;

import org.zkoss.bind.annotation.Init;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.GroupsModelArray;

public class SearchViewModel {
	
	private GroupsModelArray<Car, Object, Object, Object> carGroupModel;
	private Car selectedCar;
	
	private CarService carService = new CarServiceImpl();
	
	@Init
    public void init() {
		List<Car> result = carService.findAll();		
		Car[] careArray = result.toArray(new Car[result.size()]);		
		carGroupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, new FieldComparator("company", true));
		
		 selectedCar = result.get(0);
    }
	
	public GroupsModelArray<Car, Object, Object, Object> getCarGroupModel() {
		return carGroupModel;
	}
	
	public Car getSelectedCar() {
        return selectedCar;
    }
     
    public void setSelectedCar(Car selectedCar) {
        this.selectedCar = selectedCar;
    }
}

Columns Menu

[Since 6.5.0]

Use Component

ZKComRef Listbox Columns Menu.PNG

<zk>
	<listbox>
		<listhead menupopup="auto">
			<listheader label="Author" sort="auto"/>
			<listheader label="Title" sort="auto"/>
			<listheader label="Publisher" sort="auto"/>
			<listheader label="Hardcover" sort="auto"/>
		</listhead>
		// omitted...
	</listbox>
</zk>

Use Model

You need to set sort attribute to auto, then Listheader components will be added a FieldComparator or ArrayComparator to itself, if the argument is number (e.g. auto(0)), it will be ArrayComparator otherwise it will be a FieldComparator, the Comparator is use to sort the column or group the data of the model.

<zk>
	<window title="Search" width="600px" border="normal"
		apply="tutorial.SearchController">
		<listbox id="carListbox" height="160px">
			<listhead menupopup="auto">
				<listheader label="Name" sort="auto(name)" />
		        <listheader label="Company" sort="auto(company)" />
		        <listheader label="Price" width="20%" sort="auto(price)" />
			</listhead>
			<template name="model:group">
				<listgroup label="${each.name}" if="${groupColIndex == 0}"/>
				<listgroup label="${each.company}" if="${groupColIndex == 1}"/>
				<listgroup label="${each.price}" if="${groupColIndex == 2}"/>
			</template>
			<template name="model">
				<listitem>
					<listcell label="${each.name}"></listcell>
					<listcell label="${each.company}"></listcell>
					<listcell>$<label value="${each.price}" /></listcell>
				</listitem>
			</template>
		</listbox>
	</window>
</zk>

You need to switch the model from the ListModel to GroupModel when onGroup Event be triggered.

 
package tutorial;


import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.SortEvent;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.GroupsModelArray;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listheader;

public class SearchController extends SelectorComposer<Component> {

	private static final long serialVersionUID = 1L;
	
	@Wire
	private Listbox carListbox;
	
	private CarService carService = new CarServiceImpl();
	private List<Car> result;
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		result = carService.findAll();
		ListModelList<Car> model = new ListModelList<Car>(result);
		carListbox.setModel(model);
	}
	@Listen("onGroup = #carListbox listheader")
	public void group(SortEvent event) {
		Listheader header = (Listheader) event.getTarget();
		
		FieldComparator fieldComparator = (FieldComparator)
			 (event.isAscending() ? header.getSortAscending(): 
				 header.getSortDescending());
		
		Car[] careArray = result.toArray(new Car[result.size()]);	
		GroupsModelArray<Car, Object, Object, Object> groupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, fieldComparator);

		carListbox.setAttribute("groupColIndex", 
				header.getParent().getChildren().indexOf(header));
		carListbox.setModel(groupModel);
	}
}


Use GroupModel

 
public class CarListController extends SelectorComposer<Component> {

	@Wire
	private Listbox carListbox;
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		List<Car> result = carService.findAll();		
		Car[] careArray = result.toArray(new Car[result.size()]);		
		GroupsModelArray<Car, Object[], Object, Object> groupModel = 
			new GroupsModelArray<Car, Object[], Object, Object>(
					careArray, new FieldComparator("company", true));

		carListbox.setAttribute("groupColIndex", 1);
		carListbox.setModel(groupModel);
	}

	@Listen("onGroup = #carListbox listheader")
	public void group(SortEvent event) {
		Listheader header = (Listheader) event.getTarget();
		carListbox.setAttribute("groupColIndex", 
				header.getParent().getChildren().indexOf(header));
	}

}

Use ListitemRenderer

public class SearchController extends SelectorComposer<Component> {
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		result = carService.findAll();
		ListModelList<Car> model = new ListModelList<Car>(result);

		carListbox.setModel(model);
		carListbox.setItemRenderer(new CarGroupListItemRenderer());
	}

	//omit other codes for brevity

}

In the CarGroupListItemRenderer, you can retrieve group column index when render Listgroup component, in this case, the data are Java bean, you need to use Fields.get(Object, String) API to retrieve the field value of the Java bean as the group header label.

package tutorial;

import org.zkoss.lang.reflect.Fields;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listgroup;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;

public class CarGroupListItemRenderer implements ListitemRenderer<Car>{

	public void render(Listitem item, Car car, int index) throws Exception {
		if (item instanceof Listgroup) {
			Listbox listbox = item.getListbox();
			int groupColIndex = (Integer) 
				listbox.getAttribute("groupColIndex");
			Listheader header = (Listheader) 
				listbox.getListhead().getChildren().get(groupColIndex);
			String field = ((FieldComparator)header.getSortAscending()).getRawOrderBy();
			item.setLabel(Fields.get(car, field).toString());
		} else {
			item.appendChild(new Listcell(car.getName()));
			item.appendChild(new Listcell(car.getCompany()));
			item.appendChild(new Listcell("$" + car.getPrice()));
		}
	}
}

We use FieldComparator.getRawOrderBy() API to retrieve the filed of Java bean, the RawOrderBy attribute only available when you set sort attribute to each Listheader component.

<listbox id="carListbox" height="160px">
	<listhead menupopup="auto">
		<listheader label="Name" sort="auto(name)" />
		<listheader label="Company" sort="auto(company)" />
		<listheader label="Price" width="20%" sort="auto(price)" />
	</listhead>
</listbox>

Ungroup Column Menu

When the user groups the content of the listbox, the column's menu will show an ungroup icon for user to reset the group.

[ZK EE]
[Since 6.5.0]

ZKComRef Listbox Columns Menu Ungroup.PNG

Note: If the Listbox contains with Model, GroupsModel, you have to register an onUngroup event for listheader to show an ungroup icon and then replace the current model with a ListModel to reset the group.

For example,

<zk>
	<window title="Search" width="600px" border="normal"
		apply="tutorial.SearchController">
		<listbox id="carListbox" height="160px">
			<listhead menupopup="auto">
				<listheader label="Name" sort="auto(name)" />
		        <listheader label="Company" sort="auto(company)" />
		        <listheader label="Price" width="20%" sort="auto(price)" />
			</listhead>
			<template name="model:group">
				<listgroup label="${each.name}" if="${groupColIndex == 0}"/>
				<listgroup label="${each.company}" if="${groupColIndex == 1}"/>
				<listgroup label="${each.price}" if="${groupColIndex == 2}"/>
			</template>
			<template name="model">
				<listitem>
					<listcell label="${each.name}"></listcell>
					<listcell label="${each.company}"></listcell>
					<listcell>$<label value="${each.price}" /></listcell>
				</listitem>
			</template>
		</listbox>
	</window>
</zk>
 
package tutorial;


import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.SortEvent;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.GroupsModelArray;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listheader;

public class SearchController extends SelectorComposer<Component> {

	private static final long serialVersionUID = 1L;
	
	@Wire
	private Listbox carListbox;
	
	private CarService carService = new CarServiceImpl();
	private List<Car> result;
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		result = carService.findAll();
		ListModelList<Car> model = new ListModelList<Car>(result);
		carListbox.setModel(model);
	}
	@Listen("onGroup = #carListbox listheader")
	public void group(SortEvent event) {
		Listheader header = (Listheader) event.getTarget();
		
		FieldComparator fieldComparator = (FieldComparator)
			 (event.isAscending() ? header.getSortAscending(): 
				 header.getSortDescending());
		
		Car[] careArray = result.toArray(new Car[result.size()]);	
		GroupsModelArray<Car, Object, Object, Object> groupModel = 
			new GroupsModelArray<Car, Object, Object, Object>(
					careArray, fieldComparator);

		carListbox.setAttribute("groupColIndex", 
				header.getParent().getChildren().indexOf(header));
		carListbox.setModel(groupModel);
	}

	@Listen("onUngroup = #carListbox listheader")
	public void ungroup(SortEvent event) {
		carListbox.setModel(new ListModelList<Car>(result));
	}
}

Paging

Like grids, you can use multiple pages to represent large content by setting the mold to paging. Similarly, you can control how many items each page displays, whether to use an external paging component and whether to customize the behavior when a page is selected.

The listbox and grid components support the paging intrinsically, so you don't need to specify a paging component explicitly as above unless you want to have different visual layout or to control multiple listbox and gridcontrols with one paging component.

Please refer to the Grid for more details.

Autopaging

When using the paging mold and vflex, you could also turn on autopaging (Listbox.setAutopaging(boolean)) such that the page size will be adjusted automatically based on the available space.

[Since 5.0.2]

Note: If the autopaging is enabled, the height of each row will be applied the following CSS by default. If you want to change the height, please overwrite the CSS rule as your preference.

.z-listbox-autopaging .z-listcell-cnt {
	height: 30px;
	overflow: hidden;
}
[Since 5.0.8]


Frozen Component

In ZK 5 you are now able to freeze columns within a Grid and Listbox. This mirrors functionality seen within Excel and makes data in these components easier to read, interpret and handle.

The following code demonstrates how to freeze a column within a Grid:

    <listbox>
        <listhead>
            <listheader label="header 1"/>
            <listheader label="header 2"/>
            <listheader label="header 3"/>
            <listheader label="header 4"/>
        </listhead>
        <frozen columns="2"/>
        <listitem>
            <listcell label="cell 1"/>
            <listcell label="cell 2"/>
            <listcell label="cell 3"/>
            <listcell label="cell 4"/>
        </listitem>
    </listbox>
[Since 5.0.0]

Properties

Column Headers

You can specify column headers by using listhead and listheader, please see the code below[1]. In addition to label, you can specify an image as the header by use of the image attribute.

ZKComRef Listbox ColumnHeaders.png

 
<zk>
	<listbox width="200px">
		<listhead>
	                <listheader label="Name"/>
	                <listheader label="Occupation"/>
    	        </listhead>
       
                ...

	</listbox>
</zk>



  1. This feature is a bit different from XUL, where listhead and listheader are used.

Column Footers

You could specify the column footers by using listfoot and listfooter. Please note, each time a listhead instance is added to a list box, it must be the first child, and a listfoot instance the last child.

ZKComRef Listbox ColumnFooters.png

 
<zk>
	<listbox width="200px">
	    <listhead>
	        <listheader label="Population"/>
	        <listheader align="right" label="%"/>
	    </listhead>
	    <listitem id="a" value="A">
	        <listcell label="A. Graduate"/>
	        <listcell label="20%"/>
	    </listitem>
	    <listitem id="b" value="B">
	        <listcell label="B. College"/>
	        <listcell label="23%"/>
	    </listitem>
	    <listitem id="c" value="C">
	        <listcell label="C. High School"/>
	        <listcell label="40%"/>
	    </listitem>
	    <listitem id="d" value="D">
	        <listcell label="D. Others"/>
	        <listcell label="17%"/>
	    </listitem>
	    <listfoot>
	        <listfooter label="More or less"/>
	        <listfooter label="100%"/>
	    </listfoot>
	</listbox>
</zk>

Auxiliary Headers

Like grids, you can specify auxiliary headers with the auxhead and auxheader components.

Please refer to the Grid for more details.

SizedByContent

By default, the widths of columns have to be specified explicitly, or it will be split equally among columns regardless what content they might have. If you want to have the minimal width (that fit the content), you could specify hflex="min" at the column (not the listbox).

However, the listbox has a special mode called sized-by-content (Listbox.setSizedByContent(boolean)). By specifying it to true, the column width will be adjusted automatically. However, it is controlled by the browser, so you will have no 100% control of it. For example, if an user resized a column, the final width might not be exactly the same as what he resized.

In general, we suggest to specify hflex in column, rather than specifying sizedByContent at listbox for much more predictable result.

Span

[Since 5.0.5]

By default, when sizedByContent is true, column only takes up required space.

ZKComRef Listbox Nospan.png

If you want to span the width of the columns to occupy the whole listbox, you could specify true to this attribute.

ZKComRef Listbox Span.png

<listbox sizedByContent="true" span="true" width="800px">
	<listhead>
		<listheader	label="Time Message" />
		<listheader label="Level" />
		<listheader label="Source" />
		<listheader label="Message" />
	</listhead>
	<listitem>
		<listcell label="6/28/10 4:19:18 PM" />
		<listcell label="Info, long content.........................." />
		<listcell label="Server" />
		<listcell label="Merging recovery point 52 created 20 6/27/10 10 :11 PM" />
	</listitem>
</listbox>

If you want to leave the rest of the space to the specified column, you can specify the index of the column to this attribute.

ZKComRef Listbox Span number.png

<listbox sizedByContent="true" span="3" width="800px">
	<listhead>
		<listheader	label="Time Message" />
		<listheader label="Level" />
		<listheader label="Source" />
		<listheader label="Message" />
	</listhead>
	<listitem>
		<listcell label="6/28/10 4:19:18 PM" />
		<listcell label="Info, long content.........................." />
		<listcell label="Server" />
		<listcell label="Merging recovery point 52 created 20 6/27/10 10 :11 PM" />
	</listitem>
</listbox>

Show messages when empty

[Since 5.0.7]

The emptyMessage attribute is used to show a message when we have no items.

		<listbox id="test1" emptyMessage="No items match your search">
		
			<listhead sizable="true">
				<listheader label="Type" width="520px" />
				<listheader label="Content" hflex="min" />
				<listheader label="Content" hflex="1" />
			</listhead>
		</listbox>

Maxlength

The maxlength property defines the maximum number of characters visible at the browser. By setting this attribute, you are able to create a narrower list box.

Sizable

Like columns, you can set the sizable attribute of the listhead to true to allow users to resize the width of list headers. The onColSize event is also sent when a user resizes listbox.

Auto Fitting Columns

[Since 5.0.0]

When you want to resize a column of a Grid or Listbox, all you now need to do is double click the column when the mouse is over where the columns meet and the column will automatically resize to fit its contents. To enable this functionality Listbox's Listhead need the attribute sizable="true". In other words, all sizable column provides the auto-fitting functionality.


Custom Attributes

org.zkoss.zul.listbox.rightSelect

[default: true]
[inherit: true][1]

It specifies that the selection will be toggled when user right clicks on an item, if the checkmark is enabled (Listbox.isCheckmark()). If it is turned off, right clicking on an item won't change its selection state.

org.zkoss.zul.listbox.groupSelect

[default: false]
[inherit: true][2]
[since 5.0.7]

It specifies whether Listgroups are selectable under this Listbox. (Similar to above, it can also be specified as a library property, which will be in effect for the whole application.)

org.zkoss.zul.listbox.autoSort

[default: false]
[inherit: true][3]
[since 5.0.7]

Specifies whether to sort the model when the following cases:

If you want to ignore sort when receiving ListDataEvent, you can specifies the value as ignore.change.

org.zkoss.zul.listbox.rod

[default: false]
[inherit: true][4]

It specifies whether to enable ROD (render-on-demand). For more information, please refer to ZK Developer's Reference: Performance Tips.

org.zkoss.zul.listbox.preloadSize

[default: 50]
[inherit: true][5]
[since 6.0.1]

It specifies the number of items to preload when receiving the rendering request from the client. It is used only if live data (Listbox.setModel(ListModel)) and not paging (Listbox.getPagingChild()).

org.zkoss.zul.listbox.initRodSize

[default: 50]
[inherit: true][6]
[since 6.0.1]

Specifies the number of items rendered when the Listbox first render. It is used only if live data (Listbox.setModel(ListModel)) and not paging (Listbox.getPagingChild()).


  1. The custom attribute could be specified in this component, or any of its ancestor. In addition, it could be specified as a library property to enable or disable it for the whole application.
  2. Same as above.
  3. Same as above.
  4. Same as above.
  5. Same as above.
  6. Same as above.

Supported Events

Name
Event Type
onSelect
Event: SelectEvent

Notifies one that the user has selected a new item in the listbox.

onFocus
Event: Event

Denotes when a component gets the focus. Remember event listeners execute at the server, so the focus at the client might be changed when the event listener for onFocus got executed.

onBlur
Event: Event

Denotes when a component loses the focus. Remember event listeners execute at the server, so the focus at the client might be changed when the event listener for onBlur got executed.

onAfterRender
Event: Event

Notifies one that the model's data has been rendered.

onPageSize
Event: PageSizeEvent

Notifies the paging size has been changed when the autopaging (Listbox.setAutopaging(boolean)) is enabled and user changed the size of the content.

Supported Molds

Available molds of a component are defined in lang.xml embedded in zul.jar.

Name
Snapshot
default
Listbox mold default.png
select
Listbox mold select.png
paging
Listbox mold paging.png

Supported Children

 Listitem,  Listhead,  Listfoot,  Listgroup,  Listgroupfoot

Use Cases

Version Description Example Location
     

Version History

Last Update : 2012/10/26


Version Date Content
5.0.2 May 2010 Support the autopaging
5.0.4 July 2010 Support onAfterRender event
5.0.5 September 2010 The nonselectabletag property was introduced to enhance the control of when to select an item
5.0.5 September 2010 When a listbox's checkmark is enabled and an item is clicked, it will toggle the selection of the item and the other remains the same.
5.0.5 October 2010 When a listbox's checkmark is enabled and an item is right clicked, it will toggle the selection of the item.
5.0.5 October 2010 The span property was introduced to span the columns to occupy the whole listbox.
5.0.6 February 2011 The nonselectableTags property supported "*".
5.0.7 April 2011 Listbox shall sort model based on current state.
5.0.7 April 2011 The emptyMessage attribute supported
5.0.7 April 2011 The onPageSize event was introduced.
5.0.8 June 2011 Deprecated setPreloadSize, instead with a custom attributes "org.zkoss.zul.listbox.preloadSize".
5.0.8 June 2011 Add a custom attributes "org.zkoss.zul.listbox.initRodSize" for control ROD render size.
5.0.11 February 2012 ZK-873: Select all checkbox in listheader is only available if ROD is false.
6.5.0 June 2012 ZK-120: Provide menupopup="auto" for listbox
6.5.0 June 2012 ZK-147: Support ungroup for grid's column menu




Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.