Te mostraremos, paso a paso, como construir una sencilla aplicación web con ZK que use una base de datos. La aplicación web permitirá almacenar sucesos que vamos a hacer en el futuro o sea una lista de TO-DO. Una aplicación web así, requeriría usar una base de datos. Para la conclusión de este tutorial usaremos una base de datos Java (HSQL DB) la cual no requiere que instalemos un servidor de base de datos [1].
Pasos para ejecutar la aplicación:
- Descargue el código fuente de aquí.
- cd todo
- mvn jetty:run (si usted no tiene maven instalado por favor descárguelo aquí )
- Abra su navegador y coloque la siguiente url: http://localhost:8080/todo
Todos los Escenarios
§ Introduce toda la información relativa a un evento y presiona el botón Add para insertarlo en la base de datos.
§ Selecciona cualquier fila en la tabla para mostrar la información del evento en los campos de abajo para que modificar su información, después presiona el botón Update para modificar la información.
§ Selecciona cualquier fila en la tabla y presiona el botón Delete para eliminar el "Event" seleccionado.
Modelo
En los siguientes párrafos, el esquema de la base de datos, el objeto del dominio y el objeto DAO serán introducidos.
Esquema de la base de datos
Una tabla de la base de datos la cual va a almacenar los datos de nuestra aplicación, necesitará los siguientes atributos: event id, event name, event priority and event date.
El esquema de base de datos está listado abajo.
Field | Type |
id | varchar(50) |
name | varchar(50) |
priority | int |
date | date |
Objeto Dominio
Para la la tabla de arriba, creamos el objeto de dominio correspondiente, como se indica:
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 ommited, please refer to the source code.
}
Objeto de Acceso a Datos
Para que fácilmente se acceda a nuestra base de datos necesitaremos un objeto DAO con los siguientes métodos: findAll(),delete(),insert() and update().
public class EventDAO {
// The implementation is ommited, 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) {
}
}
La Vista
Tu primer componente ZK
El primer paso es crear un fichero cuya extensión debe ser zul, pongamos todo.zul, y situamos este fichero bajo el directorio de nuestra aplicación web, por ejemplo, $TOMCAT_HOME/webapps/ProjectName/. Declaras un componente ZK de igual forma a como declaras un componente usando HTML.
Prueba a declarar tu primer componente window de la siguiente forma:
<window title="To do list" border="normal">
</window>
Después inicia Tomcat y usa el navegador para visitar la página, eg. http://localhost:8080/todo/todo.zul. El resultado se muestra abajo, window con el título 「To do List」.
Todo en ZK es un componente. Puedes cambiar el título, el alto y el borde de tu window Es muy sencillo e intuitivo. Intenta cambiar estos atributos y comprueba el resultado.
Relaciones jerárquicas entre los componentes de ZK
Lo siguiente, vamos a intentar enriquecer esta página con más componentes ZK. Para mostrar datos en una tabla, podemos usar un componente listbox el cual está diseñado para mostrar datos. Para insertar un listbox lo declaramos dentro de los tags del componente window, como se indica:
<window title="To do list" border="normal">
<listbox id="box" multiple="true" rows="5">
</listbox>
</window>
En este ejemplo, el componente listbox es un componente hijo de window. Sí, hay relaciones jerárquicas entre los componentes de ZK, te encontrarás una excepción UI si intentas declarar un componente dentro de un contexto equivocado, por ejemplo, al declarar un componente window como hijo de un componente listbox.
Un componente anidado
Un listbox es un componente anidado, el cual soporta dos tipos de componentes hijos, listhead (aka: columna de la tabla), and listitem (aka: fila de la tabla). Con la declaración del listbox, asignamos el atributo id a 「box」, ahora podemos usar este atributo para referenciar el listbox.
<window id="win" title="To do list" width="640px" border="normal">
<listbox id="box" multiple="true" rows="5">
<listhead>
</listhead>
<listitem>
</listitem>
</listbox>
</window>
Aún no hemos terminado. Vamos a declarar tres componentes listheader dentro de los tags de listhead. Necesitamos tres columnas -- 「Item」, 「Priority」, and 「Date」 -- dentro de la tabla, como se indica:
<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>
Tenemos tres columnas en nuestra tabla, cada fila de la tabla también requerirá tres campos. Declaramos tres componentes listcell dentro de los tags del componente listitem.
<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>
La estructura anidada del componente listbox quedaría así:
listbox
+-- listhead
| |
| +-- listheader
|
+-- listitem
|
+-- listcell
Componentes para introducir información
En adición a mostrar estos eventos en el listbox, necesitamos introducir información relativa al evento, incluyendo el evento name (campo texto), el evento priority (campo numérico) y el evento date (campo fecha). Para lograr esto, declare un textbox, un intbox y un datebox dentro de los tags del componente window de la siguiente forma:
<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="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" />
</window>
Componentes Layout
Para distinguir los componentes de entrada de datos (input) de los componentes listbox de abajo, declaramos un groupbox para agrupar los componentes. Esto dibujará un borde alrededor de los componentes que estén contenidos dentro del groupbox, en este caso, los componentes de entrada de datos (input).
<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>
Adicionalmente a los componentes groupbox declaramos un componente caption para mostrar una etiqueta 「Event」 a lo largo de la caja que agrupa a los componentes. El componente caption trabaja de forma similar al legendario elemento en HTML.
Controlador
Nuestros requisitos incluyen mostrar, añadir, editar y borrar sucesos/eventos. En los siguientes párrafos, implementaremos las interacciones entre nuestra aplicación web y la base de datos.
Definiendo un Controlador
El primer paso es definir un EventController el cual herede de org.zkoss.zk.ui.util.GenericForwardComposer.
Implementando org.zkoss.zk.ui.util.GenericForwardComposer
Crea un EventController el cual heredará de org.zkoss.zk.ui.util.GenericForwardComposer permitiéndote un fácil acceso a la vista y definir un CRUD mediante los métodos Java definidos.
// org.zkforge.todo.event.EventController.java
public class EventController extends GenericForwardComposer {
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.
public List<TodoEvent> getAllEvents() {
return eventDao.findAll();
}
public void onClick$add() {
}
public void onClick$update() {
}
public void onClick$delete() {
}
}
Asociando el Controlador con la Vista
Para implementar la interacción entre la Vista y el Controlador asignamos el atributo apply al componente window en la ruta de nuestro EventController.
<window id="win" title="To do list" width="640px" border="normal"
apply="org.zkforge.todo.event.EventController">
......
Visualizando datos en una Vista
Para visualizar los datos recuperados de la base de datos en nuestra Vista solo requerimos tres pasos:
- Activar un mecanismo de DataBinding Mechanism
- Asociar datos con la Vista
- Definir una plantilla
Activando el mecanismo de DataBinding
Para usar el mecanismo Data Binding, deberemos activar el Data Binding Manager (org.zkoss.zkplus.databind.AnnotateDataBinderInit) en la parte superior de la página.
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<window id="win" title="To do list" width="640px" border="normal"
apply="org.zkforge.todo.event.EventController">
....
Asociando los datos con la vista
El paso siguiente es recuperar los datos de la base de datos mediante el EventController usando la expresión (win$composer.allEvents) para invocar EventController.getAllEvents(). El método EventController.getAllEvents() devuelve una lista que contiene todos los eventos almacenados en la base de datos que pueden ser asociados al atributo model del Listbox.
<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}">
....
Puedes seguir los siguientes pasos para definir una plantilla de Interfaz de Usuario para renderizar los datos dentro de la vista.
Definiendo una plantilla
Es posible definir una plantilla de interfaz de usuario para renderizar DataBinding dentro de los correspondientes componentes de interfaz de usuario. Esto se logra gracias a la utilización de los atributos que ofrece el componente "listitem" definiendo una variable para representar cada instancia a reflejar.
<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, load-after='add.onClick, delete.onClick, update.onClick'}"
selectedItem="@{win$composer.current}">
<listhead>
<listheader label="Item" sort="auto(name)" />
<listheader label="Priority" width="80px" sort="auto(priority)" />
<listheader label="Date" width="170px" sort="auto(date)" />
</listhead>
<listitem self="@{each='event'}" value="@{event}">
<listcell label="@{event.name}" />
<listcell label="@{event.priority}" />
<listcell label="@{event.date}" />
</listitem>
</listbox>
Para más información, por favor dirígete a Associate UI Components with a Collection.
Sincronización de vistas
Una vez el usuario selecciona un ToDoEvent en el listbox, Necesitamos mostrarlo en el componente groupbox también. Sería mucho mejor si este tedioso trabajo fuera realizado de forma automáticamente. Data Binding es la respuesta! Simplemente asocia los datos con todo lo relacionado con los componentes de UI. El DataBinding se sincronizará entonces con todos los componentes de Interfaz de Usuario una vez la propiedad ha sido modificada.
- Definir una instancia en un Controlador
- Asociar múltiples componentes UI a la instancia
Definir una instancia "ToDoEvent" a un Controlador
Define una instancia ToDoEvent' en EventController, y define métodos getter y setter para que puedan ser accesibles desde la vista.
// org.zkforge.todo.event.EventController.java
public class EventController extends GenericForwardComposer {
private TodoEvent current = new TodoEvent();
// Omitted...
public TodoEvent getCurrent() {
return current;
}
public void setCurrent(TodoEvent current) {
this.current = current;
}
Asociando múltiples componentes UI con la instancia EventController
Primero, asocia la instancia EventController con la propiedad selectedItem del listbox. Después, asocia las propiedades del ToDoEvent con el correspondiente componente UI, incluyendo textbox, intbox, y datebox. Una vez el usuario selecciona un item en un listbox, la instancia se actualizará en relación a los componentes UI.
<listbox id="box" multiple="true" rows="5"
model="@{win$composer.allEvents, load-after='add.onClick, delete.onClick, update.onClick'}"
selectedItem="@{win$composer.current}">
<!-- Omitted -->
<groupbox>
<caption label="Event" />
Item: <textbox id="name" cols="25" value="@{win$composer.current.name}" />
Priority: <intbox id="priority" cols="1" value="@{win$composer.current.priority}" />
Date: <datebox id="date" cols="8" value="@{win$composer.current.date}" />
Sincronizando la vista con la base de datos
Además de mostrar los datos de la base de datos en la vista, nos gustaría implementar eventos para añadir, actualizar y eliminar. Esta funcionalidad requiere de tres pasos, monitorizar la actividad del usuario, interactuando con los controladores para actualizar la base de datos y actualizar la vista.
- Registrar el evento Listener en el controlador
- Actualizar la vista usando Databinding
Registrar el evento Listener en el Controlador
Para monitorizar la actividad del usuario, simplemente registramos un evento listener onClick' en los siguientes tres botones add, update and delete. Cuando cliqueamos en estos botones que se corresponden a los métodos definidos en EventController estos serán invocados. Estos métodos son definidos así:
public class EventController extends GenericForwardComposer {
private EventDAO eventDao = new EventDAO();
// Omitted...
public void onClick$add() {
if (current != null) {
current.setId(UUID.randomUUID().toString());
if (validate(current)) {
// insert into database
eventDao.insert(current);
}
}
}
public void onClick$update() {
if (current != null && validate(current)) {
// update database
eventDao.update(current);
}
}
public void onClick$delete() {
if (current != null && validate(current)) {
eventDao.delete(current);
}
}
}
<button id="add" label="Add" />
<button id="update" label="Update" />
<button id="delete" label="Delete" />
Actualizar la vista usando Databinding
Después de interactuar con la base de datos, el último paso es actualizar la vista. Simplemente notificar al Databinding para que actualice el modelo por ti, cuando el usuario cliqueé en cualquier botón. Intenta registrar un atributo load-after en el atributo model del listbox, y el Databinding actualizará la vista automáticamente si el evento especificado es capturado.
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<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, load-after='add.onClick, delete.onClick, update.onClick'}"
selectedItem="@{win$composer.current}">
<!-- Omitted... -->
<button id="add" label="Add" />
<button id="update" label="Update" />
<button id="delete" label="Delete" />
</groupbox>
</window>
Eso es todo, esperamos que le sea útil y quedamos al tanto de sus comentarios.