【IT168 技术文档】简介
从ZK 5开始,开发者不仅能(继续)享受使用服务器为中心的便利开发方式,而且还能使用客户端编程进行全面的控制。二者可以按需选择。这篇文章中将为您分别演示使用服务器为中心的模式和客户端/服务器混合模式来编写一个真实的应用。
应用: ZK Finance(ZK 财经)
ZK finance 是一个真实的应用,用户可以查找一个股票的历史价格,并通过一个表格和图表来显示结果。下面是一个界面图,在左面板上有一个搜索框,右边是显示结果的表格和图表。
纯粹的服务器处理方式
首先我们使用服务器端编程来实现这个应用,我们使用MVC (Model-View-Controller) 模式。
模型
这里有两个对象——股票和价格。它们是一对多的关系,一支股票有多个历史价格。
Stock.java
Java代码
private int _id;
private String _name;
private List _priceItems = new ArrayList();
....
getter and setter methods
}
Price.java
Java代码
private String _date;
private double _open;
private double _high;
private double _low;
private double _close;
private int _volumn;
....
getter and setter methods
}
然后我们创建一个DAO对象来负责提供股票数据,StockDAO.java
Java代码
private List stocks = new LinkedList();
public Stock getStock(int id) {}
public List findAll() {}
}
View
提供一个搜索功能在listbox中显示搜索的结果
index.zul
Xml代码
<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>
我们使用一个数据表格和图表来显示价格
price.zul
Xml代码
<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
搜索功能
在StockController.java中我们给TextBox注册一个onChanging事件的监听器,当用户输入时我们搜索股票并把结果显示在ListBox中
StockController.java
Java代码
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();
}
}
当用户点击任何一支股票,onSelect事件被触发,onSelect$itemList()这个监听方法被执行,然后重新加载price.zul,显示这支股票的历史价格。
在表格和图中显示股票的历史价格
在PriceController.java中我们得到股票的历史价格的数据并为图表构建一个合适的模型。
PriceController.java
Java代码
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;
}
}
Server+client混合模式
除了上面的纯粹的服务器编程方式,我们再介绍一种混合模式。 我们想提高搜索的响应速度,那可以把相应的代码移到客户端。下面我们用搜索功能做一个例子。
修改现有的代码
为了在客户端实现搜索功能,首先我们要加载股票数据到客户端,我们使用一个include组件从服务器端加载data.xml。
Xml代码
Xml代码
Xml代码
客户端编程
为了在客户端实现搜索功能,我们为listbox 创建一个更新方法来生成用户搜索的股票项目。
Xml代码
....
<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), a way to get fellow component. It is the same as getFellow(id), i.e., an alias of getFellow.
http://www.zkoss.org/2005/zk/client is the so-called client namespace that tells ZK engines to generate the code at the client-side instead of server-side.
然后我们为textbox注册一个客户端的onChange监听方法,这个将方法调用listbox的update方法去更新listbox。注意我们要声明一个命名空间(w: ),因为这个监听器是在客户端的。
Xml代码
此外,我们还要在一开始显示所有的股票,这需要调用注册一个bind_方法来实现初始化,这个方法将调用listbox的update方法。
Xml代码
<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>
和服务器端交互
最后,还需要和服务器端交换,用户点击一支股票,需要告诉服务器更新历史价格的表格和图表。在listbox上指定onSelect事件,这样一旦用户点击了一个股票,ZK引擎将给服务器发送一个onSelect事件。
Xml代码
接下来,为了在服务器端处理onSelect事件, 我们在服务器端实现一个service,通过它我们可以实现股票历史价格的更新:
Xml代码
....
</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>
总结
在上文中,我们演示了两种不同的实现方式——纯粹的服务器编程和客户端/服务器混合编程——来实现同一个应用。无论使用哪一种,ZK 5 提供了一种新的方式来平衡易于开发和更多的可控性。
译注:最后上传两张IDE中的截图吧,我用的是ZK3.6.2,客户端/服务器端混合编程的方式没有去做。