Creating a Database-driven Application"

From Documentation
(36 intermediate revisions by 7 users not shown)
Line 1: Line 1:
 
=Before You Start=
 
=Before You Start=
  
Translations are available in [[ZK_Getting_Started/Creating a Database-driven Application|English]], [[ZK_Getting_Started_fr/Creating a Database-driven Application|Français]], [[ZK_Getting_Started_es/Creating a Database-driven Application|Español]], [[ZK_Getting_Started_it/Creating a Database-driven Application|Italiano]] and [http://jp.zkoss.org/doc/tutorial-jp.dsp 日本語].
+
Translations are available in [[ZK_Getting_Started/Creating a Database-driven Application|English]], [[ZK_Getting_Started_fr/Creating a Database-driven Application|Français]], [[ZK_Getting_Started_es/Creating a Database-driven Application|Español]] and [[ZK_Getting_Started_it/Creating a Database-driven Application|Italiano]].
  
 
'''Other Introductions'''
 
'''Other Introductions'''
  
* [[ZK_Getting_Started/Tutorial|Tutorial]] - A brief tutorial guides you through the most fundamental features and concepts of ZK.  
+
* [[ZK_Getting_Started/Learn_ZK_in_10_Minutes|Tutorial]] - A brief tutorial guides you through the most fundamental features and concepts of ZK.  
 
* [[ZK_Installation_Guide/Quick_Start/Create_and_Run_Your_First_ZK_Application_with_Eclipse_and_ZK_Studio|Creating Hello World with Eclipse and ZK Studio]] - Step by step to create a Web application from scratch with Eclipse and [http://www.zkoss.org/product/zkstudio.dsp ZK Studio].  
 
* [[ZK_Installation_Guide/Quick_Start/Create_and_Run_Your_First_ZK_Application_with_Eclipse_and_ZK_Studio|Creating Hello World with Eclipse and ZK Studio]] - Step by step to create a Web application from scratch with Eclipse and [http://www.zkoss.org/product/zkstudio.dsp ZK Studio].  
 
* [[ZK_Getting_Started/Creating a Simple Sightseeing Application|Creating a simple sightseeing application]] - A tutorial exploring some key elements of ZK by creating a sightseeing application without using database.
 
* [[ZK_Getting_Started/Creating a Simple Sightseeing Application|Creating a simple sightseeing application]] - A tutorial exploring some key elements of ZK by creating a sightseeing application without using database.
Line 11: Line 11:
  
 
= A database-driven real-world application =
 
= A database-driven real-world application =
We will show you, step by step, how to develop a simple Web application that uses a database. Although this tutorial is intended for new users of ZK, it does require that users have at least a small amount of experience with Java. Do not worry if you lack knowledge of other Web development languages, Java is all you need to know in order to develop Ajax-enabled Web applications with ZK.
+
We will show you, step by step, how to develop a simple Web application that uses a database. Although this tutorial is intended for new users of ZK, it is suggested that users must have at least a a basic knowledge about Java. Do not feel panic if you do not have any knowledge of other Web development languages, Java is all you need to know in order to develop Ajax-enabled Web applications with ZK.
  
In this tutorial, we assume that you have already installed [http://java.sun.com/ JDK] (1.4 or above), and a Servlet container (e.g., [http://tomcat.apache.org/ Tomcat]). For more information, please take a look at [[ZK_Installation_Guide/Quick_Start/Create_and_Run_Your_First_ZK_Application_with_Eclipse_and_ZK_Studio|Creating Hello World with Eclipse and ZK Studio]].
+
In this tutorial, we assume that you have already installed [http://java.sun.com/ JDK] (1.5 or above), and a Servlet container (e.g., [http://tomcat.apache.org/ Tomcat]). For more information, please take a look at [[ZK_Installation_Guide/Quick_Start/Create_and_Run_Your_First_ZK_Application_with_Eclipse_and_ZK_Studio|Creating Hello World with Eclipse and ZK Studio]].
  
 
== Your first ZK application "To-do" list ==
 
== Your first ZK application "To-do" list ==
Imagine that in order to plan our day better, we need an application which stores events we are going to deal with in the future. Such a Web application would require the use of a database. For the purposes of this tutorial, we'll use the Java database ([http://hsqldb.org/ HSQL DB]) which does not require us to install a database server. You could try this [http://www.zkoss.org/todo tutorial application online].
+
Imagine that in order to plan our day better, we need an application which stores events we are going to deal with in the future. Such a Web application would require the use of a database. For the purposes of this tutorial, we'll use the Java database ([http://hsqldb.org/ HSQL DB]) which does not require us to install a database server.  
  
==Download the ZK Todo file==
+
==Download the ZK Todo source==
#Download [https://sourceforge.net/project/downloading.php?group_id=156140&filename=zktodo-0.9.zip&a=35868794 zk-todo-0.9.zip].
+
 
#Unzip '''zk-todo-0.9.zip''', which contains the following '''todo.war''', '''hsqldb''' folder, and a '''readme.txt'''.
+
#Download [https://github.com/zkbooks/CADDA/zipball/master master branch]
  
 
==Running the sample application without an IDE==
 
==Running the sample application without an IDE==
#Copy the '''hsqldb''' folder to the root directory where you installed the Apache Tomcat. (ex.'''C:\''')
+
#Use the command git clone git@github.com:zkbooks/CADDA.git todo
# Copy '''todo.war to the '''$TOMCAT_HOME\webapps''' directory.
+
##'''OR''' [https://github.com/zkbooks/CADDA/zipball/master Download the source] and extract to a folder named "todo"
# Start '''Apache Tomcat'''.
+
#cd todo
 +
#mvn jetty:run (if you don't have maven install please [http://maven.apache.org/download.html download here])
 
# Open your browser and navigate to [http://localhost:8080/todo http://localhost:8080/todo].
 
# Open your browser and navigate to [http://localhost:8080/todo http://localhost:8080/todo].
  
==Running the sample application with an IDE==
+
==Running the sample application with Eclipse==
#Copy '''hsqldb''' folder to the root directory where your eclipse workspace located. (ex.'''C:\''').
 
#Activate Eclipse.
 
 
#Select '''File''' > '''Import''' .
 
#Select '''File''' > '''Import''' .
#In the Import dialog, select '''Web > WAR file''' and then click '''Next'''.
+
#Select "Maven" > "Checkout Maven project from SCM" (if not available install the [http://eclipse.org/m2e/download/ m2e plugin])
#Using the '''Browse''' button to locate '''todo.war'''.
+
#Select "git" and use the URL "git://github.com/zkbooks/CADDA.git"
 
#Click '''Finish''' to import the Web Project.
 
#Click '''Finish''' to import the Web Project.
 
#Right click on '''todo''' project in the explorer and select '''Run As > Run on Server'''
 
#Right click on '''todo''' project in the explorer and select '''Run As > Run on Server'''
Line 50: Line 49:
  
 
== DataBase Schema ==
 
== DataBase Schema ==
A database table which holds our application's data will need the following attributes: event id, event name, event priority and event date.  
+
A database table which holds our application's data needs the following attributes: event id, event name, event priority and event date.  
  
The database schema is as listed below.
+
The database schema is as listed as below:
  
 
{| class="prettytable" border="1" width="300px" cellspacing="0" cellpadding="3"
 
{| class="prettytable" border="1" width="300px" cellspacing="0" cellpadding="3"
Line 92: Line 91:
 
     }
 
     }
  
     // getter and setter methods are ommited, please refer to the source code.
+
     // getter and setter methods are omitted, please refer to the source code.
 
}
 
}
 
</source>
 
</source>
Line 101: Line 100:
 
<source lang="java">
 
<source lang="java">
 
public class EventDAO {
 
public class EventDAO {
     // The implementation is ommited, please refer to the source code.
+
     // The implementation is omitted, please refer to the source code.
 
public List<TodoEvent> findAll() {
 
public List<TodoEvent> findAll() {
 
}
 
}
Line 117: Line 116:
  
 
= View =
 
= View =
 +
 +
This section outlines how one can build the application view from scratch.
 +
 
== Your first ZK component ==
 
== Your first ZK component ==
The first step is to create a file whose file extension must be zul, such as todo.zul, and place this file under the home directory of your web application, for example, $TOMCAT_HOME/webapps/ProjectName/. The way you declare a ZK component is pretty much the same as you declare a component using HTML.  
+
The first step is to create a file which the file extension must be zul, such as todo.zul, and place this file under the src/main/webapp directory as per the Maven standards. The way you can declare a ZK component is pretty much the same as you declare a component using HTML.  
  
Try declaring your first&nbsp;window&nbsp;component as follows:
+
Try declaring your first ''window'' component as follows:
  
 
<source lang="xml">
 
<source lang="xml">
Line 127: Line 129:
 
</source>
 
</source>
  
Then start Tomcat and use a browser to visit this page, eg. http://localhost:8080/todo/todo.zul. The result is shown below, a&nbsp;window&nbsp;with the title  of '''To do List'''.
+
Then start your servlet container and use a browser to visit this page, eg. http://localhost:8080/todo/index.zul. The result is shown above, a&nbsp;window&nbsp;with the title  of '''To do List'''.
  
  
Line 133: Line 135:
  
 
== Hierarchical relationships among ZK components ==
 
== Hierarchical relationships among ZK components ==
Next, let's try to enrich this page with more ZK components. Since we need to show data in a table, we could utilize a listbox component which is designed for displaying data. To insert a listbox, we have to declare it within the enclosing tags of the&nbsp;window&nbsp;component, as follows:
+
 
 +
Next, let's try to enrich this page by adding more ZK components. Since the applicaiton displays data in a table, one could utilize a listbox component for data display. To insert a listbox, we have to declare it within the enclosing tags of the&nbsp;window&nbsp;component, as follows:
 
<source lang="xml">
 
<source lang="xml">
 
  <window title="To do list" border="normal">
 
  <window title="To do list" border="normal">
Line 141: Line 144:
 
</source>
 
</source>
  
In this example, the&nbsp;listbox&nbsp;component is a child component of the window. Yes, there are hierarchical relationships among ZK components, and you will encounter a UI exception if you try to declare a component within a wrong context, for example, declaring a window component as a child of a listbox component.
+
In this example, the&nbsp;listbox&nbsp;component is a child component of the window. Yes, there are hierarchical relationships among ZK components, and one will encounter a UI exception if one tries to declare a component within a wrong context, for example, declaring a window component as a child of a listbox component.
  
 
== A nested component ==
 
== A nested component ==
A listbox&nbsp;is a nested component, which supports two kinds of child components, listhead (aka: column of table), and listitem (aka: row of table). Within the declaration of the&nbsp;listbox, we set its id attribute to '''box''', we can now use this attribute to reference the listbox.
+
A listbox&nbsp;is a nested component, which supports two different kinds of child components, listhead (aka: column of table), and listitem (aka: row of table). Within the declaration of the&nbsp;listbox, we set its id attribute to '''box'''. We can now use this attribute for listbox reference.
  
 
<source lang="xml">
 
<source lang="xml">
Line 157: Line 160:
 
</source>
 
</source>
  
We are not done yet. Let's declare three listheader components within the enclosing tags of listhead since we require three columns -- '''Item''', '''Priority''', and '''Date''' -- within the table. Please see the following:
+
However, this is not finished yet. Let's declare three listheader components within the enclosing tags of listhead as three columns are required -- '''Item''', '''Priority''', and '''Date''' -- within the table. Please see the following:
 
<source lang="xml">
 
<source lang="xml">
 
<window id="win" title="To do list" width="640px" border="normal">
 
<window id="win" title="To do list" width="640px" border="normal">
Line 174: Line 177:
  
  
Since we have three columns in our table, each table row also requires three fields. Declare three listcell components within the enclosing tags of the listitem component.
+
Since the three columns in the table and each table row also requires three fieldsthree listcell components need to be declared within the enclosing tags of the listitem component.
  
 
<source lang="xml">
 
<source lang="xml">
Line 205: Line 208:
  
 
== Input components ==
 
== Input components ==
In addition to displaying these events in the&nbsp;listbox, we need to input information about the event, including the event name (text value), event priority (numeric value) and the event date (date value). To accomplish that, we declare a textbox, an intbox and a datebox within the enclosing tags of the&nbsp;window&nbsp;component. Please see the following:
+
In addition to displaying events in the&nbsp;listbox, event information needs to inputted, including the event name (text value), event priority (numeric value) and the event date (date value). To accomplish that, a textbox, an inbox and a datebox are declared within the enclosing tags of the&nbsp;window&nbsp;component. Please see the following:
  
 
<source lang="xml">
 
<source lang="xml">
Line 221: Line 224:
 
</listitem>
 
</listitem>
 
</listbox>
 
</listbox>
Item: <textbox id="name" cols="25" />
+
Item: <textbox id="txtName" cols="25" />
Priority: <intbox id="priority" cols="1" />
+
Priority: <intbox id="txtPriority" cols="1" />
 
Date: <datebox id="date" cols="8" />
 
Date: <datebox id="date" cols="8" />
 
<button id="add" label="Add" />
 
<button id="add" label="Add" />
Line 231: Line 234:
  
 
== Layout components ==
 
== Layout components ==
To distinguish these input components from the above listbox we declare a groupbox to group the components together. This will draw a border around all the components contained within the groupbox. In this case, they are the input components.
+
To distinguish these input components from the above listbox a groupbox needs to be declared to group these components together. This will draw a border around all the components contained within the groupbox. In this case, they are the input components.
 
<source lang="xml">
 
<source lang="xml">
 
<window id="win" title="To do list" width="640px" border="normal">
 
<window id="win" title="To do list" width="640px" border="normal">
Line 258: Line 261:
 
</source>
 
</source>
  
In addition to the&nbsp;groupbox&nbsp;component, we declare a&nbsp;caption&nbsp;component to show an '''Event''' label along the top of the group box. The caption component works in a similar manner to the HTML legend element.
+
In addition to the&nbsp;groupbox&nbsp;component, a&nbsp;caption&nbsp;component is declared to show an '''Event''' label along the top of the group box. The caption component works in a similar manner to the HTML legend element.
  
= Controller =
+
= ViewModel =
Our requirements include displaying, adding, editing and deleting events. In the following paragraphs, we will implement the interaction between our web application page and the application database.
+
Our requirements include displaying, adding, editing and deleting events. In the following paragraphs, the interactions between our web application page and the application database will be implemented.
  
 
== Defining a Controller ==
 
== Defining a Controller ==
The first step is to define an EventController which extends '''org.zkoss.zk.ui.util.GenericForwardComposer'''.
 
  
=== Implementing org.zkoss.zk.ui.util.GenericForwardComposer ===
+
The first step is to define an EventViewModel which does not need to extend any class, it is just a POJO.
Create an EventController which extends org.zkoss.zk.ui.util.GenericForwardComposer allowing you to easily access the View and define CRUD related methods in the Java file.
+
 
<source lang="java">
+
 
// org.zkforge.todo.event.EventController.java
+
=== Associating the ViewModel with the View ===
  
public class EventController extends GenericForwardComposer {
+
To add interaction between the View and ViewModel, the window's apply attribute is set to BindComposer, this is a class in ZK Bind which enables binding on any control which is a child of the control on which it is delcared. For example, now any child of Window will be able to take advantage of MVVM binding.
    private static final long serialVersionUID = -9145887024839938515L;
 
    private EventDAO eventDao = new EventDAO();
 
  
    // Note: Something is omitted at here. You can view detail about this class on source code.
+
<source lang="xml" high="2,3">
 +
<window id="win" title="To do list" width="640px" border="normal"
 +
apply="org.zkoss.bind.BindComposer"
 +
viewModel="@id('vm') @init('org.zkoss.todo.event.EventViewModel')">
 +
</source>
  
    public List<TodoEvent> getAllEvents() {
+
Additionally the attribute viewModel is set, initializing our POJO using the @init command and assigning an id of "vm" for the POJO using the @id command. This is shown above on line 3.
        return eventDao.findAll();
+
 
    }
+
== Displaying data in a View ==
 +
To display retrieved data from the database in the View requires only three steps:
  
    public void onClick$add() {
+
*[[#Associating the data with View|Associating the data with View]]
    }
+
*[[#Binding the data | Binding the data]]
  
    public void onClick$update() {
+
===Associating the data with View===
    }
+
To provide the UI with appropriate data we need a method that returns a list of TodoEvents, this is easily done in the view model which has the following method displayed.
  
    public void onClick$delete() {
+
<source lang="java">
    }
+
public List<TodoEvent> getEvents() {
 +
return eventDao.findAll();
 
}
 
}
 
</source>
 
</source>
  
=== Associating the Controller with the View ===
+
After retrieving the data it needs to be bound to the view.
To implement the interaction between the View and Controller, we set the '''apply''' attribute of the '''window''' component to the path of our '''EventController'''.
 
<source lang="xml" high="2">
 
<window id="win" title="To do list" width="640px" border="normal"
 
apply="org.zkforge.todo.event.EventController">
 
  
......
+
===Binding the data===
</source>
 
  
== Displaying data in a View ==
+
It is possible to define a UI template for DataBinding to render data into corresponding UI components. This is achieved using the bind commands such as @bind and @load. For more information on these commands please refer [ZK_Developer's_Reference/MVVM/DataBinding#A_whole_new.2C_clean_annotation_expression | here]. For now all that needs to be knows is the @bind commands handles both loading and saving of data from the UI which the load commands handles @loading only.
To display retrieved data from the database in the view requires only three steps:
 
  
*[[#Activate DataBinding Mechanism|Activate DataBinding Mechanism]]
+
====Asscoiating the model and selectedItem====
*[[#Associate Data with View|Associate Data with View]]
 
*[[#Define a Template|Define a Template]]
 
  
===Activating the DataBinding mechanism===
+
The first thing that's needed is to load our viewmodel's data, this is exposed to the UI using a method in the ViewModel named "getEvents()". The id of the ViewModel is already set as "vm" as previously discussed, therefore this makes it easy to retrieve the data as follows:
To make use of the [[ZK Developer's Reference/Data Binding|Data Binding]] mechanism, we have to activate the Data Binding Manager by defining a page Initializer('''org.zkoss.zkplus.databind.AnnotateDataBinderInit''') at the top of the page.
 
  
<source lang="xml" high="1">
+
<source lang="xml">
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
+
<listbox id="box" multiple="true" rows="5" model="@bind(vm.events)">
 +
</source>
  
<window id="win" title="To do list" width="640px" border="normal"
+
The @bind annotation does the legwork as the ViewModel has already been initialized. Additionally a Listbox also has a selectedItem property, it makes sense to also provide this to the ViewModel so the data can be retrieved. To do this the ViewModel is edited to contain a java bean of type TodoEvent to hold the selected item:
apply="org.zkforge.todo.event.EventController">
+
 
....
+
<source lang="java">
 +
public TodoEvent getSelectedEvent() {
 +
return selectedEvent;
 +
}
 +
 
 +
public void setSelectedEvent(TodoEvent selectedEvent) {
 +
this.selectedEvent = selectedEvent;
 +
}
 
</source>
 
</source>
  
===Associating the data with View===
+
The relevent bean is then bound to the UI. Not here @bind is used as not only is the loading of the data needed but also when a user changes the selectedItem in the UI this change also needs to be reflected at the backend, @bind provides both loading and saving functionality so that is used. Technically using two separate commands @load & @save and specifying the bean would yield exactly the same result.
The next step is to retrieve data from database via the '''EventController''' by using the expression('''win$composer.allEvents''') to invoke '''EventController.getAllEvents()'''. The method '''EventController.getAllEvents()''' returns a list that contains all events stored in the database which can be associated to the '''model''' attribute of Listbox.  
 
  
 +
<source lang="xml">
 +
<listbox id="box" multiple="true" rows="5" model="@bind(vm.events)" selectedItem="@bind(vm.selectedEvent)">
 +
</source>
  
<source lang="xml" high="3">
+
====Displaying the model====
<window id="win" title="To do list" width="640px" border="normal"
 
apply="org.zkforge.todo.event.EventController">
 
<listbox id="box" multiple="true" rows="5" model="@{win$composer.allEvents}">
 
....
 
</source>
 
You can follow the next step to define a UI template to render the Data into View.
 
  
===Defining a Template===
+
The model has been bound and the selectedItem therefore the grid of all the items needs to be populated. This is exceptionally easy using binding.
It is possible to define a UI template for DataBinding to render data into corresponding UI components. This is achieved by utilizing the '''self''' attribute of '''listitem''' to define a variable to represent each '''ToDoEvent''' instance. Each '''listcell''' is then used to display the data of each '''ToDoEvent'''.
 
  
<source lang="xml" high="12, 13, 14">
+
<source lang="xml">
<window id="win" title="To do list" width="640px" border="normal"
+
<listbox id="box" multiple="true" rows="5" model="@bind(vm.events)"
apply="org.zkforge.todo.event.EventController">
+
selectedItem="@bind(vm.selectedEvent)">
<listbox id="box" multiple="true" rows="5"
+
<listhead>
    model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"  
+
<listheader label="Item" sort="auto(name)" />
    selectedItem="@{win$composer.current}">
+
<listheader label="Priority" width="80px"
<listhead>
+
sort="auto(priority)" />
<listheader label="Item" sort="auto(name)" />
+
<listheader label="Date" width="170px" sort="auto(date)" />
<listheader label="Priority" width="80px" sort="auto(priority)" />
+
</listhead>
<listheader label="Date" width="170px" sort="auto(date)" />
+
<template name="model" var="event">
</listhead>
+
<listitem value="@load(event)">
<listitem self="@{each='event'}" value="@{event}">
+
<listcell label="@load(event.name)" />
<listcell label="@{event.name}" />
+
<listcell label="@load(event.priority)" />
<listcell label="@{event.priority}" />
+
<listcell label="@load(event.date)" />
<listcell label="@{event.date}" />
 
 
</listitem>
 
</listitem>
</listbox>
+
</template>
 +
</listbox>
 
</source>
 
</source>
  
For more information, please refer to [[ZK Developer's Reference/Data Binding/Associate UI Components with a Collection|Associate UI Components with a Collection]].
+
The code snippet above demonstrates how to display the information row by row utilizing a template. The template is used by setting the name attribute to "model", this is a naming convention when placed inside a listbox. The name can be changed and will need to be if two templates exist. The var attribute is the name of the bean reference for the row object which will be accessed within the template.
 +
 
 +
As shown in the above code, the listitem and listcell's are bound using the @load command, data will not be changed using the UI here so @bind and @save are redundant. A Listitem and appropriate listcells will be generated for each  TodoEvent in Listbox's model, thus is the functionality of using a template within a Listbox.
  
====Add the sorting feature====
+
====Adding the sorting feature====
  
The sorting feature can be added easily by specifying the sort attribute with an expression describing how to sort the column. For example, we could specify <code>auto(name)</code> for the first <tt>listheader</tt> since it is going to show the name of the todo event. Similarly, <code>auto(priority)</code> is used to sort based on the priority of the todo event.
+
The sorting feature can be added easily by specifying the sort attribute with an expression describing how to sort the column. For example, one could specify <code>auto(name)</code> for the first <tt>listheader</tt> since it is going to show the name of the todo event. Similarly, <code>auto(priority)</code> can be used for sorting based on the priority of the todo event.
  
 
== Synchronizing Views ==
 
== Synchronizing Views ==
Once the user selects a ToDoEvent in the '''listbox''', we need to display it in the '''groupbox''' too. It would be best if these tedious jobs are done automatically.
 
[[ZK Developer's Reference/Data Binding|Data Binding]] is the answer! You simply associate the data with all related UI components. DataBinding will synchronize the state of all UI components once the property of data is modified.
 
*[[#Define an Instance in Controller|Define an Instance in Controller]]
 
*[[#Associate Multiple UI components to The Instance|Associate Multiple UI components to The Instance]]
 
  
===Define a "ToDoEvent" Instance within a Controller===
+
Previously the selectedItem has been setup to both load and save a TodoEvent bean depending on what item is selected in the Listbox and vice versa. Having achieved this one wants to display the selected events data in the textbox, intbox and date fields at the bottom of the zul file for the purposes of updating. This data must then be bound to those controls.  
Define a '''ToDoEvent''' instance in '''EventController''', and define getter and setter methods so that it can be accessed by the View.
 
<source lang="java" high="4, 6, 10">
 
// org.zkforge.todo.event.EventController.java
 
  
public class EventController extends GenericForwardComposer {
+
Eventually one will want to make changes to the data, to make this easier MVVM offers a form concept. This is used in an easy manner, on the groupbox one initializes the form attribute giving it an id of event using the @id command and loading the selectedEvent bean using the @load command. The data can then be bound using the @bind command.
    private TodoEvent current = new TodoEvent();
 
    // Omitted...
 
    public TodoEvent getCurrent() {
 
        return current;
 
    }
 
  
    public void setCurrent(TodoEvent current) {
+
<source lang="xml" high="2, 5, 7, 9">
        this.current = current;
+
<groupbox id="mygrp"
    }
+
form="@id('event') @load(vm.selectedEvent)>
 +
<caption label="Event" />
 +
Item:
 +
<textbox id="txtName" cols="25" value="@bind(event.name)" />
 +
Priority:
 +
<intbox id="txtPriority" cols="1" value="@bind(event.priority)" />
 +
Date:
 +
<datebox id="date" cols="8" value="@bind(event.date)" />
 
</source>
 
</source>
  
===Associating Multiple UI components with the EventController instance===
+
When the selectedEvent is changed the informaiton will automatically be changed in the bound controls.
Firstly, associate the EventController instance with the '''selectedItem''' property of the '''listbox'''. Then, associate the properties of '''ToDoEvent''' with the corresponding UI components, including '''textbox''', '''intbox''', and '''datebox'''. Once the user selects an item in '''listbox''', the instance will be updated accordingly along with the UI components.  
+
 
<source lang="xml" start="4" high="6, 10, 11, 12">
+
 
<listbox id="box" multiple="true" rows="5"
+
===Add, Update and Remove===
      model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"
+
 
      selectedItem="@{win$composer.current}">
+
This isn't enough though, to follow normal use cases the applications needs the ability to add, update and remove items. To make this happen 3 buttons are created next to the textbox, intbox and datebox.
<!-- Omitted -->
+
 
<groupbox>
+
<source lang="xml">
<caption label="Event" />
+
<button id="add" label="Add" onClick="@command('add')" />
Item: <textbox id="name" cols="25" value="@{win$composer.current.name}" />
+
<button id="update" label="Update" onClick="@command('update')" disabled="@load(empty vm.selectedEvent)" />
Priority: <intbox id="priority" cols="1" value="@{win$composer.current.priority}" />
+
<button id="delete" label="Delete" onClick="@command('delete')" disabled="@load(empty vm.selectedEvent)" />
Date: <datebox id="date" cols="8" value="@{win$composer.current.date}" />
 
 
</source>
 
</source>
  
== Synchronizing the View and Database ==
+
To link these buttons with a function in the ViewModel the command @command is used with a string identifying the command name, in this case "add", "update" and "delete". The subsequent code in the ViewModel looks like this:
In addition to displaying data from the database in the View, we would like to implement add, update and delete events. This functionality requires three steps, monitoring user's activity, interacting with the Controller to update database, and updating the View. They can be done by the following approaches.
 
*[[#Register Event Listener in Controller|Register Event Listener in Controller]]
 
*[[#Update the View using Databinding|Update the View using Databinding]]
 
  
=== Register Event Listener in Controller ===
+
<source lang="java" high="1, 2">
To monitor user's activities, simply register an '''onClick''' event listener on the following three buttons, '''add''', '''update''' and '''delete'''. When these buttons are clicked, corresponding methods defined in '''EventController''' will be invoked. These methods are defined below:
+
@Command("add")
<source lang="java" high="4, 15, 22">
+
@NotifyChange("events")
public class EventController extends GenericForwardComposer {
+
public void add() {
    private EventDAO eventDao = new EventDAO();
+
this.newEvent.setId(UUID.randomUUID().toString());
    // Omitted...
+
eventDao.insert(this.newEvent);
    public void onClick$add() {
+
this.newEvent = new TodoEvent();
        if (current != null) {
+
}
            current.setId(UUID.randomUUID().toString());
 
           
 
            if (validate(current)) {
 
                // insert into database
 
                eventDao.insert(current);
 
            }
 
        }
 
    }
 
  
    public void onClick$update() {
+
@Command("update")
        if (current != null && validate(current)) {
+
@NotifyChange("events")
            // update database
+
public void update() {
            eventDao.update(current);
+
eventDao.update(this.selectedEvent);
        }
+
}
    }
 
  
    public void onClick$delete() {
+
@Command("delete")
        if (current != null && validate(current)) {
+
@NotifyChange({"events", "selectedEvent"})
            eventDao.delete(current);
+
public void delete() {
        }
+
//shouldn't be able to delete with selectedEvent being null anyway
    }
+
//unless trying to hack the system, so just ignore the request
 +
if(this.selectedEvent != null) {
 +
eventDao.delete(this.selectedEvent);
 +
this.selectedEvent = null;
 +
}
 
}
 
}
 
</source>
 
</source>
  
Set the button id attributes to correspond to their respective controller method.
+
====@Command====
 +
The highlighted annotation @command corresponds to the view's command in the button's onClick method. This stats that onClick issue the related command then the binder will trigger a command in the associated ViewModel where one exists. If one does not exist an error will be shown.
 +
 
 +
====@NotifyChange====
 +
 
 +
The other annotation of note is the @NotifyChange which tells the binder that after the function has executed the referred properties will have changed. The specified properties will then be refereshed in the UI to the new values. The @NotifyChange annotation takes either a string or an array given with the format {"string", "string"}.
 +
 
 +
====Putting it all together====
 +
 
 +
Finally, the buttons correspond to a command in the ViewModel, the related properties will refresh, however, the commands still need to act on provided data. As shown in the above code snippet the add command uses a property in the view model named "newEvent" while the add and remove rely on the "selectedEvent" property. Therefore it is important to make sure that the data held in these properties is correct.
 +
 
 +
Therefore, before performing these commands the form needs to be told where to save the appropriate data.  
  
 
<source lang="xml">
 
<source lang="xml">
<button id="add" label="Add" />
+
<groupbox id="mygrp" form="@id('event') @load(vm.selectedEvent) @save(vm.selectedEvent, before='update') @save(vm.newEvent, before='add')>
<button id="update" label="Update" />
+
</source>
<button id="delete" label="Delete" />
+
 
 +
This is done using the @save command when setting the form attribute. In this case the @save command can be given conditions, such as "before" and "after" certain @comands have been executed. In this case before the update @command has been triggered the current form's TodoEvent representation will be saved into the viewmodel's selectedEvent property.  Before the add @command is triggered the form's internal TodoEvent representation will be saved to the viewmodel's newEvent property. These properties are then accessed in the viewmodel functions related to those @commands.
 +
 
 +
== Validating the data==
 +
 
 +
A very common use case is the need to validate the data in the form. ZK provides a convenient way of implementing this using validators and the MVVM's binding engine. To start a validator is added to the form which will handle all the validation necessary.
 +
 
 +
<source lang="xml">
 +
<groupbox id="mygrp"
 +
form="@id('event') @load(vm.selectedEvent) @save(vm.selectedEvent, before='update') @save(vm.newEvent, before='add') @validator('org.zkoss.todo.event.EventValidator')">
 
</source>
 
</source>
  
See the [http://www.zkoss.org/javadoc/3.6.2/zk/ javadoc] for more clarification.
+
We then create the EventValidator.
  
=== Update the View using Databinding ===
+
<source lang="java" high="4,5,9">
After interacting with the database, the last mile is to update the View. Simply notify Databinding to update the model for you when the user clicks any of the following three buttons. Try to register '''load-after''' attribute on '''model''' attribute of '''listbox''', and Databinding will update the view automatically if the specified event is triggered.
+
public class EventValidator extends AbstractValidator {
  
<source lang="xml" high="6">
+
public void validate(ValidationContext ctx) {
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
+
String name = (String)ctx.getProperties("name")[0].getValue();
 +
Integer priority = (Integer)ctx.getProperties("priority")[0].getValue();
 +
 +
if(name == null || "".equals(name))
 +
this.addInvalidMessage(ctx, "name", "You must enter a name");
 +
 +
if(ctx.getProperties("date")[0].getValue() == null)
 +
this.addInvalidMessage(ctx, "date", "You must specify a date");
 +
 +
if(priority == null || priority < 1 || priority > 10)
 +
this.addInvalidMessage(ctx, "priority", "You must give a priority > 0 && < 10");
 +
}
 +
}
 +
</source>
  
 +
The current property values are retrieved as properties of the TodoEvent, for example line 4 of the above code retrieves the value of the form's internal TodoEvent's name property, line 5 the value of the priority property etc. These values can then be tested to see if they are valid, if they are not valid a message can be added to the validationContext using the function addInvalidMessage where the first value is the context, second value is the key and 3rd value is array of messages. These keys can then be bound at UI to be displayed.
 +
 +
The UI binding would look as follows:
 +
 +
<source lang="xml">
 +
<hlayout>
 +
<label style="color:red" value="@bind(vmsgs['name'])" />
 +
<label style="color:red" value="@bind(vmsgs['priority'])" />
 +
<label style="color:red" value="@bind(vmsgs['date'])" />
 +
</hlayout>
 +
</source>
 +
 +
In the above source the labels are bound to properties of the validation message array. To set the id for the validation method array it is added to the component on which the binding and viewmodel are originally initialized, in this case the window:
 +
 +
<source lang="xml" high="4">
 
<window id="win" title="To do list" width="640px" border="normal"
 
<window id="win" title="To do list" width="640px" border="normal"
apply="org.zkforge.todo.event.EventController">
+
apply="org.zkoss.bind.BindComposer"
<listbox id="box" multiple="true" rows="5"
+
viewModel="@id('vm') @init('org.zkoss.todo.event.EventViewModel')"
    model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"
+
validationMessages="@id('vmsgs')">
    selectedItem="@{win$composer.current}">
 
<!-- Omitted... -->
 
<button id="add" label="Add" />
 
<button id="update" label="Update" />
 
<button id="delete" label="Delete" />
 
</groupbox>
 
</window>
 
 
</source>
 
</source>
 +
 +
The variable vmsgs can now be accessed anywhere like an array using the key values "name", "priority" and "date" as passed using the addInvalidMessage function.
 +
 +
=Conclusion=
 +
 +
ZK provides extremely powerful functionality in its databinder to provide developers with a hassle free way of implementing applications. To continue the learning experience please refer to the [[ZK Essentials | ZK Essentials]] book.
  
 
----
 
----
 
{{LastUpdated}}
 
{{LastUpdated}}

Revision as of 06:49, 28 March 2014

Before You Start

Translations are available in English, Français, Español and Italiano.

Other Introductions

A database-driven real-world application

We will show you, step by step, how to develop a simple Web application that uses a database. Although this tutorial is intended for new users of ZK, it is suggested that users must have at least a a basic knowledge about Java. Do not feel panic if you do not have any knowledge of other Web development languages, Java is all you need to know in order to develop Ajax-enabled Web applications with ZK.

In this tutorial, we assume that you have already installed JDK (1.5 or above), and a Servlet container (e.g., Tomcat). For more information, please take a look at Creating Hello World with Eclipse and ZK Studio.

Your first ZK application "To-do" list

Imagine that in order to plan our day better, we need an application which stores events we are going to deal with in the future. Such a Web application would require the use of a database. For the purposes of this tutorial, we'll use the Java database (HSQL DB) which does not require us to install a database server.

Download the ZK Todo source

  1. Download master branch

Running the sample application without an IDE

  1. Use the command git clone [email protected]:zkbooks/CADDA.git todo
    1. OR Download the source and extract to a folder named "todo"
  2. cd todo
  3. mvn jetty:run (if you don't have maven install please download here)
  4. Open your browser and navigate to http://localhost:8080/todo.

Running the sample application with Eclipse

  1. Select File > Import .
  2. Select "Maven" > "Checkout Maven project from SCM" (if not available install the m2e plugin)
  3. Select "git" and use the URL "git://github.com/zkbooks/CADDA.git"
  4. Click Finish to import the Web Project.
  5. Right click on todo project in the explorer and select Run As > Run on Server
  6. Select Apache > Tomcat v6.0 Server in the server type dialog and click Finish
  7. A browser will be activated automatically to explore the todo example.

Todo.png

All Scenarios

  • Key in related information about an event, and press the Add button to insert it into database.
  • Select any row in the table to show event information in the fields below for users to modify, then press Update button to update the event.
  • Select any row in the table, and press the Delete button to delete the selected "Event".

Model

In the following paragraphs, the database schema, domain object, and the DAO object will be introduced.

DataBase Schema

A database table which holds our application's data needs the following attributes: event id, event name, event priority and event date.

The database schema is as listed as below:

Field Type
id varchar(50)
name varchar(50)
priority int
date date

Domain Object

When the todo example is run, a corresponding domain object is created. The following source code can be viewed in ToDoEvent.java.

public class TodoEvent {
    private String id;
    private String name;
    private int priority;
    private Date date;

    public TodoEvent(String id, String name, int priority, Date date) {
        this.id = id;
        this.name = name;
        this.priority = priority;
        this.date = date;
    }

    // getter and setter methods are omitted, please refer to the source code.
}

Data Access Object

In order to easily access the data in our database, a DAO object with the methods of findAll(),delete(),insert() and update() is needed. The following source code can be seen in EventDAO.java when the ToDo example is run.

public class EventDAO {
    // The implementation is omitted, please refer to the source code.
	public List<TodoEvent> findAll() {
	}
	
	public boolean delete(TodoEvent evt) {
	}
	
	public boolean insert(TodoEvent evt) {
	}
	
	public boolean update(TodoEvent evt) {
    }
}

View

This section outlines how one can build the application view from scratch.

Your first ZK component

The first step is to create a file which the file extension must be zul, such as todo.zul, and place this file under the src/main/webapp directory as per the Maven standards. The way you can declare a ZK component is pretty much the same as you declare a component using HTML.

Try declaring your first window component as follows:

<window title="To do list" border="normal">
</window>

Then start your servlet container and use a browser to visit this page, eg. http://localhost:8080/todo/index.zul. The result is shown above, a window with the title of To do List.


Everything in ZK is a component. As such, you can change the title, width, and border of your window as you want. It is quite simple and intuitive. Try to change these attributes, and see the result.

Hierarchical relationships among ZK components

Next, let's try to enrich this page by adding more ZK components. Since the applicaiton displays data in a table, one could utilize a listbox component for data display. To insert a listbox, we have to declare it within the enclosing tags of the window component, as follows:

 <window title="To do list" border="normal">
  <listbox id="box" multiple="true" rows="5">
  </listbox>
 </window>

In this example, the listbox component is a child component of the window. Yes, there are hierarchical relationships among ZK components, and one will encounter a UI exception if one tries to declare a component within a wrong context, for example, declaring a window component as a child of a listbox component.

A nested component

A listbox is a nested component, which supports two different kinds of child components, listhead (aka: column of table), and listitem (aka: row of table). Within the declaration of the listbox, we set its id attribute to box. We can now use this attribute for listbox reference.

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
		</listhead>		
		<listitem>	
		</listitem>
	</listbox>
</window>

However, this is not finished yet. Let's declare three listheader components within the enclosing tags of listhead as three columns are required -- Item, Priority, and Date -- within the table. Please see the following:

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>
		</listitem>
	</listbox>
</window>


Since the three columns in the table and each table row also requires three fields, three listcell components need to be declared within the enclosing tags of the listitem component.

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>			
			<listcell />
			<listcell />
			<listcell />
		</listitem>
	</listbox>
</window>

The nested structure of the listbox component is as follows:

listbox 
+-- listhead
|    |
|    +-- listheader
|
+-- listitem
      |
     +-- listcell

Input components

In addition to displaying events in the listbox, event information needs to inputted, including the event name (text value), event priority (numeric value) and the event date (date value). To accomplish that, a textbox, an inbox and a datebox are declared within the enclosing tags of the window component. Please see the following:

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>			
			<listcell />
			<listcell />
			<listcell />
		</listitem>
	</listbox>
	Item: <textbox id="txtName" cols="25" />
	Priority: <intbox id="txtPriority" cols="1" />
	Date: <datebox id="date" cols="8" />
	<button id="add" label="Add" />
	<button id="update" label="Update" />
	<button id="delete" label="Delete" />
</window>

Layout components

To distinguish these input components from the above listbox a groupbox needs to be declared to group these components together. This will draw a border around all the components contained within the groupbox. In this case, they are the input components.

<window id="win" title="To do list" width="640px" border="normal">
	<listbox id="box" multiple="true" rows="5">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="80px" />
			<listheader label="Date" width="170px" />
		</listhead>		
		<listitem>			
			<listcell />
			<listcell />
			<listcell />
		</listitem>
	</listbox>
	<groupbox>
		<caption label="Event" />
		Item: <textbox id="name" cols="25" />
		Priority: <intbox id="priority" cols="1" />
		Date: <datebox id="date" cols="8" />
		<button id="add" label="Add" />
		<button id="update" label="Update" />
		<button id="delete" label="Delete" />
	</groupbox>	
</window>

In addition to the groupbox component, a caption component is declared to show an Event label along the top of the group box. The caption component works in a similar manner to the HTML legend element.

ViewModel

Our requirements include displaying, adding, editing and deleting events. In the following paragraphs, the interactions between our web application page and the application database will be implemented.

Defining a Controller

The first step is to define an EventViewModel which does not need to extend any class, it is just a POJO.


Associating the ViewModel with the View

To add interaction between the View and ViewModel, the window's apply attribute is set to BindComposer, this is a class in ZK Bind which enables binding on any control which is a child of the control on which it is delcared. For example, now any child of Window will be able to take advantage of MVVM binding.

<window id="win" title="To do list" width="640px" border="normal"
	apply="org.zkoss.bind.BindComposer"
	viewModel="@id('vm') @init('org.zkoss.todo.event.EventViewModel')">

Additionally the attribute viewModel is set, initializing our POJO using the @init command and assigning an id of "vm" for the POJO using the @id command. This is shown above on line 3.

Displaying data in a View

To display retrieved data from the database in the View requires only three steps:

Associating the data with View

To provide the UI with appropriate data we need a method that returns a list of TodoEvents, this is easily done in the view model which has the following method displayed.

public List<TodoEvent> getEvents() {
	return eventDao.findAll();
}

After retrieving the data it needs to be bound to the view.

Binding the data

It is possible to define a UI template for DataBinding to render data into corresponding UI components. This is achieved using the bind commands such as @bind and @load. For more information on these commands please refer [ZK_Developer's_Reference/MVVM/DataBinding#A_whole_new.2C_clean_annotation_expression | here]. For now all that needs to be knows is the @bind commands handles both loading and saving of data from the UI which the load commands handles @loading only.

Asscoiating the model and selectedItem

The first thing that's needed is to load our viewmodel's data, this is exposed to the UI using a method in the ViewModel named "getEvents()". The id of the ViewModel is already set as "vm" as previously discussed, therefore this makes it easy to retrieve the data as follows:

<listbox id="box" multiple="true" rows="5" model="@bind(vm.events)">

The @bind annotation does the legwork as the ViewModel has already been initialized. Additionally a Listbox also has a selectedItem property, it makes sense to also provide this to the ViewModel so the data can be retrieved. To do this the ViewModel is edited to contain a java bean of type TodoEvent to hold the selected item:

public TodoEvent getSelectedEvent() {
	return selectedEvent;
}

public void setSelectedEvent(TodoEvent selectedEvent) {
	this.selectedEvent = selectedEvent;
}

The relevent bean is then bound to the UI. Not here @bind is used as not only is the loading of the data needed but also when a user changes the selectedItem in the UI this change also needs to be reflected at the backend, @bind provides both loading and saving functionality so that is used. Technically using two separate commands @load & @save and specifying the bean would yield exactly the same result.

<listbox id="box" multiple="true" rows="5" model="@bind(vm.events)" selectedItem="@bind(vm.selectedEvent)">

Displaying the model

The model has been bound and the selectedItem therefore the grid of all the items needs to be populated. This is exceptionally easy using binding.

<listbox id="box" multiple="true" rows="5" model="@bind(vm.events)"
		selectedItem="@bind(vm.selectedEvent)">
	<listhead>
		<listheader label="Item" sort="auto(name)" />
		<listheader label="Priority" width="80px"
			sort="auto(priority)" />
		<listheader label="Date" width="170px" sort="auto(date)" />
	</listhead>
	<template name="model" var="event">
		<listitem value="@load(event)">
			<listcell label="@load(event.name)" />
			<listcell label="@load(event.priority)" />
			<listcell label="@load(event.date)" />
		</listitem>
	</template>
</listbox>

The code snippet above demonstrates how to display the information row by row utilizing a template. The template is used by setting the name attribute to "model", this is a naming convention when placed inside a listbox. The name can be changed and will need to be if two templates exist. The var attribute is the name of the bean reference for the row object which will be accessed within the template.

As shown in the above code, the listitem and listcell's are bound using the @load command, data will not be changed using the UI here so @bind and @save are redundant. A Listitem and appropriate listcells will be generated for each TodoEvent in Listbox's model, thus is the functionality of using a template within a Listbox.

Adding the sorting feature

The sorting feature can be added easily by specifying the sort attribute with an expression describing how to sort the column. For example, one could specify auto(name) for the first listheader since it is going to show the name of the todo event. Similarly, auto(priority) can be used for sorting based on the priority of the todo event.

Synchronizing Views

Previously the selectedItem has been setup to both load and save a TodoEvent bean depending on what item is selected in the Listbox and vice versa. Having achieved this one wants to display the selected events data in the textbox, intbox and date fields at the bottom of the zul file for the purposes of updating. This data must then be bound to those controls.

Eventually one will want to make changes to the data, to make this easier MVVM offers a form concept. This is used in an easy manner, on the groupbox one initializes the form attribute giving it an id of event using the @id command and loading the selectedEvent bean using the @load command. The data can then be bound using the @bind command.

<groupbox id="mygrp"
		form="@id('event') @load(vm.selectedEvent)>
		<caption label="Event" />
		Item:
		<textbox id="txtName" cols="25" value="@bind(event.name)" />
		Priority:
		<intbox id="txtPriority" cols="1" value="@bind(event.priority)" />
		Date:
		<datebox id="date" cols="8" value="@bind(event.date)" />

When the selectedEvent is changed the informaiton will automatically be changed in the bound controls.


Add, Update and Remove

This isn't enough though, to follow normal use cases the applications needs the ability to add, update and remove items. To make this happen 3 buttons are created next to the textbox, intbox and datebox.

<button id="add" label="Add" onClick="@command('add')" />
<button id="update" label="Update" onClick="@command('update')" disabled="@load(empty vm.selectedEvent)" />
<button id="delete" label="Delete" onClick="@command('delete')" disabled="@load(empty vm.selectedEvent)" />

To link these buttons with a function in the ViewModel the command @command is used with a string identifying the command name, in this case "add", "update" and "delete". The subsequent code in the ViewModel looks like this:

@Command("add")
@NotifyChange("events")
public void add() {
	this.newEvent.setId(UUID.randomUUID().toString());
	eventDao.insert(this.newEvent);
	this.newEvent = new TodoEvent();
}

@Command("update")
@NotifyChange("events")
public void update() {
	eventDao.update(this.selectedEvent);
}

@Command("delete")
@NotifyChange({"events", "selectedEvent"})
public void delete() {
	//shouldn't be able to delete with selectedEvent being null anyway
	//unless trying to hack the system, so just ignore the request
	if(this.selectedEvent != null) {
		eventDao.delete(this.selectedEvent);
		this.selectedEvent = null;
	}
}

@Command

The highlighted annotation @command corresponds to the view's command in the button's onClick method. This stats that onClick issue the related command then the binder will trigger a command in the associated ViewModel where one exists. If one does not exist an error will be shown.

@NotifyChange

The other annotation of note is the @NotifyChange which tells the binder that after the function has executed the referred properties will have changed. The specified properties will then be refereshed in the UI to the new values. The @NotifyChange annotation takes either a string or an array given with the format {"string", "string"}.

Putting it all together

Finally, the buttons correspond to a command in the ViewModel, the related properties will refresh, however, the commands still need to act on provided data. As shown in the above code snippet the add command uses a property in the view model named "newEvent" while the add and remove rely on the "selectedEvent" property. Therefore it is important to make sure that the data held in these properties is correct.

Therefore, before performing these commands the form needs to be told where to save the appropriate data.

<groupbox id="mygrp" 	form="@id('event') @load(vm.selectedEvent) @save(vm.selectedEvent, before='update') @save(vm.newEvent, before='add')>

This is done using the @save command when setting the form attribute. In this case the @save command can be given conditions, such as "before" and "after" certain @comands have been executed. In this case before the update @command has been triggered the current form's TodoEvent representation will be saved into the viewmodel's selectedEvent property. Before the add @command is triggered the form's internal TodoEvent representation will be saved to the viewmodel's newEvent property. These properties are then accessed in the viewmodel functions related to those @commands.

Validating the data

A very common use case is the need to validate the data in the form. ZK provides a convenient way of implementing this using validators and the MVVM's binding engine. To start a validator is added to the form which will handle all the validation necessary.

<groupbox id="mygrp"
		form="@id('event') @load(vm.selectedEvent) @save(vm.selectedEvent, before='update') @save(vm.newEvent, before='add') @validator('org.zkoss.todo.event.EventValidator')">

We then create the EventValidator.

public class EventValidator extends AbstractValidator {

	public void validate(ValidationContext ctx) {
		String name = (String)ctx.getProperties("name")[0].getValue();
		Integer priority = (Integer)ctx.getProperties("priority")[0].getValue();
		
		if(name == null || "".equals(name))
			this.addInvalidMessage(ctx, "name", "You must enter a name");		
		
		if(ctx.getProperties("date")[0].getValue() == null)
			this.addInvalidMessage(ctx, "date", "You must specify a date");
		
		if(priority == null || priority < 1 || priority > 10)
			this.addInvalidMessage(ctx, "priority", "You must give a priority > 0 && < 10");
	}
}

The current property values are retrieved as properties of the TodoEvent, for example line 4 of the above code retrieves the value of the form's internal TodoEvent's name property, line 5 the value of the priority property etc. These values can then be tested to see if they are valid, if they are not valid a message can be added to the validationContext using the function addInvalidMessage where the first value is the context, second value is the key and 3rd value is array of messages. These keys can then be bound at UI to be displayed.

The UI binding would look as follows:

<hlayout>
	<label style="color:red" value="@bind(vmsgs['name'])" />
	<label style="color:red" value="@bind(vmsgs['priority'])" />
	<label style="color:red" value="@bind(vmsgs['date'])" />
</hlayout>

In the above source the labels are bound to properties of the validation message array. To set the id for the validation method array it is added to the component on which the binding and viewmodel are originally initialized, in this case the window:

<window id="win" title="To do list" width="640px" border="normal"
	apply="org.zkoss.bind.BindComposer"
	viewModel="@id('vm') @init('org.zkoss.todo.event.EventViewModel')"
	validationMessages="@id('vmsgs')">

The variable vmsgs can now be accessed anywhere like an array using the key values "name", "priority" and "date" as passed using the addInvalidMessage function.

Conclusion

ZK provides extremely powerful functionality in its databinder to provide developers with a hassle free way of implementing applications. To continue the learning experience please refer to the ZK Essentials book.


Last Update : 2014/03/28