Description & Source Code
  • Description
  • XML & Java Source

Each of the above is a manifestation of a composite component called "imageLabel". A composite component is a collection of ZK components working as whole to bring forth some functionality.

In this demo, the "imageLabel" component shows an image and a label together with a header. When users click on the header, an in-place editor pops up and whatever changes made is updated on the "imageLabel" once user clicks "Submit".

The demo itself is rendered from only a few component declarations in the composite_demo.zul file. A composite component extends an existing ZK component, please see In the ImageLabel constructor, the UI components associated with this composite is created based on the template "ImageLabel.zul". An onClick event listener is registered so that when users click on the header, an in-place editor is created based on the template "InplaceEditor.zul".

This serves as an example for how the behavior of a composite component can be defined within itself, rather then in an outside controller class (eg. a controller class that extends GenericForwardComposer), giving developers another approach in separating the presentation layer in ZK.

<!-- Composite component  -->
<?component name="imageLabel" class="demo.composite.ImageLabel" ?>
<!-- This component is editable contains a icon of item -->
	<style src="/widgets/composite/composite_component/Customize.css" />
	<!-- Prepare Data -->
	   import demo.composite.*;               	
		String dir = "/widgets/composite/composite_component/img/";
		ItemBean[] beans = new ItemBean[]{
			new ItemBean("Label", dir + "label.png", "A label component represents a piece of text."),
   	    	new ItemBean("Intbox", dir + "intbox.png", "A intbox is used to let users input integer data."),   	    	   	    	
   	    	new ItemBean("Listbox", dir + "listbox.png", "A list box is used to display a number of items in a list. The user may select an item from the list.")
	<!-- Create composite component with attributes -->
	<zk forEach="${beans}">
		<imageLabel sclass="image-Label" title="${}" description="${each.description}" imagePath="${each.picturePath}">
			<attribute name="onLabelEdit"><![CDATA[
				if (info.getChildren().size() == 2) {
				new Html("<b>Edit Log</b> (" + new Date() +")<ul><li>Title : " + event.getCurrentTitle() + "</li><li> Desc : " + event.getCurrentDesc() + "</li></ul>").setParent(info);
	<vlayout id="info" sclass="log-info"/>
.image-Label {
	margin: 10px 5px 0;
	padding: 5px 10px;

.image-Label .title .z-caption-l,.image-Label .title .z-caption-cnt {
	font-size: 1.2em;
	font-weight: bold;
	cursor: pointer;

.image-Label .title .z-caption-l:hover,.image-Label .title .z-caption-cnt:hover {
	color: orange;

.image-Label .desc-div {
	padding: 10px 20px;

.image-Label .desc {
	color: #636363;
	font-size: 10px;
	display: block;
	word-wrap: break-word;
	white-space: normal;
.log-info div:nth-child(even) {
	background: #FFFEE6;
.log-info div:nth-child(odd) {
	background: #FFFFFF;

.log-info span,
.log-info li {
	font-family: monospace;
	font-size: 0.95em;
	color: #565656;
.log-info ul {
	margin: 5px 10px;
<groupbox id="item" width="100%" closable="false" sclass="item" >
	<caption id="titleCaption" sclass="title"/>
		<image id="labelImage" sclass="image" />
		<div sclass="desc-div">
			<label id="descLabel" sclass="desc" />
	<hlayout width="90%">
		<textbox id="editTitle" width="400px" />
		<button id="submitBtn" label="Submit" />
		<button id="cancelBtn" label="Cancel" />
	<textbox id="editDesc" rows="5" width="90%" />
package demo.composite;

import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Caption;
import org.zkoss.zul.Div;
import org.zkoss.zul.Groupbox;
import org.zkoss.zul.Image;
import org.zkoss.zul.Label;
import org.zkoss.zul.Textbox;

public class ImageLabel extends Div implements IdSpace {
	private static final long serialVersionUID = 1L;
	private Groupbox item;
	private Image labelImage;
	private Caption titleCaption;
	private Label descLabel;

	public ImageLabel() {
		Executions.createComponents("/widgets/composite/composite_component/ImageLabel.zul", this, null);
		Selectors.wireComponents(this, this, false);
		Selectors.wireEventListeners(this, this);

	public String getImagePath() {
		return labelImage.getSrc();

	public void setImagePath(String imgPath) {

	public String getTitle() {
		return titleCaption.getLabel();

	public void setTitle(String title) {

	public String getDescription() {
		return descLabel.getValue();

	public void setDescription(String description) {

	InplaceEditor itemInplaceEditor;

	@Listen("onClick = #titleCaption")
	public void titleEdit() {
		if (itemInplaceEditor == null) {
			itemInplaceEditor = new InplaceEditor();
		} else {

	// Editable Area after click the description
	public class InplaceEditor extends Div implements IdSpace {
		private static final long serialVersionUID = 1L;
		private Textbox editTitle, editDesc;

		public InplaceEditor() {
			Executions.createComponents("/widgets/composite/composite_component/InplaceEditor.zul", this, null);
			Selectors.wireComponents(this, this, false);
			Selectors.wireEventListeners(this, this);

		@Listen("onClick = #submitBtn")
		public void submitTitleUpdate() {
			Events.postEvent(new LabelEditEvent());

		@Listen("onClick = #cancelBtn")
		public void cancelEdit() {
			itemInplaceEditor = null;

	// Customize Event
	public static final String ON_LABEL_EDIT = "onLabelEdit";

	// Editable Event which contains the content of title and description
	public class LabelEditEvent extends Event {
		private static final long serialVersionUID = 1L;

		public LabelEditEvent() {
			super(ON_LABEL_EDIT, ImageLabel.this);

		public String getCurrentTitle() {
			return getTitle();

		public String getCurrentDesc() {
			return getDescription();