Creating a Database-driven Application
This page is for ZK 5 and is out of date. If you are interested in translating our tutorial into French please contact us.
Before You Start
Translations are available in English, Français, Español, Italiano and 日本語.
Other Introductions
- Tutorial - A brief tutorial guides you through the most fundamental features and concepts of ZK.
- Creating Hello World with Eclipse and ZK Studio - Step by step to create a Web application from scratch with Eclipse and ZK Studio.
- Creating a simple sightseeing application - A tutorial exploring some of ZK's key elements by creating a sightseeing application without using database
- ZK Essentials - A book which walks you through the key concepts and features by building a working application from the ground up.
Une réelle application avec une base de données
Nous allons vous montrer, étape par étape, comment développer une application web simple qui utilise une base de données. Bien que ce tutoriel soit destiné aux nouveaux utilisateurs de ZK, il suppose que ces derniers ont une base d'expérience avec JAVA. Ne vous inquiétez pas si vous n'avez aucune connaissance des autres langages de développement web, Java est le seul à connaitre pour développer des applications web basées sur Ajax avec ZK.
Dans ce tutoriel, nous supposons que vous avez déjà installé JDK (1.4 ou supérieur), ainsi qu'un servlet container (ex.Tomcat). Si vous n'avez pas configuré votre environnement mais que vous désirez continuer avec ce tutoriel, nous vous invitons à lire Create and Run Your First ZK Application with Eclipse and ZK Studio!
Votre première application ZK : "To-do" list
Imaginons que pour mieux organiser notre journée nous ayons besoin d'une application qui contienne les évènements auxquels nous allons participer dans le futur. Une telle application web nécessite l'utilisation d'une base de données. Pour les besoins de ce tutoriel nous utiliserons la base de données Java (HSQL DB) qui ne requiert l'installation d'aucun serveur de base de données. Vous pouvez voir une Démo Live de l'application ici.
Télécharger le fichier ZK Todo
- Téléchargez zk-todo-0.9.zip.
- Décompressez zk-todo-0.9.zip, le zip contient les dossiers todo.war, hsqldb, ainsi qu'un fichier readme.txt.
Exécuter l'application sans IDE
- Copiez le dossier hsqldb dans le dossier root d'Apache Tomcat. (ex.C:\)
- Copiez todo.war dans le dossier $TOMCAT_HOME\webapps.
- Démarrez Apache Tomcat.
- Ouvrez votre navigateur et allez à l'adresse http://localhost:8080/todo.
Exécuter l'application avec un IDE
- Copiez le dossier hsqldb dans le dossier racine du workspace d'eclipse. (ex.C:\).
- Démarrez Eclipse.
- Sélectionnez File > Import .
- Dans la boite de dialogue Import, sélectionnez Web > WAR file et ensuite cliquez sur Next.
- Utilisez le bouton Browse pour localiser le fichier todo.war.
- Cliquez sur Finish pour importer le projet.
- Faites un click-droit sur le projet todo dans l'explorateur et sélectionnez Run As > Run on Server
- Sélectionnez Apache > Tomcat v6.0 Server dans la boite de dialogue de choix du serveur et cliquez sur Finish
- Un navigateur va être automatiquement lancé pour visualiser l'exemple "todo".
Tous les Scénarios
- Encodez un évènement, et ensuite cliquez sur le bouton Add pour l'enregistrer dans la base de données.
- Sélectionnez une ligne du tableau pour afficher les informations relatives à un évènement et pour pouvoir les modifier, cliquez sur le bouton Update pour mettre à jour l'évènement.
- Sélectionnez une ligne du tableau et appuyez sur le bouton Delete pour supprimer l'évènement correspondant.
Modèle
Dans les paragraphes suivants, nous allons introduire le schéma de la base de données, le domaine objet et l'objet DAO.
Schéma de la basse de données
Un table qui devra contenir les évènements de notre application devra posséder les attributs suivants: event id, event name, event priority et event date.
La structure de la base de données est donnée ci-dessous.
Field | Type |
---|---|
id | varchar(50) |
name | varchar(50) |
priority | int |
date | date |
Domaine Objet
Nous créons un domaine objet correspondant à la table ci-dessus comme suit:
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.
}
Objet d'accès aux données (Data Access Object)
Pour accéder facilement aux données de la base de données, nous avons besoin d'un DAO avec les méthodes suivantes: findAll(),delete(),insert() et 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) {
}
}
Visualisation
Votre premier composant ZK
La première étape est de créer un fichier avec une extension zul, disons todo.zul, et le positionner dans le dossier home de votre application web, par exemple,$TOMCAT_HOME/webapps/ProjectName/. Vous déclarez un composant ZK de la même manière que vous déclareriez un composant avec HTML.
Essayez de déclarer votre premier composant window comme suit:
<window title="To do list" border="normal">
</window>
Démarrez ensuite Tomcat et utiliser un navigateur pour visualiser cette page, ex. http://localhost:8080/todo/todo.zul. Le résultat est une fenêtre avec comme titre 「To do List」.
Dans ZK, tout est composant, de sorte que vous pouvez changer le titre, la largeur, et la bordure de votre fenêtre selon vos préférences. C'est assez simple et intuitif. Essayez de changer ces attributs et visualisez le résultat.
Relations hiérarchiques entre les composants ZK
Essayons maintenant d'enrichir cette page avec d'autres composants ZK. Vu que nous devons afficher des données dans un tableau, nous pourrions utiliser une listbox qui est conçue pour présenter des données. Nous la déclarons donc à l'intérieur des balises du composant fenêtre comme suit:
<window title="To do list" border="normal">
<listbox id="box" multiple="true" rows="5">
</listbox>
</window>
Dans cet exemple, le composant listbox est un enfant du composant window. En effet, il existe une relation hiérarchique entre les composants ZK, et si vous essayez de déclarer un composant dans un contexte erroné, vous allez générer une exception, par exemple, si vous déclarez un composant fenêtre comme enfant d'une listbox.
Un composant imbriqué
Une listbox est un composant imbriqué, qui supporte 2 types de composants enfants, listhead (aka: colonne d'un tableau), et listitem (aka: ligne d'un tableau). Dans la déclaration de la listbox, nous donnons la valeur 「box」 à son attribut id et nous pouvons dès lors utiliser cet attribut pour faire référence à la 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>
Mais nous n'en avons pas terminé. Déclarons 3 composants listheader dans les balises listhead pour obtenir 3 colonnes -- 「Item」, 「Priority」, et 「Date」 -- dans le tableau:
<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>
Vu que nous avons 3 colonnes dans le tableau, chaque ligne nécessite 3 champs. Déclarons donc 3 composants listcell dans les balises du composant 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 structure imbriquée de la listbox se présente comme suit:
listbox +-- listhead | | | +-- listheader | +-- listitem | +-- listcell
Composants pour l'entrée de données
En plus d'afficher les évènements dans la listbox, nous devons encoder les informations relatives à un évènement, notamment son nom (valeur de type texte), sa priorité (valeur numérique) et sa date (valeur de type date). Pour cela, nous déclarons une textbox, une intbox et une datebox dans les balises de la fenêtre comme suit:
<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>
Composants de Layout
Pour distinguer ces composants de la listbox ci-dessus, nous déclarons une groupbox et y regroupons certains composants. Ceci aura pour effet de dessiner une bordure autour de tous composants se trouvant à l'intérieur de la groupbox (dans ce cas-ci les composants d'entrée de données).
<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>
En plus du composant groupbox , nous déclarons un composant caption pour afficher un label 「Event」 au dessus de la groupbox. Ce composant caption travaille de la même manière que l'élément legend d'HTML.
Contrôleur
Nous avons besoin d'évènements qui gèrent l'affichage, l'ajout, l'édition et la suppression des données. Dans les paragraphes suivants, nous allons implémenter l'interaction entre notre application web et la base de données.
Définir un contrôleur
La première étape consiste en la définition d'un EventController qui hérite de org.zkoss.zk.ui.util.GenericForwardComposer.
Implémenter org.zkoss.zk.ui.util.GenericForwardComposer
Créer un EventController qui hérite org.zkoss.zk.ui.util.GenericForwardComposer vous facilite l'accès au View et définit des méthodes CRUD liées dans le fichier Java.
// 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() {
}
}
Associer le Contrôleur avec le View
Pour implémenter l'interaction entre le View et le Contrôleur, nous indiquons dans l'attribut apply du composant window le chemin de notre EventController.
<window id="win" title="To do list" width="640px" border="normal"
apply="org.zkforge.todo.event.EventController">
......
Afficher les données dans le View
Afficher les données de la base de données dans le View ne nécessite que 3 étapes:
- Activer le mécanisme de liaison de données (DataBinding)
- Associer les données au View
- Définir un Template
Activer le mécanisme de liaison de données (DataBinding)
Pour utiliser le mécanisme Data Binding, nous devons activer le Data Binding Manager en définissant une page Initializer(org.zkoss.zkplus.databind.AnnotateDataBinderInit) en tête de page.
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<window id="win" title="To do list" width="640px" border="normal"
apply="org.zkforge.todo.event.EventController">
....
Associer les données au View
L'étape suivante consiste en la récupération des données depuis la base de données via l'EventController en utilisant les expressions(win$composer.allEvents) qui vont invoquer EventController.getAllEvents(). La méthode EventController.getAllEvents() renvoie une liste qui contient tous les évènements stockés dans la base de données qui peuvent être associés à l'attribut model de la 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}">
....
Vous pouvez suivre l'étape suivante pour définir un template UI qui va afficher les données dans le View.
Définir un Template
Il est possible de définir un template UI pour le DataBinding qui affichera les données dans les composants UI correspondants. Ceci est réalisé en utilisant l'attribut self de listitem pour définir une variable pour représenter chaque instance de ToDoEvent. Chaque listcell est dès lors utilisée pour afficher les données de chaque ToDoEvent.
<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>
Pour de plus amples informations, merci de vous référer à Associate UI Components with a Collection.
Synchroniser les Views
Lorsque l'utilisateur choisit un ToDoEvent dans la listbox, il faut l'afficher aussi dans la groupbox. Il serait bien plus pratique que ces jobs pénibles soient faits automatiquement. Data Binding est la solution! Il suffit simplement d'associer les données avec les composants UI. DataBinding synchronisera l'état de tous les composants UI lorsque la propriété des données aura été modifiée.
- Définir une Instance dans le Contrôleur
- Associer plusieurs composants UI à l'Instance EventController
Définir une Instance "ToDoEvent" dans le Contrôleur
Définissez une instance ToDoEvent dans EventController, et définissez les accesseurs et modificateurs (méthodes getter et setter) pour qu'elle soit accessible depuis le View.
// 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;
}
Associer plusieurs composants UI à l'Instance EventController
D'abord, associez l'instance d'EventController avec la propriété selectedItem de la listbox. Ensuite, associez les propriétés de ToDoEvent avec les composants UI correspondants, notamment textbox, intbox, et datebox. Dès que l'utilisateur sélectionne un élément dans la listbox, l'instance sera mise à jour en même temps que les composants 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}" />
Synchroniser le View avec la base de données
En plus de l'affichage des données de la base de données dans le View, nous souhaitons implémenter les évènements pour ajouter, mettre à jour et supprimer des évènements. Cette fonctionnalité nécessite 3 étapes: surveiller les activités de l'utilisateur, interagir avec le Contrôleur pour mettre à jour la base de données et mettre à jour le View.
Renseigner l'Event Listener au Contrôleur
Pour surveiller les activités de l'utilisateur, renseignez simplement un écouteur d'évènement onClick sur les 3 boutons suivants, add, update et delete. Lorsque l'utilisateur clique sur un de ces boutons, les méthodes correspondantes définies dans l'EventController sont invoquée. Ces méthodes sont définies comme suit:
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" />
Mettre à jour le View grâce à Databinding
Après l'interaction avec la base de données, la dernière étape est de mettre à jour le View. Demandez simplement au Databinding de mettre le modèle à jour pour vous quand l'utilisateur clique sur n'importe lequel des 3 boutons. Essayez d'indiquer l'attribut load-after dans l'attribut model de la listbox, et le Databinding mettra automatiquement le View à jour lorsque l'évènement spécifié survient.
<?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>