ZK 5.0 and Server+Client Fusion fr
Introduction
Avec ZK 5, les développeurs peuvent non seulement profiter de la facilité de développement de l'architecture centrée serveur, mais aussi profiter du contrôle total de la programmation coté client. Le développeur peut choisir de développer une fonction coté serveur ou coté client selon ses besoins.
Dans ce document, nous allons présenter deux approches différentes, la première entièrement centrée serveur, ensuite un développement client/serveur pour implémenter une application concrète.
L'application: ZK Finance
ZK finance est une application réelle et concrète; les utilisateurs peuvent rechercher un historique des prix, visualiser le résultat dans un tableau et un graphique. Voici un snapshot de cette application. On peut y voir une boite de recherche sur le panel gauche, et un tableau ainsi qu'un graphique sur le panel droit.
Approche entièrement centrée Serveur
Pour commencer, nous utilisons l'approche entièrement centrée serveur pour implémenter cette application avec le modèle MVC (Model-View-Controller).
Modèle
Deux objets sont présents dans cette application: Stock et Price (prix). La relation entre ceux-ci est un-à-plusieurs. Chaque Stock possède plusieurs objets Price qui représentent l'historique de ses prix.
Stock.java
public class Stock {
private int _id;
private String _name;
private List _priceItems = new ArrayList();
....
getter and setter methods
}
Price.java
public class Price {
private String _date;
private double _open;
private double _high;
private double _low;
private double _close;
private int _volumn;
....
getter and setter methods
}
Ensuite, nous créons une objet DAO qui fournit les données relatives au stock. StockDAO.java
public class StockDAO {
private List stocks = new LinkedList();
public Stock getStock(int id) {}
public List findAll() {}
}
Visualisation
Nous implémentons une fonction de recherche, et nous affichons les résultats dans une Listbox.
index.zul
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<borderlayout id="main" apply="StockController">
<west title="ZK Finance" size="250px" flex="true"
splittable="true" minsize="210" maxsize="500" collapsible="true">
<panel>
<toolbar>
<label value="Search:" />
<textbox id="searchBox" ctrlKeys="#down#up"
focus="true" sclass="demo-search-inp" />
</toolbar>
<panelchildren>
<listbox id="itemList" model="@{main$composer.stocks}"
fixedLayout="true" vflex="true">
<listitem self="@{each='stock'}" value="@{stock}">
<listcell label="@{stock.name}" />
</listitem>
</listbox>
</panelchildren>
</panel>
</west>
<center>
<include id="detail"/>
</center>
</borderlayout>
Pour visualiser l'historique de prix d'un stock, nous utilisons un Grid et un LineChart.
price.zul
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<window id="main2" apply="PriceController">
<grid id="history" model="@{main2$PriceController.prices}" >
<columns menupopup="auto">
<column label="Date" />
<column label="Open" />
<column label="High" />
<column label="Low" />
<column label="Close" />
<column label="Volumn" />
</columns>
<rows>
<row self="@{each='price'}">
<label value="@{price.date}"/>
<label value="@{price.open}"/>
<label value="@{price.high}"/>
<label value="@{price.low}"/>
<label value="@{price.close}"/>
<label value="@{price.volumn}"/>
</row>
</rows>
</grid>
<chart id="line" width="500" height="250" type="line"
fgAlpha="128" model="@{main2$PriceController.cateModel}"/>
</window>
Controller
Fonction de recherche
Dans le fichier StockController.java, nous ajoutons l'écouteur d'évènement onChanging au composant Textbox de manière à récupérer instantanément toute valeur introduite par l'utilisateur. Ensuite, nous listons tous les stocks qui correspondent à la requête dans le composant Listbox .
StockController.java
public class StockController extends GenericForwardComposer {
Textbox searchBox;
Listbox itemList;
Include detail;
StockDAO dao = new StockDAO();
private static String DETAIL_URL = "price.zul";
public void onCreate$main(){
itemList.setSelectedIndex(0);
Events.postEvent(new Event(Events.ON_SELECT, itemList));
}
public void onSelect$itemList(){
int id = ((Stock)itemList.getSelectedItem().getValue()).getId();
detail.setSrc(DETAIL_URL + "?id=" + id);
}
public void onChanging$searchBox(InputEvent event) {
String key = event.getValue();
LinkedList item = new LinkedList();
List items = dao.findAll();
if (key.trim().length() != 0) {
for (Iterator iterator = items.iterator(); iterator.hasNext();) {
Stock st = (Stock) iterator.next();
if (st.getName().toLowerCase()
.indexOf(key.toLowerCase()) != -1)
item.add(st);
}
itemList.setModel(new ListModelList(item));
} else itemList.setModel(new ListModelList(items));
}
public List getStocks(){
return dao.findAll();
}
}
Dès que l'utilisateur clique sur un des Stock, nous l'indiquons à l'écouteur d'évènement onSelect pour pouvoir afficher l'historique des prix en rechargeant la page price.zul. onSelect$itemList()
Afficher le résultat dans un tableau et dans un graphique
Pour afficher l'historique des prix d'un certain stock, dans le fichier PriceController.java, nous récupérons les données relatives aux prix depuis StockDAO, et nous créons le modèle de données requis pour le graphique.
PriceController.java
public class PriceController extends GenericAutowireComposer {
private StockDAO dao = new StockDAO();
private CategoryModel cateModel;
private List items;
public PriceController() {
init();
}
public void init() {
//get stock id
int id = Integer.parseInt((String) Executions.getCurrent().getParameter("id"));
Stock stock = dao.getStock(id);
items = stock.getPriceItems();
//create category model for chart
cateModel = new SimpleCategoryModel();
for (Iterator iterator = items.iterator(); iterator.hasNext();) {
Price price = (Price) iterator.next();
cateModel.setValue(stock.getName(), price.getDate(), price.getClose());
}
}
public List getPrices(){
return items;
}
public CategoryModel getCateModel() {
return cateModel;
}
}
L'approche "Client+Server Fusion"
A coté de l'approche purement centrée serveur, nous allons illustrer une approche hybride. Disons que nous souhaitons améliorer le processus de recherche, déplaçons donc le code correspondant à cette recherche chez le client. Dans les paragraphes suivants, la fonction de recherche est utilisée comme exemple.
Modification du code existant
Pour implémenter la fonction de recherche du coté client, nous devons tout d'abord charger les données relatives au stock chez le client. Nous utilisons un composant "include" pour charger la source de données (data.xml) depuis le serveur.
<include id="data" src="data.xml" comment="true"/>
Ensuite, le code du contrôleur (StockController.java) coté serveur doit être retiré du fichier index.zul vu qu'il sera implémenté coté client.
<borderlayout id="main">
De plus, vu que nous ne générons plus la liste des stocks du coté serveur, le code correspondant doit aussi être retiré.
<listbox id="list" rows="10" width="300px">
Programmation coté client
Pour implémenter la fonction de recherche du coté client, nous créons une fonction de mise à jour de la listbox qui affiche les stocks correspondants aux choix de l'utilisateur.
<zk xmlns:w="http://www.zkoss.org/2005/zk/client">
....
<listbox id="list" rows="10" width="300px">
<attribute w:name="update"><![CDATA[
(function () {
var data;
function loadData(w) {
var xmlDoc = zUtl.parseXML(jq(w).html().replace(/^<!--|-->$/g, '').trim()),
ids = xmlDoc.getElementsByTagName("id"),
labels = xmlDoc.getElementsByTagName("name");
data = [];
jq(ids).each(function (i) {
data.push({id: this.firstChild.nodeValue, label: labels[i].firstChild.nodeValue});
});
}
function createItems (listbox, data) {
if (!data.length) return;
jq(data).each(function () {
listbox.appendChild(new zul.sel.Listitem({label: this.label, uuid: this.id}));
});
}
return function (txt) {
txt = txt || '';
if (!data) loadData(this.$f('data'));
this.clear();
createItems(this, jq.grep(data, function (item) {
return item.label.toLowerCase().indexOf(txt.toLowerCase()) != -1;
}));
this.stripe();
};
})()
]]></attribute>
</listbox>
- $f (id), une manière de récupérer le composant partenaire. C'est le même que d'écrire getFellow(id), i.e., un alias de getFellow.
- http://www.zkoss.org/2005/zk/client est le namespace client qui dit au moteur ZK de générer le code coté client au lieu de le générer coté serveur.
Ensuite, nous ajoutons l'écouteur d'évènement onChanging sur la textbox pour appeler la fonction de mise à jour de la listbox du coté client. Remarquez que nous devons spécifier le namespace client (w:), vu que l'écouteur se trouve coté client.
<textbox id="inp" w:onChanging="this.$f('list').update(event.data.value)"/>
En outre, si nous voulons afficher tous les stocks dès le début, nous devons invoquer la fonction de mise à jour du composant listbox dès l'initialisation (fonction bind_).
<listbox id="list" rows="10" width="300px">
<attribute w:name="bind_">
function (desktop, skipper, after) {
this.$bind_.apply(this, arguments);
var self = this;
after.push(function () {
self.update();
self.firstChild.setSelected(true);
});
}
</attribute>
....
</listbox>
Interagir avec les composants coté serveur
Jusqu'ici, nous avons migré la fonction de recherche chez le client. La dernière étape est de communiquer avec le serveur pour mettre à jour l'historique des prix des stocks sélectionnés. Pour garantir l'envoi instantané de l'évènement onSelect vers le serveur, il nous suffit d'indiquer cet évènement au composant listbox. Dès lors, dès que l'utilisateur clique sur un stock, le moteur ZK va envoyer un évènement onSelect au serveur.
<listbox id="list" onSelect="" rows="10" width="300px">
....
Ensuite, pour gérer directement l'évènement onSelect sur le serveur, nous pourrions implémenter un service coté serveur; dès lors, nous pouvons mettre à jour le contenu de l'historique de prix en conséquence.
<listbox id="list" onSelect="" rows="10" width="300px">
....
</listbox>
<include id="content" src="price.zul?id=1"/>
<zscript>
public class MyService implements org.zkoss.zk.au.AuService {
public boolean service(org.zkoss.zk.au.AuRequest request, boolean everError) {
final String cmd = request.getCommand();
if (cmd.equals(Events.ON_SELECT)) {
String uuid = ((List)request.getData().get("items")).get(0);
System.out.println("selected:" + uuid);
content.setSrc("price.zul?id=" + uuid);
return true;
}
return false;
}
}
list.setAuService(new MyService());
</zscript>
Résumé
Dans les paragraphes précédents, nous avons illustré deux approches différentes pour implémenter la même application; une approche purement coté serveur, et une approche hybride. Que le choix du développeur se porte sur un modèle centré serveur ou bien sur un modèle centré client, ZK 5 met en évidence une nouvelle manière d'implémenter des applications internet riches qui offrent à la fois facilité de développement et facilité de contrôle.
Téléchargement
Cette application peut être téléchargée ici.
See Also
ZK 5.0 and Client+Server Fusion (English)
Copyright © Potix Corp. This article is licensed under GNU Free Documentation License. |