技术开发 频道

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

  【IT168 技术文档】简介

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

  应用: ZK Finance(ZK 财经)

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

  纯粹的服务器处理方式

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

  模型

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

  Stock.java

  Java代码

public class Stock {  
  
  
    
private int _id;  
  
  
    
private String _name;  
  
  
    
private List _priceItems = new ArrayList();  
  
  
....  
  
  
getter and setter methods  
  
  
}  

  Price.java

  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

  Java代码

public class StockDAO {

  
private List stocks = new LinkedList();

  
public Stock getStock(int id) {}

  
public List findAll() {}

  }

  View

  提供一个搜索功能在listbox中显示搜索的结果

  index.zul

  Xml代码

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

  Xml代码 

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

  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

  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。

  Xml代码

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

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

  Xml代码

<borderlayout id="main">  

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

  Xml代码

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

  客户端编程

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

  Xml代码

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

  Xml代码

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

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

  Xml代码

<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事件。

  Xml代码

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

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

  Xml代码

<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,客户端/服务器端混合编程的方式没有去做。

0
相关文章