ZK5.0和客户端+服务器端相结合的编程方式

译注:我对JSF比较熟悉,最近想研究一些其它的事件驱动的基于组件的WEB层框架,继Wicket和GWT之后,我开始仔细学习了一下ZK,虽然之前说的那几种框架也各有很多的优点,但ZK还是给了我很强的冲击。肤浅的总结一下:

  • 丰富漂亮的组件库。这一点我觉得比JSF和Wicket要强。JSF虽然有很多三方组件库,但是各自为政,虽然都是基于规范的,但大多做了自己的扩展,很难一起使用,比如要想把IceFaces和RichFaces一起用可能就不太现实。致命的是这些三方组件库虽然多但很少有精品,ADF算一个,但必须和Oracle其它部分结合起来才能尽显威力,而且Oracle的产品都是很贵的商业产品。GWT有GXT和SmartGWT到是都很好。
  • 不用编写客户端脚本。这一点其实其他几个框架也都能不同程度的做到,GWT做的最好,但他是client-generate的,这样GWT和服务器的结合相对其它几个server-generate的框架来说比较难一些。JSF和Wicket感觉没有ZK做的透明。就像这篇文章中说的ZK从5.0开始也支持客户端的编程模型了,在某些场合下也是一种不错的选择。而且ZK在UI中可以直接使用服务器脚本,对于一些小项目或原型项目基本可以不写Controller,开发速度可以提升不少。
  • 很棒的可视化开发工具。可以看一下Visual RIA Builder - How to Build a CRUD Application wtih ZK Studio 这个视频。我觉的这得益于ZUL这种界面语言。比如JSF使用jsp或facelets做UI语言,好处是你可以使用任何的html、css和javascript,提供了极大的灵活性,但是这种灵活性都开发一个可视化的IDE来说就是一个很大的难题。而ZUL中各种元素标签的种类和使用方式那是很明确的,这样可以大大简化可视化工具的开发。至于表现的灵活性ZK通过提供大量的组件尤其是布局来解决。
  • ZK的社区比较活跃,不断的在开发和改进,文档和教材以及示例都很丰富。

我现在还在学习,上面只是粗浅的认识,下面是关于ZK5的一篇文章,我照着文章几分钟就做出了一个还挺漂亮的示例,现在把它翻译一下。

原文参见:ZK 5.0 and Client+Server Fusion

 

 

简介

 

从ZK 5开始,开发者不仅能(继续)享受使用服务器为中心的便利开发方式,而且还能使用客户端编程进行全面的控制。二者可以按需选择。这篇文章中将为您分别演示使用服务器为中心的模式和客户端/服务器混合模式来编写一个真实的应用。

 

应用: ZK Finance(ZK 财经)

ZK finance 是一个真实的应用,用户可以查找一个股票的历史价格,并通过一个表格和图表来显示结果。下面是一个界面图,在左面板上有一个搜索框,右边是显示结果的表格和图表。

 

纯粹的服务器处理方式

首先我们使用服务器端编程来实现这个应用,我们使用MVC (Model-View-Controller) 模式。

 

模型

这里有两个对象——股票和价格。它们是一对多的关系,一支股票有多个历史价格。

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


}
 

然后我们创建一个DAO对象来负责提供股票数据,StockDAO.java

public class StockDAO {


private List stocks = new LinkedList();


public Stock getStock(int id) {}


public List findAll() {}


 


}
 

 

View

提供一个搜索功能在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>
 

我们使用一个数据表格和图表来显示价格

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

 

搜索功能

在StockController.java中我们给TextBox注册一个onChanging事件的监听器,当用户输入时我们搜索股票并把结果显示在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();


	}


}
 

当用户点击任何一支股票,onSelect事件被触发,onSelect$itemList()这个监听方法被执行,然后重新加载price.zul,显示这支股票的历史价格。

 

在表格和图中显示股票的历史价格

在PriceController.java中我们得到股票的历史价格的数据并为图表构建一个合适的模型。

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;


	}	


}
 

 

Server+client混合模式

除了上面的纯粹的服务器编程方式,我们再介绍一种混合模式。 我们想提高搜索的响应速度,那可以把相应的代码移到客户端。下面我们用搜索功能做一个例子。

 

修改现有的代码

为了在客户端实现搜索功能,首先我们要加载股票数据到客户端,我们使用一个include组件从服务器端加载data.xml。

<include id="data" src="data.xml" comment="true"/>
 

 

其次在服务器端的控制器(StockController.java) 可以不要了,它的功能将在客户端实现。

<borderlayout id="main">
 

另外我们不再在服务器端生成股票列表了,把这部分相关的服务器端代码也除去。

<listbox id="list" rows="10" width="300px">
 

 

客户端编程

为了在客户端实现搜索功能,我们为listbox 创建一个更新方法来生成用户搜索的股票项目。

<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), 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: ),因为这个监听器是在客户端的。

<textbox id="inp" w:onChanging="this.$f('list').update(event.data.value)"/>
 

此外,我们还要在一开始显示所有的股票,这需要调用注册一个bind_方法来实现初始化,这个方法将调用listbox的update方法。

<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>
 

 

和服务器端交互

最后,还需要和服务器端交换,用户点击一支股票,需要告诉服务器更新历史价格的表格和图表。在listbox上指定onSelect事件,这样一旦用户点击了一个股票,ZK引擎将给服务器发送一个onSelect事件。

<listbox id="list" onSelect="" rows="10" width="300px">


 

接下来,为了在服务器端处理onSelect事件, 我们在服务器端实现一个service,通过它我们可以实现股票历史价格的更新:

<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>
 

 

总结

在上文中,我们演示了两种不同的实现方式——纯粹的服务器编程和客户端/服务器混合编程——来实现同一个应用。无论使用哪一种,ZK 5 提供了一种新的方式来平衡易于开发和更多的可控性。

 

 

译注:最后上传两张IDE中的截图吧,我用的是ZK3.6.2,客户端/服务器端混合编程的方式没有去做。


ZK5.0和客户端+服务器端相结合的编程方式_第1张图片
 
ZK5.0和客户端+服务器端相结合的编程方式_第2张图片

你可能感兴趣的:(编程,应用服务器,JSF,zk,gwt)