Processing...
Description & Source Code
A tutorial is available for this demo. Click "Tutorial" on your right to start the tutorial.
ZK supports the Model-View-ViewModel (MVVM) design pattern which automates the data-binding tasks that developers would have to otherwise implement in a traditional controller. This pattern divides an application into three parts.
  • The Model consists of application data and business rules.
  • The View means user interface. The zul page which contains ZK components represents this part. An user's interaction with components triggers events to be sent to controllers.
  • The ViewModel is type of View abstraction which contains a View's state and behavior. It is responsible for exposing data from the Model to the View and providing required action requested from the View
  • There is a binder in ZK which synchronizes data between ViewModel and View and handle events automatically according to your data binding expressions. You don't need to control components by yourself.

Write a ViewModel:
ViewModel is an abstraction of View. Therefore when we design a ViewModel, we should analyse UI's functions for what states it contains and what behaviours it has. In the ViewModel of this demo, which contains the state : keyword, car list, selected car and behavior search
public class SearchViewModel{
	
	private String keyword;
	private List<Car> carList;
	private Car selectedCar;

	public void search(){
		...
	}
	//getter & setter ...
} 
		 	
Annotate the command method :
Any behaviour which can be requested by a View is a command in a ViewModel, you should apply an annotation @Command on the behaviour method (also called command method). After the execution of the command method, a ViewModel annotated with @NotifyChange would reflect the state changes made in View.
public class SearchViewModel{
	...	
	@Command
	@NotifyChange("carList")
	public void search(){
		carList = carService.search(keyword);
	}
}
		 	
Bind View to ViewModel :
To bind View to a ViewModel, you should apply a composer called org.zkoss.bind.BindComposer, and use @id('VMID') @init('FULL.QUALIFIED.CLASSNAME') syntax in viewModel attribute. @id() is used to set ViewModel's id to an arbitrary variable name of your choice. You will use this id to reference ViewModel's properties. @init() is used to initialize the ViewModel object.
<window apply="org.zkoss.bind.BindComposer"
	viewModel="@id('vm') @init('demo.getting_started.mvvm.SearchViewModel')">
	...
</window>				 
		 	
Bind attribute with property :
To bind a component attribute to a property of ViewModel, you use @bind(VMID.PROPERTY_NAME) on the attribute of component.
		<textbox value="@bind(vm.keyword)" />	 
		 	
Bind event with command :
To bind a component event to a command of ViewModel, you use @command('COMMAND_NAME') on the event of component.
		<button label="Search" onClick="@command('search')" />			 
		 	
demo.zul
<window title="Search" width="600px" border="normal"
	viewModel="@id('vm') @init('demo.getting_started.mvvm.SearchViewModel')">
	<hlayout valign="middle">
		Keyword:
		<textbox value="@bind(vm.keyword)" />
		<button label="Search" iconSclass="z-icon-search" onClick="@command('search')" />
	</hlayout>
	<listbox height="160px" model="@bind(vm.carList)" emptyMessage="No car found in the result"
	selectedItem="@bind(vm.selectedCar)" style="margin-top:10px">
		<listhead>
			<listheader label="Model" />
			<listheader label="Make" />
			<listheader label="Price" width="20%"/>
		</listhead>
		<template name="model">
			<listitem>
				<listcell label="@bind(each.model)"></listcell>
				<listcell label="@bind(each.make)"></listcell>
				<listcell>$<label value="@bind(each.price)" /></listcell>
			</listitem>
		</template>
	</listbox>
	<hlayout style="margin-top:20px" visible="@bind(not empty vm.selectedCar)">
		<image src="@bind(vm.selectedCar.preview)" style="padding:10px" />
		<vlayout>
			<hlayout>
				Model : <label value="@bind(vm.selectedCar.model)" style="font-weight:bold"/>
			</hlayout>
			<hlayout>
				Make : <label value="@bind(vm.selectedCar.make)" style="font-weight:bold"/>
			</hlayout>
			<hlayout>
				Price : 
				<span>$<label value="@bind(vm.selectedCar.price)" style="font-weight:bold"/></span>
			</hlayout>
			<label value="@bind(vm.selectedCar.description)" />
		</vlayout>
	</hlayout>
</window>
SearchViewModel.java
package demo.getting_started.mvvm;

import java.util.List;

import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;

import demo.getting_started.tutorial.Car;
import demo.getting_started.tutorial.CarService;
import demo.getting_started.tutorial.CarServiceImpl;

public class SearchViewModel {
	
	private String keyword;
	private List<Car> carList;
	private Car selectedCar;
	
	private CarService carService = new CarServiceImpl();
	
	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}
	public String getKeyword() {
		return keyword;
	}

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

	
	@Command
	@NotifyChange("carList")
	public void search(){
		carList = carService.search(keyword);
	}
}
Car.java
package demo.getting_started.tutorial;

public class Car {

	private Integer id;
	private String model;
	private String make;
	private String preview;
	private String description;
	private Integer price;

	public Car() {
	}

	public Car(Integer id, String model, String make, String description, String preview, Integer price) {
		this.id = id;
		this.model = model;
		this.make = make;
		this.preview = preview;
		this.description = description;
		this.price = price;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getMake() {
		return make;
	}

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

	public String getPreview() {
		return preview;
	}

	public void setPreview(String preview) {
		this.preview = preview;
	}

	public String getDescription() {
		return description;
	}

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

	public Integer getPrice() {
		return price;
	}

	public void setPrice(Integer price) {
		this.price = price;
	}

	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}
}
CarService.java
package demo.getting_started.tutorial;

import java.util.List;

public interface CarService {

	/**
	 * Retrieve all cars in the catalog.
	 * @return all cars
	 */
	public List<Car> findAll();
	
	/**
	 * search cars according to keyword in name and company.
	 * @param keyword for search
	 * @return list of car that match the keyword
	 */
	public List<Car> search(String keyword);
}