Processing...
Description & Source Code
  • Description
  • View
    demo.zul
    car_detail.zul
    order_result.zul
  • Controller
    OrderController.java
    ResultController.java
  • Model
    Accessories.java
    Car.java
    CarData.java
    CarService.java
    Category.java
    OrderItem.java

This demo shows how to display a group of components in a pop-up, how to notify users with messages, how to prompt users for an input, and how to dynamically create a modal window as a custom dialog.


Controller Data Accessing

We may access variables in the controller from view by the use of an EL Expression:${expression}. The implicit variable win$composer is a reference to the controller which was assigned to the Window component with the ID: win. For instance, to assign the component attribute model with the a data model called orderItemModel in the controller, we write model="${win$composer.orderItemModel}".


Notification

When we wish to notify users of simple messages or hints, a convenient way is calling the Clients.showNotification() methods. We may also supply the method with additional arguments such as position and duration, among others.

@Listen("onCreate = #win")
public void init() {
	Clients.showNotification("...", "info", tosCheckbox, "end_center", 3000);
}

Notice in the snippet that the notification is shown when an onCreate is triggered by the creation of a Window component with the ID win.


Popup

Popup is a container component that may group and display several components when invoked; hence making it an ideal component to show notification with more detailed information.

As demonstrated in the snippet below, a Popup is assigned to a Row component via the popup attribute. The first part of the argument specifies the ID of the Popup we wish to invoke and the second one specifies the relative position of the Popup with respect to the Row.

<row popup="detail_${each.car.carId}, position=end_before" style="cursor:pointer">
	<div>
		...
		<popup id="detail_${each.car.carId}">
			<include src="/.../car_detail.zul" orderItem="${each}" />
		</popup>
	</div>
	...
</row>

The Include component is used to "include" a "modularised" ZUL page that can be reused.


Informed Message and Confirmation Dialog

Messagebox provides a set of utilities to show a message and prompt users for a decision.

To show users a message, we simply invoke the static method show(String) in controller.

Messagebox.show("Please read the terms of service and accept it before you submit the order.");

If we want to prompt users for a decision, we can invoke other static methods such as show(String, String, Messagebox.Button[], String, EventListener<Messagebox.ClickEvent>) in controller. These methods have additional arguments for adjusting the dialog, such as the icon, title and buttons, among others.

EventListener<ClickEvent> clickListener = new EventListener<Messagebox.ClickEvent>() {
	public void onEvent(ClickEvent event) throws Exception {...}
};
Messagebox.show("Are you sure you want to cancel?", "Cancel Order", new Messagebox.Button[]{
		Messagebox.Button.YES, Messagebox.Button.NO}, Messagebox.QUESTION, clickListener);

Customized Dialog

A modal Window is a good option when heavy customization on the dialog is required.

We may construct a content rich Window and make it a separate zul file as a template page (in this demo, order_result.zul).

<window id="resultWin" title="Order Result" width="500px" border="normal" apply="...ResultController">
	...
</window>

We can then call the utility method Executions.createComponents(String, Component, Map<?,?>) to render the template page and return it as a Window component as shown in the snippet below.

Finally, we can display the Window by invoking its doModal() method.

Map<String, Object> arguments = ...
String template = "/.../order_result.zul";
Window window = (Window)Executions.createComponents(template, null, arguments);
window.doModal();

We have full control of this custom dialog implementation through its controller. When the dialog is no longer needed, we should manually detach the Window by invoking its detach() method.

public class ResultController ...{
	@Wire
	private Window resultWin;

	@Listen("onClick = #closeButton")
	public void close() {
		resultWin.detach();
	}
}
demo.zul
<window id="win" title="Confirm Your Order" width="600px" border="normal"
	apply="demo.getting_started.dialog_popup.OrderController">
	<vlayout>
		<grid model="${win$composer.orderItemsModel}">
			<columns>
				<column label="Model" align="center" />
				<column label="Make" align="center" />
				<column label="Price" align="center" />
				<column label="Quantity" align="center" />
				<column label="Subtotal Price" align="center" />
			</columns>
			<template name="model">
				<row popup="detail_${each.car.carId}, position=end_before" style="cursor:pointer">
					<div>
						<label value="${each.car.model}" />
						<popup id="detail_${each.car.carId}">
							<include src="/widgets/getting_started/dialog_popup/car_detail.zul"
								orderItem="${each}" />
						</popup>
					</div>
					<label value="${each.car.make}" />
					<label value="${each.unitPrice}" />
					<label value="${each.quantity}" />
					<label value="${each.subtotalPrice}" />
				</row>
			</template>
		</grid>
		Total Price: ${win$composer.totalPrice}
		<checkbox id="tosCheckbox" label=" I have read and accepted the terms of service." />
		<hlayout width="100%" style="text-align:center">
			<button id="submitButton" label="Submit" />
			<button id="cancelButton" label="Cancel" />
		</hlayout>
	</vlayout>
</window>
car_detail.zul
<vlayout width="300px">
	<label value="${orderItem.car.model}" style="font-size: 14px; font-weight: bold;" />
	<grid>
		<rows>
			<row>
				Make:
				<label value="${orderItem.car.make}" />
			</row>
			<row>
				Origin:
				<hlayout valign="middle">
					<label value="${orderItem.car.country}" />
					<image src="/widgets/getting_started/img/${orderItem.car.country}.png" />
				</hlayout>
			</row>
			<row>
				Type:
				<label value="${orderItem.car.type}" />
			</row>
			<row>
				Displacement:
				<label value="${orderItem.car.engineDisplacement} c.c." />
			</row>
			<row>
				Transmission:
				<hlayout valign="middle">
					<image
						src="/widgets/getting_started/img/${orderItem.car.autoTransmission ? 'at' : 'mt'}.png" />
					<label value="${orderItem.car.autoTransmission ? 'AT' : 'MT'}" />
				</hlayout>
			</row>
			<row>
				Accessories:
				<label value="${orderItem.car.accessories}" />
			</row>
			<row>
				Price:
				<label value="${orderItem.unitPrice}" />
			</row>
		</rows>
	</grid>
</vlayout>
order_result.zul
<window id="resultWin" title="Order Result" width="500px" border="normal"
	apply="demo.getting_started.dialog_popup.ResultController">
	<grid model="${arg.orderItems}">
		<columns>
			<column label="Order Number" align="center" />
			<column label="Model" align="center" />
			<column label="Make" align="center" />
			<column label="Quantity" align="center" />
			<column label="Subtotal Price" align="center" />
		</columns>
		<template name="model">
			<row>
				<label value="${each.itemId}" />
				<label value="${each.car.model}" />
				<label value="${each.car.make}" />
				<label value="${each.quantity}" />
				<label value="${each.subtotalPrice}" />
			</row>
		</template>
	</grid>
	Total Price: ${arg.totalPrice}
	<hlayout width="100%" style="text-align: center">
		<button id="closeButton" label="Close" />
	</hlayout>
</window>
OrderController.java
package demo.getting_started.dialog_popup;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.EventListener;
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.zk.ui.util.Clients;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Messagebox.ClickEvent;
import org.zkoss.zul.Window;
import demo.getting_started.Car;
import demo.getting_started.CarService;
import demo.getting_started.CarServiceImpl;
import demo.getting_started.OrderItem;

public class OrderController extends SelectorComposer<Component> {
	private static final long serialVersionUID = 1L;

	private CarService carService = new CarServiceImpl();
	private ListModel<OrderItem> orderItemsModel = new ListModelList<OrderItem>();

	@Wire
	private Checkbox tosCheckbox;

	public OrderController() {
		List<Car> cars = carService.findAll();
		for(int i = 0; i < 3; ++i) {
			Car car = cars.get(i);
			((ListModelList<OrderItem>)orderItemsModel).add(new OrderItem("XYZ-00" + i, 2, car.getCost() + 3000.0, car));
		}
	}

	public ListModel<OrderItem> getOrderItemsModel() {
		return orderItemsModel;
	}

	public String getTotalPrice() {
		double totalPrice = 0.0;
		for(OrderItem orderItem : ((ListModelList<OrderItem>)orderItemsModel).getInnerList())
			totalPrice += orderItem.getSubtotalPrice();
		return MessageFormat.format("{0,number,#.0}", totalPrice);
	}
	
	@Listen("onCreate = #win")
	public void init() {
		Clients.showNotification("Accept terms of service before submit the order","info",tosCheckbox,"end_center",3000);
	}

	@Listen("onClick = #submitButton")
	public void submit() {
		// TOS should be checked before accepting order
		if(tosCheckbox.isChecked()) {
			carService.order(((ListModelList<OrderItem>)orderItemsModel));
			// show result
			Map<String, Object> arguments = new HashMap<String, Object>();
			arguments.put("orderItems", orderItemsModel);
			arguments.put("totalPrice", getTotalPrice());
			String template = "/widgets/getting_started/dialog_popup/order_result.zul";
			Window window = (Window)Executions.createComponents(template, null, arguments);
			window.doModal();
		} else {
			Messagebox.show("Please read the terms of service and accept it before you submit the order.");
		}
	}

	@Listen("onClick = #cancelButton")
	public void cancel() {
		// ask confirmation before canceling order
		EventListener<ClickEvent> clickListener = new EventListener<Messagebox.ClickEvent>() {
			public void onEvent(ClickEvent event) throws Exception {
				if(Messagebox.Button.YES.equals(event.getButton())) {
					// cancel order
					// ...
					// notify user
					Messagebox.show("The order has been cancelled.");
				}
			}
		};
		Messagebox.show("Are you sure you want to cancel?", "Cancel Order", new Messagebox.Button[]{
				Messagebox.Button.YES, Messagebox.Button.NO }, Messagebox.QUESTION, clickListener);
	}
	
}
ResultController.java
package demo.getting_started.dialog_popup;

import org.zkoss.zk.ui.Component;
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.Window;

public class ResultController extends SelectorComposer<Component> {
	private static final long serialVersionUID = 1L;

	@Wire
	private Window resultWin;

	@Listen("onClick = #closeButton")
	public void close() {
		resultWin.detach();
	}
}
Accessories.java
package demo.getting_started;

public class Accessories {
	boolean abs;
	boolean airbag;
	boolean gps;
	boolean keyless;

	public Accessories() {
	}

	public Accessories(boolean abs, boolean airbag, boolean gps, boolean keyless) {
		this.abs = abs;
		this.airbag = airbag;
		this.gps = gps;
		this.keyless = keyless;
	}

	public boolean isAbs() {
		return abs;
	}

	public void setAbs(boolean abs) {
		this.abs = abs;
	}

	public boolean isAirbag() {
		return airbag;
	}

	public void setAirbag(boolean airbag) {
		this.airbag = airbag;
	}

	public boolean isGps() {
		return gps;
	}

	public void setGps(boolean gps) {
		this.gps = gps;
	}

	public boolean isKeyless() {
		return keyless;
	}

	public void setKeyless(boolean keyless) {
		this.keyless = keyless;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		if(abs)
			sb.append(", ABS");
		if(airbag)
			sb.append(", Airbag");
		if(gps)
			sb.append(", GPS");
		if(keyless)
			sb.append(", Keyless");
		if(sb.length() <= 0)
			sb.append("None");
		else
			sb.delete(0, 2);
		return sb.toString();
	}
}
Car.java
package demo.getting_started;

import java.util.Set;

public class Car {

	private String carId;
	private String model;
	private String picture;
	private String make;
	private String country;
	private String type;
	private double cost;
	private int engineDisplacement;
	private boolean autoTransmission;
	private Accessories accessories;
	private Set<String> salesmen;

	public Car() {
	}

	public String getCarId() {
		return carId;
	}

	public void setCarId(String carId) {
		this.carId = carId;
	}

	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}

	public String getPicture() {
		return picture;
	}

	public void setPicture(String picture) {
		this.picture = picture;
	}

	public String getMake() {
		return make;
	}

	public void setMake(String make) {
		this.make = make;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public double getCost() {
		return cost;
	}

	public void setCost(double cost) {
		this.cost = cost;
	}

	public int getEngineDisplacement() {
		return engineDisplacement;
	}

	public void setEngineDisplacement(int engineDisplacement) {
		this.engineDisplacement = engineDisplacement;
	}

	public boolean isAutoTransmission() {
		return autoTransmission;
	}

	public void setAutoTransmission(boolean autoTransmission) {
		this.autoTransmission = autoTransmission;
	}

	public Accessories getAccessories() {
		return accessories;
	}

	public void setAccessories(Accessories accessories) {
		this.accessories = accessories;
	}

	public Set<String> getSalesmen() {
		return salesmen;
	}

	public void setSalesmen(Set<String> salesmen) {
		this.salesmen = salesmen;
	}

	public String toString() {
		return model;
	}
}
CarData.java
package demo.getting_started;

import java.util.Arrays;
import java.util.List;

public class CarData {

	public static List<String> getAccessories() {
		return Arrays.asList(new String[]{"ABS", "Airbag", "GPS", "Keyless"});
	}

	public static List<String> getCountries() {
		return Arrays.asList(new String[]{"China", "France", "Germany", "Italy", "Japan", "Korea", "Sweden",
				"Taiwan", "United Kingdom", "United States"});
	}

	public static List<String> getSalesmen() {
		return Arrays.asList(new String[]{"Adam", "Brian", "Cary", "Danny", "Edward", "Franklin", "Geroge"});
	}

	public static List<String> getTypes() {
		return Arrays.asList(new String[]{"MPV", "SUV", "Sedan", "Sport", "Supercar", "Van"});
	}

}
CarService.java
package demo.getting_started;

import java.util.List;

public interface CarService {

	/**
	 * Retrieve all cars in the car store.
	 * @return all cars.
	 */
	public List<Car> findAll();

	/**
	 * Store or modify a car in car store.
	 */
	void store(Car car);

	/**
	 * Store or modify a inventory item in car store.
	 */
	void store(InventoryItem inventoryItem);

	/**
	 * Order cars.
	 */
	void order(List<OrderItem> orderItems);

	/**
	 * Retrieve the root of car categories.
	 */
	Category getCarCategoriesRoot();

	/**
	 * Count cars by filter.
	 */
	int countByFilter(String filter);

	/**
	 * Query cars by filter.
	 */
	List<Car> queryByFilter(String filter);
}
Category.java
package demo.getting_started;

import java.util.LinkedList;
import java.util.List;

public class Category {
	private String name;
	private String description;
	private List<Category> children = new LinkedList<Category>();

	public Category(String name, String description) {
		this.name = name;
		this.description = description;
	}

	public void addChild(Category child) {
		if(child != null)
			children.add(child);
	}

	public void removeChild(Category child) {
		if(child != null)
			children.remove(child);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public List<Category> getChildren() {
		return children;
	}

	public void setChildren(List<Category> children) {
		this.children = children;
	}
}
OrderItem.java
package demo.getting_started;

import org.zkoss.bind.annotation.DependsOn;

public class OrderItem {
	private String itemId;
	private int quantity;
	private double unitPrice;
	private Car car;

	public OrderItem(String itemId, int quantity, double unitPrice, Car car) {
		this.itemId = itemId;
		this.quantity = quantity;
		this.unitPrice = unitPrice;
		this.car = car;
	}

	@DependsOn({"unitPrice", "quantity"})
	public double getSubtotalPrice() {
		return unitPrice * quantity;
	}

	public String getItemId() {
		return itemId;
	}

	public void setItemId(String itemId) {
		this.itemId = itemId;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	public double getUnitPrice() {
		return unitPrice;
	}

	public void setUnitPrice(double unitPrice) {
		this.unitPrice = unitPrice;
	}

	public Car getCar() {
		return car;
	}

	public void setCar(Car car) {
		this.car = car;
	}

}