- 浏览: 762230 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (241)
- 个人思考 (1)
- 数据库 (5)
- java基础 (32)
- 软件工程 (2)
- zk开源框架 (15)
- 设计模式 (25)
- javascript (12)
- css (9)
- UML (2)
- CMMI软件需求 (3)
- CMMI软件设计 (2)
- CMMI软件实现 (1)
- CMMI软件测试 (1)
- 正则表达式 (4)
- 系统日志配置 (4)
- 应用服务器 (1)
- spring (7)
- XML (6)
- java web (10)
- Ajax (3)
- RichFaces (14)
- 问题 (1)
- 评论文章 (2)
- fushioncharts (2)
- MAVN (4)
- jquery (26)
- Ext js学习 (2)
- 学习心得 (2)
- CSS兼容问题 (3)
- XSL-FOP (1)
- Quartz (0)
- OSGI (1)
- spring--security (6)
- apache tools (1)
- eclispe 小技巧 (2)
- Ant (1)
- 杂记 (1)
- spring3系列 (5)
- java cache (4)
- EffectiveJava (2)
- 代码重构 (0)
最新评论
-
psz6696:
可以说是超级简单的Demo了,可惜没有演示设值注入和构造注入两 ...
模拟spring中的ClassPathXmlApplicationContext类的实现 -
ziyourJava:
[flash=200,200][img][url][list] ...
spring security进级篇 V 自定义标签控制显示 -
ztw1122:
...
zk组件开发指南(目录) -
zjysuv:
容我说一句 在座的各位都是垃圾 spring 3.2以后的@C ...
三. spring mvc 异常统一处理 -
chengwu1201:
二 基于Spring的异常体系处理
3.展现层
在客户端,展现层是一个组件的可视化部分。根据客户端的不同,它可以是HTML标记加JavaScript代码,MIDLet,XML节点和flash。出于描述的考虑,我们只讨论Ajax浏览器中的展现。即HTML标记语言加JavaScript代码。
3.1 HTML标记(HTML tags)
你可以选择你喜欢的任何Servlet技术来生成HTML标记。Zk(更确切的说是AbstractComponent)利用javax.servlet.RequestDispatcher类中的include方法去包含为展现层生成的HTML标记。
出于描述的考虑,我们在大多数的例子中采用了DSP文件(动态服务页面)。DSP是一个由zk自己开发的模板技术。其用法除了不能在代码中内嵌java代码外和jsp相似。DSP文件有诸多的优点,例如,它不需要编译,可以是jar文件的一部分,没有jsp/EL的要求等等。
3.1.1渲染中获取组件(Retrieve Component Being Rendered)
当展现层被渲染时,相关的信息是通过一个叫arg的请求属性来传递的。Arg是map类的一个实例,在arg中,被渲染的组件存储在一个叫self的实体中。因此你可以用下面的EL表达式来获取它:
${requestScope.arg.self}
你也可采用如下的java代码重新获取它:
void doGet(HttpServletRequest request, HttpServletResponse response) {
Component self = (Component)((Map)request.getAttribute("arg")).get("self");
下面是org.zkoss.zul.Image展现层的例子(用DSP展现的):
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<img id="${self.uuid}"z.type="zul.widget.Img"${self.outerAttrs}${self.innerAttrs}/>
● 你需要通过组件的UUID来生成id这个属性
● 通过调用HtmlBasedComponent中的getOuterAttrs 和 getInnerAttrs方法生成需要的各个属性。
● 如果展现层需要某段具体的JavaScript代码,z.type属性需要通过指定一个唯一的值来表示组件的类型
从上面的img例子中,大家注意到每个展现层必须有一个顶端的HTML元素。下面的展现层的例子是不正确的。因为它有两个顶端元素。
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<img id="${self.uuid}" ${self.outerAttrs}${self.innerAttrs}/>
<img/>
要使它正确,你需要用span 或div标签包含它们。
<span id="${self.uuid}" ${self.outerAttrs}${self.innerAttrs}/>
<img/>
<img/>
</span>
DSP文件的位置(DSP File Location)
根据自己的要求,你可以把DSP文件放到web应用中或jar包类库中来表示展现层。例如你要将DSP文件放到web应用中,如路径为/WEB-INF/myaddon/button.dsp,只需在下面的mold-uri元素中指明正确的路径就可以了。
<component>
<mold>
<mold-name>default</mold-name>
<mold-uri>/WEB-INF/myaddon/button.dsp</mold-uri>
</mold>
如果你想将文件放到jar文件中,你必须把它放到/web目录下,例如/web/myaddon/button.dsp是一个合法的路径,在mold URI中的配置如下:
<component>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./myaddon/button.dsp</mold-uri>
</mold>
这里的"~."在jar文件中表示的是/web目录
组件渲染器(Component Renderer)
DSP是一种设计组件界面的简单方式,但文件中EL表达式的执行效率比纯的java代码慢的多。为了最大体现java代码的执行效率,你可以开发一个Servlet或所谓的组件渲染器来代替DSP文件的这种方式。
组件渲染器是一个直接渲染产生页面的java类。它必须继承和实现org.zkoss.zk.ui.render.ComponentRenderer 接口, 该接口只有一个方法:render.例如,下面是Image渲染的java代码:
public class ImageDefault implements ComponentRenderer {
public void render(Component comp, Writer out) throws IOException {
final Image self = (Image)comp;
out.write("<img id=\"");
out.write(self.getUuid());
out.write("\" z.type=\"zul.widget.Img\"");
out.write(self.getOuterAttrs());
out.write(self.getInnerAttrs());
out.write("/>");
}
}
然后,在mold URI的配置中必须以"class:"开头,以类的名字结束,实例如下:
<component>
<mold>
<mold-name>default</mold-name>
<mold-uri>class:org.zkoss.zkmax.zul.render.ImageDefault</mold-uri>
</mold>
JavaScript 代码
除了可视化的展现外,一个组件的页面通常会在客户端有运行的代码,比如说初始化页面和用户进行交互。对于AJAX驱动程序来说,客户端的代码是用JavaScript来写的。出于方便,我们在这里只讨论AJAX驱动程序
组件类型(Component Type)
在客户端要使JavaScript代码能够运行,每个组件必须在z.type属性中提供一个唯一的类型,例如
<BUTTON id="z_ed_0" z.type="mycomps.MyButton">I am a button</BUTTON>
在语法dir1.dir2.file.Type中,最后一个点后面的部分是类型的名字,前面的部分是处理页面的JavaScript文件加载的路径。Zk利用类型作为名字入口去调用JS文件中正确的JavaScript方法。例如,假设类型是Tp,那么,如果zkTp.init方法在JavaScript文件中被声明,当zk在客户端初始化组件的时候,这个方法就会被调用。
zkTp = {}
zkTp.init = function (cmp) {
zk.listen(cmp, "onClick", zkTp.onclick);
}
zkTp.onclick = function (evt) {
var el = Event.element(evt);
...
};
JavaScript 文件的位置(JavaScript File Location)
正如上一节所描述的那样,z.type属性的第一部分是JavaScript的加载路径。例如,假设z.type属性是a.b.c.Tx,那么zk会在初始化页面前到路径/web/js/a/b/c.js中正确加载文件。注意,文件的路径是相对类的路径存储的,并且它总是以/web/js开头的。
初始化和清除(Initialization and Cleanup)
初始化(Initialization)
当zk客户端引擎初始化一个组件的页面时,如果页面的z.type属性被定义,引擎会首先检查该属性。如果该属性未指定,这意味着该页面不需要JavaScript代码,引擎会结束这个组件的初始化。
如果 z.type属性被指定,比如说是 widget.SuperButton, ZK 客户端引擎会首先/web/js/widget.js这个javascript文件, 然后检查是否有叫zkSuperButton.init的方法. 如果发现该方法,就执行该方法。我们叫该方法为初始化回调方法(init callback)。组件页面通常在该方法中注册事件监听。例如
zkSuperButton = {} //declare zkSuperButton to hold init, cleanup and other methods
zkSuperButton.init = function (cmp) {
zk.listen(cmp, "focus", zkau.onfocus);
zk.listen(cmp, "blur", zkau.onblur);
};
初始化的顺序是从子组件开始的,然后是父组件,再到父组件的父组件,依次类推。Init方法调用后,如果可能,引擎会调用zkSuperButton.beforeSize 和 zkSuperButton.onSize方法去初始化页面的大小。组件很少提供这两个方法,除非它们涉及布局,例如Hbox 、 Borderlayout组件。我们在下面的ZK Callbacks这节中会详细讲到它们。
清除(Cleanup)
当一个组件打算移除时,zk会检测cleanup 这个方法是否存在(例如在上面的例子中的zkSuperButton.cleanup方法)。如果存在,就调用该方法。移除的调用顺序先是顶级组件,然后是它的子组件,再是子组件的子组件,依次类推。
提示:你不需要在初始化回调方法(init callback)中解除你注册的事件监听,zk客户端引擎会自动解除它们。
事件处理(Event Handling)
Zk中存在两种事件处理的类型。一种类型是zk回调方法(ZK callbacks),象我们上一节提到的init和cleanup回调方法。另外一种是浏览器事件监听器(browser event listeners).
Zk回调方法(ZK Callbacks)
与浏览器事件监听器一样,Zk回调方法用来处理以下类似的某种情况,例如组件的父组件要调整大小,或者页面变成可视化。同浏览器事件监听器不同的是,这些方法是通过zk客户端引擎直接调用的,它们是通过约定俗称的名字来注册的。换句话说,一旦你声明了这个方法,它会自动被调用。
注意:zk会在调用init回调方法后扫描要注册的任何回调方法,因此你可在init回调方法中声明你要注册的回调方法。
zkType.init = function (cmp) {
zkType.onSize = function (cmp) {};
};
1、初始化回调方法(The init Callback)
zkType.init = function (cmp) {
};
一个组件附属于一个页面或因为无效而重画时会调用该方法。调用的顺序是首先调用父组件的init方法(类似java的构造方法)。没有父的组件的init方法首先被调用,然后是它的子组件的init方法,再是它的孙组件的init方法,依次类推。
2、清除回调方法 The cleanup Callback
zkType.cleanup = function (cmp) {
};
一个组件被销毁或因为无效而重画时会调用该方法。当服务器端的invalidate方法调用时,zk客户端引擎会首先调用cleanup这个回调方法,取代DOM 树,然后调用init回调方法。调用的顺序是首先是父组件的cleanup。顶端父亲的cleanup首先被调用,然后是它的子组件的cleanup方法,再是它的孙组件的cleanup方法,依次类推。
3、beforeSize和onSize回调方法(The beforeSize and onSize Callback1)
zkType.beforeSize = function (cmp) {
};
zkType.onSize = function (cmp) {
};
当浏览器窗口或可改变大小的父组件(象Borderlayout)重新改变大小时,zk客户端引擎会首先为那些受影响的组件调用beforeSize回调方法,然后再为它们调用onSize回调方法。换句话说,它是一个两层通过的机制。beforSize回调方法用来做一些准备。例如你可能在beforSize回调方法中设置一下style.width的属性,然后在onSize回调方法中设置正确的大小。该方法的调用顺序是父为先。顶端的受影响的父组件的方法先被调用,然后是它的子组件,再是它的孙组件,以此类推。
注意:
1. beforeSize 和 onSize 回调方法在初始化组件的时候也被调用, 因此你不需要也没必要在init回调函数中处理大小问题。
2. 当一个DOM元素的大小改变时,浏览器不会通知JavaScript 代码, 所以两个回调方法是在布局组件中调用zk.beforeSizeAt 和 zk.onSizeAt 方法时才被触发的。
4、onVisi 回调方法(The onVisi Callback)
zkType.onVisi = function (cmp) {
};
当一个组件变成可见时会调用onVisi右边的函数。调用的顺序是父为先。顶端变为可见的父组件的onVisi 先被调用,然后是它的子组件的onVisi 被调用,然后是孙组件的,以此类推。
注意:
1. 当一个 DOM元素变为可见或不可见时,浏览器并不通知JavaScript代码, 因此你直接设置display="none"该方法是不被自动调用的。换句话说,在服务器段只有可见性通过visibel属性设置被改变时,或者在客户端调用了zk.show或action.show方法,onVisi这个方法才会被zk客户端引擎自动调用。
5、onHide 回调方法(The onHide Callback)
zkType.onHide = function (cmp) {
};
当一个组件变成不可见时会调用onHide右边的方法。注意,当该方法调用时,组件仍然是可见的。调用的顺序是父为先。顶端变为不可见的父组件的onHide先被调用,然后是它的子组件的onHide被调用,然后是孙组件的,以此类推。
6、onScroll 回调方法(The onScroll Callback1)
zkType.onScroll = function (cmp) {
};
该方法在用户缩放浏览器窗口或该组件的父组件需要产生滚动条时被调用。调用的顺序是父为先。顶端受影响要产生滚动条的父组件的onScroll先被调用,然后是它的子组件的onScroll被调用,然后是孙组件的,以此类推。
注意:
1.当用户(而不是浏览器窗口)调整使一个DOM元素产生滚动条时, 浏览器并不通知JavaScript代码。该方法的调用取决于组件的页面是否调用了zk.onScrollAt 方法。
浏览器事件监听器(Browser Event Listeners)
象其他的JavaScript代码一样,你可以在代码中监听你关心的任何浏览器事件。但是zk提供了
了两个方法简化你的工作:zk.listen and zk.unlisten.它们不仅适合各种浏览器,而且可以自动调用unlisten,因此你无须担忧内存会泄漏。事实上,你很少去调用zk.unlisten 方法。
zk.listen(cmp, "click", function () {zkCkbox.onclick(cmp);});
与服务器端的交互(Communicate with the Server)
发送请求给服务器(Send Requests to the Server)
在客户端,为了通报发生的事情,展现层需要发送一个AU request给服务器端。在服务器端,AU request作为一个org.zkoss.zk.au.AuRequest类的实例被封装。在客户端的javascrpit代码中, AU request是一个具有 uuid, cmd, data 和其他属性的对象。 例如
{uuid: cmp.id, cmd: "onClick", data: [10, 20], ctrl: true}
1、AU Request
下面列出了一个AU request可能有的属性:
属性 描述
uuid [可选则但必须是明确指定的uuid 和 dtid中的一个]
一个组件的UUID必须接收该请求,同时它必须被目标组件所识别。
dtid [可选则但必须是明确指定的uuid 和 dtid中的一个]
桌面 ID 必须接受该请求。
cmd [必须的]
它表示命令的ID,比如onClick和 onSelect等。该属性会命令中被寻找。Zk更新引擎首先会调用目标组件的getCommand方法去寻找指定组件具体的命令。如果没找到,它会调用AuRequest的静态方法去寻找全局命令。
data [可选; 默认值: null]
传送到服务器的data数组。它的长度和内容取决于发送的命令。例如onClick命令接受一个长度为2的数组元素来代表鼠标的位置。
ctl [可选; 默认值: false]
当请求是一个象onClick, onOK等控制命令时, 为了避免前一个执行在长时间的操作中由于用户重复点击而执行两次。zk会在第一次点击执行的时候忽略对相同组件的第二次点击。如果没有特殊需求,不要将它指明为true。
If any doubt, don't specify it (with true).
ignorable [可选, 默认值: false]
当系统在忙或导致错误时,通过设置该属性发送的请求会被忽略。典型的例子就是onChanging 和 onScrolling命令。如果不将ignorable设置为true,频繁的发送可能会很烦。
2、发送请求的基本的应用方法(Basic Utilities to Send Requests)
zkau.sendasap 和 zkau.send 是两个最常见的向服务器端发送AU request的命令方法。
zkau.send(evt, timeout)
该方法是在指定的毫秒结束后向服务器端发送一个AU request请求。如果它前面有未解决的AU request请求,那么该请求会添加到未解决的请求后面。
Evt
该参数是发送的AU request请求.
Timeout
发送请求延迟的时间(单位:毫秒)。如果不指定,它的延迟时间默认是0毫秒(即调用setTimeout方法设置值为0)。如果指定一个负数,它表明这是一个可延迟的命令。只有其他非延迟的zkau.send方法调用完毕后,该方法才会发送反馈到服务器端。为了更好提高执行效率,最好将允许延迟的事件监听都指定为-1。通过zkau.asapTimeout方法很容易地做到,代码如下:
zkau.send({uuid: cmp.id, cmd: "onChange", data: [cmp.value]},
zkau.asapTimeout(cmp, "onChange"));
zkau.sendasap(evt, timeout)
如果存在不可延迟的事件监听注册了该命令,在发送AU request请求的时候会自动检测。下面是上面代码的简写:
zkau.send(evt, zkau.asapTimeout(evt.uuid, evt.cmd, timeout));
evt
该参数是发送的AU request请求.
timeout
发送请求延迟的时间(单位:毫秒)。注意这个参数只适用于注册的没有延迟的事件监听器。
zkau.sendAhead(evt, timeout)
发送一个AU request请求,该请求的优先级排在其他未解决的AU requests前面。
evt
该参数是发送的AU request请求.
timeout
发送请求延迟的时间(单位:毫秒)。如果不指定,它的延迟时间默认是0毫秒(即调用setTimeout方法设置值为0)。如果指定一个负数,它表明这是一个可延迟的命令。只有其他非延迟的zkau.send方法调用完毕后,该方法才会发送反馈到服务器端。
3、回调函数 Callbacks
存在两个回调函数,在向服务器端发送Au request之前和在接受服务端resposnes后,你可以通过向这两个函数注册事件去做某些处理。
zkau.addOnSend(func);
zkau.removeOnSend(func);
注册的回调函数(上面方法中的参数是个func)会在AU request请求发送给给服务器前被调用。如果你用计时器或其他的操作监控DOM元素的值,采用这两个方法是很有用的。FCKeditor组件就是一个典型的例子。通过应用onSend回调方法,我们可以重复检测值,发现其他AU request改变值时,会及时反馈正确的值。
zkau.addOnSend(function() {
//check any change and call zkau.sendAhead to carry the change back
});
zkau.addOnResponse(script)
在客户端处理完服务器端发送的回应后,该注册方法会被调用。
script
该参数可以是function或javascript代码的字符串。
4、更多的发送请求的方法(More Utilities to Send Requests)
这里列出了一系列的针对特殊的AU request请求发送的方法,象onClose等。它们都是基于zkau.send方法的。
zkau.sendRemove(uuid)
发送 remove 命令给服务器移除指定的组件。
zkau.sendOnMove
发送 onMove 命令表明一个组件被移动。
zkau.sendOnSize
发送 onSize 命令表明一个组件的大小被改变.
zkau.sendOnZIndex
发送 onZIndex 命令表明一个组件的Z-index被改变.
zkau.sendOnClose
发送onClose 命令,该命令使onClose事件发送到组件。onClose事件如何处理依赖于组件本身,例如window组件,它的效果就是销毁该组件。
服务器端处理请求(Process Request at the Server)
当一个AU request请求到达服务器端,通过与命令(org.zkoss.zk.au.Command)关联,它被封装成一个org.zkoss.zk.au.AuRequest类的实例。命令会相应地处理相关联的请求。ZK 更新引擎只考虑去调用命令中的process方法。对于组件开发者来说,process方法被调用时,这个命令可能会更新组件的状态,或发送一个事件或其他事件。
1、全局的或组件指定的命令(Global and Component-specific Commands)
存在两种命令:全局命令和组件指定的命令。全局命令应用于所有的组件,而组件指定的命令只应用于某种特殊类型的组件。更确切地说,全局命令是在整个全局的地域中进行维护的,而组件指定的命令只在单个的组件中维护。
如何鉴别一个命令 (How to Identify a Command)
Zk引擎为AU request请求分两步寻找命令:
1. Zk引擎首先通过发送的AU request请求的命令ID检测是否有组件特定的命令被定义。它是通过调用ComponentCtrl接口中的getCommand方法来完成的。
2. 然后, Zk引擎通过发送的AU request请求的命令ID检测是否有全局的该命令被定义。如果没发现组件特定的命令被定义,它就通过调用AuRequest类的静态getCommand方法来完成的。
内置的命令(Built-in Commands)
在AU responses中存在许多的内置命令,象InputCommand, MouseCommand,KeyCommand等等,它们都在org.zkoss.zk.au.in这个包中。这些内置命令都是全局的。换句话说,它们应用于所有的组件。为了调高组件开发的效率,尽可能应用内嵌的命令。
2、开发自己的命令 (Develop Your Own Command)
如果内嵌的命令不能满足你的要求,你可以开发自己的命令。
开发自己的组件指定的命令 (Develop Your Own Component-specific Command)
首先,要决定你开发的命令是否能用于两类或更多类的组件中。如果该命令只应用于某种特殊的组件,你应该开发一个组件指定的命令:
1、 继承ComponentCommand类,实现process方法。
2、 当组件的getCommand调用时返回命令。
例如,
public class SuperButton extends AbstractComponent {
private static Command _flycmd = new ComponentCommand("onFly", 0) {
protected void process(AuRequest request) {
final SuperButton btn = (SuperButton)request.getComponent();
btn.doFly();
Events.postEvent(new Event(getId(), btn, request.getData()));
}
}; //note: it won't register itself to the global map
//because it is extended from ComponentCommand)
public Command getCommand(String cmdId) {
return "fly".equals(cmdId) ? _flycmd: null;
}
void doFly() {
//whatever
}
}
开发自己的全局命令(Develop Your Own Global Command)
如果要求命令能应用于两种或更多种其他的组件,你可能要考虑应用一个全局的命令。因为该命令用于全局,所以要保证它的名字和别的命令不发生冲突。
定义一个全局的命令需要继承org.zkoss.zk.au.Command类。
class FlyCommand extends Command {
public FlyCommand(String id, int flags) {
super(id, flags);
}
protected process(AuRequest request) {
final SuperButton btn = (SuperButton)request.getComponent();
btn.doFly();
Events.postEvent(new Event(getId(), btn, request.getData()));
}
}
因为它是全局的,你在启用的时候必须实例化一个该类的实例。典型的应用方法是在类中声明一个静态的实例。例如,
public class SuperButton extends AbstractComponent {
static {
new FlyCommand("onFly", 0); //register itself automatically
}
...
有趣的是你不需要去参考其他的全局命令是如何调用的。它在实例化的时候就已经自动地被注册了。另外注意的是你必须在要加载的类中实例化你的命令---- 组件类只要在lang.xml 或 lang-addon.xml文件中被注册,它们总是被加载的。
getExtraCtrl 方法(The getExtraCtrl Method)
当执行一个命令的时候,你可以调用你想调用的任何方法。但是,zk提供了一个特殊的方法将这些实现经行了封装,使得这些方法对应用开发者来说是不可见的。这个方法就是getExtraCtrl(在ComponentCtrl接口中)
public Object getExtraCtrl();
注意:你不需要象它这样做。 首先,它只是我们用来执行组件的一种简单的设计模式。其次.如果你只用 组件特定的命令,命令涉及的方法都已经封装的很好了。
下面是我们如何应用它。让我们想象一下,你不想让应用程序访问doFly这个命令。最简单的方法是我们将它声明为private 或protected 类型,然后把它当作组件特定的命令进行执行。就像我们在自定义自己的组件特定命令(develop Your Own ComponentspecificCommand )一节中做的那样。但是,如果FlyCommand想成为一个全局的命令如何实现呢。
首先,你设计一个接口,叫Flyable
public interface Flyable {
public void doFly();
}
然后在组件的实现中,我们这样做:
public class SuperButton extends AbstractComponent {
public Object newExtraCtrl() {
return new ExtraCtrl();
}
private void doFly() { //make it inaccessibl
//whatever
}
private class ExtraCtrl implements Flyable {
public void doFly() {
SuperButto.this.doFly();
}
}
...
在这里我们是重写newExtraCtrl方法,而不是getExtraCtrl方法。因为getExtraCtrl用来处理处理extra control的生命周期,并调用newExtraCtrl方法实例化对象。最后你可以采用下面的方式实现FlyCommand:
class FlyCommand extends Command {
public FlyCommand(String id, int flags) {
super(id, flags);
}
protected process(AuRequest request) {
final Component comp = request.getComponent();
if (comp instanceof Flyable)
((Flyable)comp).doFly();
Events.postEvent(new Event(getId(), comp, request.getData()));
}
}
4、处理灵活更新(Process Smart Updates)
处理从服务器端发回的灵活更新的方法很直接,只需在客户端的js代码中声明一个叫setAttr的回调方法。例如,组件的type属性为SuperButton,那么它的setAttr回调方法的写法是这个样子的:
zkSuperButton.setAttr = function (cmp, name, value) {
if ("fly" == name) {
//whatever
}
return false;
}
返回的值代表你是否想让默认的行为发生,即是否想更新相应的属性。更确切地说,当返回值为false的时候zkau.setAttr(cmp, name, value)将被调用。如果你喜欢先更新属性,你可以手动地调用zkau.setAttr方法。例如:
zkSuperButton.setAttr = function (cmp, name, value) {
if ("disabled" == name) {
zkau.setAttr(cmp, name, value); //update cmp.disabled first
//whatever
return true; //attribute has been updated
}
return false;
}
带有多个值的灵活更新(Smart Updates with Multiple Values)
当执行一个比较复杂的组件时,你可能要传多个值(即,一个存值的数组)给某个属性。
在服务器端,你调用 smartUpdateValues()方法。在客户端,带参数的setAttr 回调方法将被调用,例如,在服务器端,你调用:
smartUpdateValues("fly", new String[] {"low", "fast"});
然后,在客户端,你相应的处理是这样的:
zkSuperButton.setAttr = function (cmp, name, value1, value2) {
if ("fly" == name) {
fly(value1, value2);
...
JavaScript提示:你可以应用arguments.length来决定有多少参数被传递。
5、处理Au response回应(Process AU Responses)
在org.zkoss.zk.au.out包中,内嵌了好多你可以用的response回应。而invoke (AuInvoke) 和 script (AuScript) 可能是你主要用到的。
Invoke回应(The invoke Response)
Invoke回应用来调用回调方法。例如,在服务器端你可以发送如下的一个AU response
public void fly() {
response("ctrl", new AuInvoke(this, "fly", "low", "fast"));
}
然后,在客户端,相应的回调方法会被调用
zkSuperButton.fly = function (cmp, height, speed) {
//whatever
}
建议:正如上面显示的,我们可以用AU response和smart update做相同的任务(例如例子中的fly)。但是,它们两个是不同的:如果组件是无效时,AU response发送的是事件,而smart update不是。因此,如果init回调函数处理所有的事情,那smart update应该被使用。(因此,当组件失效时,我们将做2次)
脚本回复 (The script Response)
脚本回复应用执行客户端的任何JavaScript代码,不仅仅是回调函数。例如:
public void fly() {
response("ctrl",
new AuScript(this, "zkSuperButton.fly('" + getUuid() + "','low','fast')"));
}
因为JavaScript代码是硬编码,在有些时候,它不能很容易地支复合模式。例如,假设你想用相同的java类去执行另外的模式。那么,JavaScript代码实现的fly可能会不同。因此你可能必须这样做:
public void fly() {
response("ctrl",
new AuScript(this,
("SuperButton".equals(getMold()) ? "zkSuperButton": "zkGiantButton")
+ ".fly('" + getUuid() + "','low','fast')"));
}
另一方面,如果你用invoke回复方法,你可能有相同的回复但却定义了不同的组件类型。例如 GiantButton. 虽然你不需要修改Java 类.但必须提供另外一个回调函数。
zkGiantButton.fly = function (cmp, height, speed) {
//whatever
}
一般情况下,能用invoke方法的推荐最好用invoke方法。
定义自己的AU Responses回复(Develop Your Own AU Responses)
不像AU request的命令一样,你很少需要开发自己的AU response.因为invoke response可以提供几乎所有的服务功能。但是,如果你喜欢提供一个你自己的回复,你可以看一看下面一节的设施描述。
首先,你必须实现一个java类,该类继承于org.zkoss.zk.au.AuResponse类。
public AuFly extends AuResponse {
public AuFly(SuperButton comp, String height, String speed) {
super("fly", comp, new String[] {comp.getUuid(), height, speed});
}
}
在这里,“fly”是回复(Response)的名字,并且这个回复(Response)的构造函数中的第三个参数(String[])将传送到客户端。回复(Response)的名字必须是唯一的,第三个参数叫做回复数据。然后,在客户端,你可以提供一个叫zkau.cmd1.fly的方法处理它。
zkau.cmd1.fly = function (uuid, cmp, height, speed) {
//whatever
}
存在两种集合的回复(Response)处理器:zkau.cmd0 和 zkau.cmd1。其中 zkau.cmd0用来处理那些应用于整个浏览器窗口的responses,而zkau.cmd1用于处理个体组件。
如果zkau.cmd1被重写,回复数据(response data)的第一个元素必须为组件的UUID(在上面的例子中是comp.getUuid())。然后,zk客户端引擎会将它转化为组件的引用,接着调用含该UUID和引用(在上面的例子中指的是前两个参数,请参考上面例子的zkau.cmd.fly)的回复处理器
在客户端,展现层是一个组件的可视化部分。根据客户端的不同,它可以是HTML标记加JavaScript代码,MIDLet,XML节点和flash。出于描述的考虑,我们只讨论Ajax浏览器中的展现。即HTML标记语言加JavaScript代码。
3.1 HTML标记(HTML tags)
你可以选择你喜欢的任何Servlet技术来生成HTML标记。Zk(更确切的说是AbstractComponent)利用javax.servlet.RequestDispatcher类中的include方法去包含为展现层生成的HTML标记。
出于描述的考虑,我们在大多数的例子中采用了DSP文件(动态服务页面)。DSP是一个由zk自己开发的模板技术。其用法除了不能在代码中内嵌java代码外和jsp相似。DSP文件有诸多的优点,例如,它不需要编译,可以是jar文件的一部分,没有jsp/EL的要求等等。
3.1.1渲染中获取组件(Retrieve Component Being Rendered)
当展现层被渲染时,相关的信息是通过一个叫arg的请求属性来传递的。Arg是map类的一个实例,在arg中,被渲染的组件存储在一个叫self的实体中。因此你可以用下面的EL表达式来获取它:
${requestScope.arg.self}
你也可采用如下的java代码重新获取它:
void doGet(HttpServletRequest request, HttpServletResponse response) {
Component self = (Component)((Map)request.getAttribute("arg")).get("self");
下面是org.zkoss.zul.Image展现层的例子(用DSP展现的):
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<img id="${self.uuid}"z.type="zul.widget.Img"${self.outerAttrs}${self.innerAttrs}/>
● 你需要通过组件的UUID来生成id这个属性
● 通过调用HtmlBasedComponent中的getOuterAttrs 和 getInnerAttrs方法生成需要的各个属性。
● 如果展现层需要某段具体的JavaScript代码,z.type属性需要通过指定一个唯一的值来表示组件的类型
从上面的img例子中,大家注意到每个展现层必须有一个顶端的HTML元素。下面的展现层的例子是不正确的。因为它有两个顶端元素。
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<img id="${self.uuid}" ${self.outerAttrs}${self.innerAttrs}/>
<img/>
要使它正确,你需要用span 或div标签包含它们。
<span id="${self.uuid}" ${self.outerAttrs}${self.innerAttrs}/>
<img/>
<img/>
</span>
DSP文件的位置(DSP File Location)
根据自己的要求,你可以把DSP文件放到web应用中或jar包类库中来表示展现层。例如你要将DSP文件放到web应用中,如路径为/WEB-INF/myaddon/button.dsp,只需在下面的mold-uri元素中指明正确的路径就可以了。
<component>
<mold>
<mold-name>default</mold-name>
<mold-uri>/WEB-INF/myaddon/button.dsp</mold-uri>
</mold>
如果你想将文件放到jar文件中,你必须把它放到/web目录下,例如/web/myaddon/button.dsp是一个合法的路径,在mold URI中的配置如下:
<component>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./myaddon/button.dsp</mold-uri>
</mold>
这里的"~."在jar文件中表示的是/web目录
组件渲染器(Component Renderer)
DSP是一种设计组件界面的简单方式,但文件中EL表达式的执行效率比纯的java代码慢的多。为了最大体现java代码的执行效率,你可以开发一个Servlet或所谓的组件渲染器来代替DSP文件的这种方式。
组件渲染器是一个直接渲染产生页面的java类。它必须继承和实现org.zkoss.zk.ui.render.ComponentRenderer 接口, 该接口只有一个方法:render.例如,下面是Image渲染的java代码:
public class ImageDefault implements ComponentRenderer {
public void render(Component comp, Writer out) throws IOException {
final Image self = (Image)comp;
out.write("<img id=\"");
out.write(self.getUuid());
out.write("\" z.type=\"zul.widget.Img\"");
out.write(self.getOuterAttrs());
out.write(self.getInnerAttrs());
out.write("/>");
}
}
然后,在mold URI的配置中必须以"class:"开头,以类的名字结束,实例如下:
<component>
<mold>
<mold-name>default</mold-name>
<mold-uri>class:org.zkoss.zkmax.zul.render.ImageDefault</mold-uri>
</mold>
JavaScript 代码
除了可视化的展现外,一个组件的页面通常会在客户端有运行的代码,比如说初始化页面和用户进行交互。对于AJAX驱动程序来说,客户端的代码是用JavaScript来写的。出于方便,我们在这里只讨论AJAX驱动程序
组件类型(Component Type)
在客户端要使JavaScript代码能够运行,每个组件必须在z.type属性中提供一个唯一的类型,例如
<BUTTON id="z_ed_0" z.type="mycomps.MyButton">I am a button</BUTTON>
在语法dir1.dir2.file.Type中,最后一个点后面的部分是类型的名字,前面的部分是处理页面的JavaScript文件加载的路径。Zk利用类型作为名字入口去调用JS文件中正确的JavaScript方法。例如,假设类型是Tp,那么,如果zkTp.init方法在JavaScript文件中被声明,当zk在客户端初始化组件的时候,这个方法就会被调用。
zkTp = {}
zkTp.init = function (cmp) {
zk.listen(cmp, "onClick", zkTp.onclick);
}
zkTp.onclick = function (evt) {
var el = Event.element(evt);
...
};
JavaScript 文件的位置(JavaScript File Location)
正如上一节所描述的那样,z.type属性的第一部分是JavaScript的加载路径。例如,假设z.type属性是a.b.c.Tx,那么zk会在初始化页面前到路径/web/js/a/b/c.js中正确加载文件。注意,文件的路径是相对类的路径存储的,并且它总是以/web/js开头的。
初始化和清除(Initialization and Cleanup)
初始化(Initialization)
当zk客户端引擎初始化一个组件的页面时,如果页面的z.type属性被定义,引擎会首先检查该属性。如果该属性未指定,这意味着该页面不需要JavaScript代码,引擎会结束这个组件的初始化。
如果 z.type属性被指定,比如说是 widget.SuperButton, ZK 客户端引擎会首先/web/js/widget.js这个javascript文件, 然后检查是否有叫zkSuperButton.init的方法. 如果发现该方法,就执行该方法。我们叫该方法为初始化回调方法(init callback)。组件页面通常在该方法中注册事件监听。例如
zkSuperButton = {} //declare zkSuperButton to hold init, cleanup and other methods
zkSuperButton.init = function (cmp) {
zk.listen(cmp, "focus", zkau.onfocus);
zk.listen(cmp, "blur", zkau.onblur);
};
初始化的顺序是从子组件开始的,然后是父组件,再到父组件的父组件,依次类推。Init方法调用后,如果可能,引擎会调用zkSuperButton.beforeSize 和 zkSuperButton.onSize方法去初始化页面的大小。组件很少提供这两个方法,除非它们涉及布局,例如Hbox 、 Borderlayout组件。我们在下面的ZK Callbacks这节中会详细讲到它们。
清除(Cleanup)
当一个组件打算移除时,zk会检测cleanup 这个方法是否存在(例如在上面的例子中的zkSuperButton.cleanup方法)。如果存在,就调用该方法。移除的调用顺序先是顶级组件,然后是它的子组件,再是子组件的子组件,依次类推。
提示:你不需要在初始化回调方法(init callback)中解除你注册的事件监听,zk客户端引擎会自动解除它们。
事件处理(Event Handling)
Zk中存在两种事件处理的类型。一种类型是zk回调方法(ZK callbacks),象我们上一节提到的init和cleanup回调方法。另外一种是浏览器事件监听器(browser event listeners).
Zk回调方法(ZK Callbacks)
与浏览器事件监听器一样,Zk回调方法用来处理以下类似的某种情况,例如组件的父组件要调整大小,或者页面变成可视化。同浏览器事件监听器不同的是,这些方法是通过zk客户端引擎直接调用的,它们是通过约定俗称的名字来注册的。换句话说,一旦你声明了这个方法,它会自动被调用。
注意:zk会在调用init回调方法后扫描要注册的任何回调方法,因此你可在init回调方法中声明你要注册的回调方法。
zkType.init = function (cmp) {
zkType.onSize = function (cmp) {};
};
1、初始化回调方法(The init Callback)
zkType.init = function (cmp) {
};
一个组件附属于一个页面或因为无效而重画时会调用该方法。调用的顺序是首先调用父组件的init方法(类似java的构造方法)。没有父的组件的init方法首先被调用,然后是它的子组件的init方法,再是它的孙组件的init方法,依次类推。
2、清除回调方法 The cleanup Callback
zkType.cleanup = function (cmp) {
};
一个组件被销毁或因为无效而重画时会调用该方法。当服务器端的invalidate方法调用时,zk客户端引擎会首先调用cleanup这个回调方法,取代DOM 树,然后调用init回调方法。调用的顺序是首先是父组件的cleanup。顶端父亲的cleanup首先被调用,然后是它的子组件的cleanup方法,再是它的孙组件的cleanup方法,依次类推。
3、beforeSize和onSize回调方法(The beforeSize and onSize Callback1)
zkType.beforeSize = function (cmp) {
};
zkType.onSize = function (cmp) {
};
当浏览器窗口或可改变大小的父组件(象Borderlayout)重新改变大小时,zk客户端引擎会首先为那些受影响的组件调用beforeSize回调方法,然后再为它们调用onSize回调方法。换句话说,它是一个两层通过的机制。beforSize回调方法用来做一些准备。例如你可能在beforSize回调方法中设置一下style.width的属性,然后在onSize回调方法中设置正确的大小。该方法的调用顺序是父为先。顶端的受影响的父组件的方法先被调用,然后是它的子组件,再是它的孙组件,以此类推。
注意:
1. beforeSize 和 onSize 回调方法在初始化组件的时候也被调用, 因此你不需要也没必要在init回调函数中处理大小问题。
2. 当一个DOM元素的大小改变时,浏览器不会通知JavaScript 代码, 所以两个回调方法是在布局组件中调用zk.beforeSizeAt 和 zk.onSizeAt 方法时才被触发的。
4、onVisi 回调方法(The onVisi Callback)
zkType.onVisi = function (cmp) {
};
当一个组件变成可见时会调用onVisi右边的函数。调用的顺序是父为先。顶端变为可见的父组件的onVisi 先被调用,然后是它的子组件的onVisi 被调用,然后是孙组件的,以此类推。
注意:
1. 当一个 DOM元素变为可见或不可见时,浏览器并不通知JavaScript代码, 因此你直接设置display="none"该方法是不被自动调用的。换句话说,在服务器段只有可见性通过visibel属性设置被改变时,或者在客户端调用了zk.show或action.show方法,onVisi这个方法才会被zk客户端引擎自动调用。
5、onHide 回调方法(The onHide Callback)
zkType.onHide = function (cmp) {
};
当一个组件变成不可见时会调用onHide右边的方法。注意,当该方法调用时,组件仍然是可见的。调用的顺序是父为先。顶端变为不可见的父组件的onHide先被调用,然后是它的子组件的onHide被调用,然后是孙组件的,以此类推。
6、onScroll 回调方法(The onScroll Callback1)
zkType.onScroll = function (cmp) {
};
该方法在用户缩放浏览器窗口或该组件的父组件需要产生滚动条时被调用。调用的顺序是父为先。顶端受影响要产生滚动条的父组件的onScroll先被调用,然后是它的子组件的onScroll被调用,然后是孙组件的,以此类推。
注意:
1.当用户(而不是浏览器窗口)调整使一个DOM元素产生滚动条时, 浏览器并不通知JavaScript代码。该方法的调用取决于组件的页面是否调用了zk.onScrollAt 方法。
浏览器事件监听器(Browser Event Listeners)
象其他的JavaScript代码一样,你可以在代码中监听你关心的任何浏览器事件。但是zk提供了
了两个方法简化你的工作:zk.listen and zk.unlisten.它们不仅适合各种浏览器,而且可以自动调用unlisten,因此你无须担忧内存会泄漏。事实上,你很少去调用zk.unlisten 方法。
zk.listen(cmp, "click", function () {zkCkbox.onclick(cmp);});
与服务器端的交互(Communicate with the Server)
发送请求给服务器(Send Requests to the Server)
在客户端,为了通报发生的事情,展现层需要发送一个AU request给服务器端。在服务器端,AU request作为一个org.zkoss.zk.au.AuRequest类的实例被封装。在客户端的javascrpit代码中, AU request是一个具有 uuid, cmd, data 和其他属性的对象。 例如
{uuid: cmp.id, cmd: "onClick", data: [10, 20], ctrl: true}
1、AU Request
下面列出了一个AU request可能有的属性:
属性 描述
uuid [可选则但必须是明确指定的uuid 和 dtid中的一个]
一个组件的UUID必须接收该请求,同时它必须被目标组件所识别。
dtid [可选则但必须是明确指定的uuid 和 dtid中的一个]
桌面 ID 必须接受该请求。
cmd [必须的]
它表示命令的ID,比如onClick和 onSelect等。该属性会命令中被寻找。Zk更新引擎首先会调用目标组件的getCommand方法去寻找指定组件具体的命令。如果没找到,它会调用AuRequest的静态方法去寻找全局命令。
data [可选; 默认值: null]
传送到服务器的data数组。它的长度和内容取决于发送的命令。例如onClick命令接受一个长度为2的数组元素来代表鼠标的位置。
ctl [可选; 默认值: false]
当请求是一个象onClick, onOK等控制命令时, 为了避免前一个执行在长时间的操作中由于用户重复点击而执行两次。zk会在第一次点击执行的时候忽略对相同组件的第二次点击。如果没有特殊需求,不要将它指明为true。
If any doubt, don't specify it (with true).
ignorable [可选, 默认值: false]
当系统在忙或导致错误时,通过设置该属性发送的请求会被忽略。典型的例子就是onChanging 和 onScrolling命令。如果不将ignorable设置为true,频繁的发送可能会很烦。
2、发送请求的基本的应用方法(Basic Utilities to Send Requests)
zkau.sendasap 和 zkau.send 是两个最常见的向服务器端发送AU request的命令方法。
zkau.send(evt, timeout)
该方法是在指定的毫秒结束后向服务器端发送一个AU request请求。如果它前面有未解决的AU request请求,那么该请求会添加到未解决的请求后面。
Evt
该参数是发送的AU request请求.
Timeout
发送请求延迟的时间(单位:毫秒)。如果不指定,它的延迟时间默认是0毫秒(即调用setTimeout方法设置值为0)。如果指定一个负数,它表明这是一个可延迟的命令。只有其他非延迟的zkau.send方法调用完毕后,该方法才会发送反馈到服务器端。为了更好提高执行效率,最好将允许延迟的事件监听都指定为-1。通过zkau.asapTimeout方法很容易地做到,代码如下:
zkau.send({uuid: cmp.id, cmd: "onChange", data: [cmp.value]},
zkau.asapTimeout(cmp, "onChange"));
zkau.sendasap(evt, timeout)
如果存在不可延迟的事件监听注册了该命令,在发送AU request请求的时候会自动检测。下面是上面代码的简写:
zkau.send(evt, zkau.asapTimeout(evt.uuid, evt.cmd, timeout));
evt
该参数是发送的AU request请求.
timeout
发送请求延迟的时间(单位:毫秒)。注意这个参数只适用于注册的没有延迟的事件监听器。
zkau.sendAhead(evt, timeout)
发送一个AU request请求,该请求的优先级排在其他未解决的AU requests前面。
evt
该参数是发送的AU request请求.
timeout
发送请求延迟的时间(单位:毫秒)。如果不指定,它的延迟时间默认是0毫秒(即调用setTimeout方法设置值为0)。如果指定一个负数,它表明这是一个可延迟的命令。只有其他非延迟的zkau.send方法调用完毕后,该方法才会发送反馈到服务器端。
3、回调函数 Callbacks
存在两个回调函数,在向服务器端发送Au request之前和在接受服务端resposnes后,你可以通过向这两个函数注册事件去做某些处理。
zkau.addOnSend(func);
zkau.removeOnSend(func);
注册的回调函数(上面方法中的参数是个func)会在AU request请求发送给给服务器前被调用。如果你用计时器或其他的操作监控DOM元素的值,采用这两个方法是很有用的。FCKeditor组件就是一个典型的例子。通过应用onSend回调方法,我们可以重复检测值,发现其他AU request改变值时,会及时反馈正确的值。
zkau.addOnSend(function() {
//check any change and call zkau.sendAhead to carry the change back
});
zkau.addOnResponse(script)
在客户端处理完服务器端发送的回应后,该注册方法会被调用。
script
该参数可以是function或javascript代码的字符串。
4、更多的发送请求的方法(More Utilities to Send Requests)
这里列出了一系列的针对特殊的AU request请求发送的方法,象onClose等。它们都是基于zkau.send方法的。
zkau.sendRemove(uuid)
发送 remove 命令给服务器移除指定的组件。
zkau.sendOnMove
发送 onMove 命令表明一个组件被移动。
zkau.sendOnSize
发送 onSize 命令表明一个组件的大小被改变.
zkau.sendOnZIndex
发送 onZIndex 命令表明一个组件的Z-index被改变.
zkau.sendOnClose
发送onClose 命令,该命令使onClose事件发送到组件。onClose事件如何处理依赖于组件本身,例如window组件,它的效果就是销毁该组件。
服务器端处理请求(Process Request at the Server)
当一个AU request请求到达服务器端,通过与命令(org.zkoss.zk.au.Command)关联,它被封装成一个org.zkoss.zk.au.AuRequest类的实例。命令会相应地处理相关联的请求。ZK 更新引擎只考虑去调用命令中的process方法。对于组件开发者来说,process方法被调用时,这个命令可能会更新组件的状态,或发送一个事件或其他事件。
1、全局的或组件指定的命令(Global and Component-specific Commands)
存在两种命令:全局命令和组件指定的命令。全局命令应用于所有的组件,而组件指定的命令只应用于某种特殊类型的组件。更确切地说,全局命令是在整个全局的地域中进行维护的,而组件指定的命令只在单个的组件中维护。
如何鉴别一个命令 (How to Identify a Command)
Zk引擎为AU request请求分两步寻找命令:
1. Zk引擎首先通过发送的AU request请求的命令ID检测是否有组件特定的命令被定义。它是通过调用ComponentCtrl接口中的getCommand方法来完成的。
2. 然后, Zk引擎通过发送的AU request请求的命令ID检测是否有全局的该命令被定义。如果没发现组件特定的命令被定义,它就通过调用AuRequest类的静态getCommand方法来完成的。
内置的命令(Built-in Commands)
在AU responses中存在许多的内置命令,象InputCommand, MouseCommand,KeyCommand等等,它们都在org.zkoss.zk.au.in这个包中。这些内置命令都是全局的。换句话说,它们应用于所有的组件。为了调高组件开发的效率,尽可能应用内嵌的命令。
2、开发自己的命令 (Develop Your Own Command)
如果内嵌的命令不能满足你的要求,你可以开发自己的命令。
开发自己的组件指定的命令 (Develop Your Own Component-specific Command)
首先,要决定你开发的命令是否能用于两类或更多类的组件中。如果该命令只应用于某种特殊的组件,你应该开发一个组件指定的命令:
1、 继承ComponentCommand类,实现process方法。
2、 当组件的getCommand调用时返回命令。
例如,
public class SuperButton extends AbstractComponent {
private static Command _flycmd = new ComponentCommand("onFly", 0) {
protected void process(AuRequest request) {
final SuperButton btn = (SuperButton)request.getComponent();
btn.doFly();
Events.postEvent(new Event(getId(), btn, request.getData()));
}
}; //note: it won't register itself to the global map
//because it is extended from ComponentCommand)
public Command getCommand(String cmdId) {
return "fly".equals(cmdId) ? _flycmd: null;
}
void doFly() {
//whatever
}
}
开发自己的全局命令(Develop Your Own Global Command)
如果要求命令能应用于两种或更多种其他的组件,你可能要考虑应用一个全局的命令。因为该命令用于全局,所以要保证它的名字和别的命令不发生冲突。
定义一个全局的命令需要继承org.zkoss.zk.au.Command类。
class FlyCommand extends Command {
public FlyCommand(String id, int flags) {
super(id, flags);
}
protected process(AuRequest request) {
final SuperButton btn = (SuperButton)request.getComponent();
btn.doFly();
Events.postEvent(new Event(getId(), btn, request.getData()));
}
}
因为它是全局的,你在启用的时候必须实例化一个该类的实例。典型的应用方法是在类中声明一个静态的实例。例如,
public class SuperButton extends AbstractComponent {
static {
new FlyCommand("onFly", 0); //register itself automatically
}
...
有趣的是你不需要去参考其他的全局命令是如何调用的。它在实例化的时候就已经自动地被注册了。另外注意的是你必须在要加载的类中实例化你的命令---- 组件类只要在lang.xml 或 lang-addon.xml文件中被注册,它们总是被加载的。
getExtraCtrl 方法(The getExtraCtrl Method)
当执行一个命令的时候,你可以调用你想调用的任何方法。但是,zk提供了一个特殊的方法将这些实现经行了封装,使得这些方法对应用开发者来说是不可见的。这个方法就是getExtraCtrl(在ComponentCtrl接口中)
public Object getExtraCtrl();
注意:你不需要象它这样做。 首先,它只是我们用来执行组件的一种简单的设计模式。其次.如果你只用 组件特定的命令,命令涉及的方法都已经封装的很好了。
下面是我们如何应用它。让我们想象一下,你不想让应用程序访问doFly这个命令。最简单的方法是我们将它声明为private 或protected 类型,然后把它当作组件特定的命令进行执行。就像我们在自定义自己的组件特定命令(develop Your Own ComponentspecificCommand )一节中做的那样。但是,如果FlyCommand想成为一个全局的命令如何实现呢。
首先,你设计一个接口,叫Flyable
public interface Flyable {
public void doFly();
}
然后在组件的实现中,我们这样做:
public class SuperButton extends AbstractComponent {
public Object newExtraCtrl() {
return new ExtraCtrl();
}
private void doFly() { //make it inaccessibl
//whatever
}
private class ExtraCtrl implements Flyable {
public void doFly() {
SuperButto.this.doFly();
}
}
...
在这里我们是重写newExtraCtrl方法,而不是getExtraCtrl方法。因为getExtraCtrl用来处理处理extra control的生命周期,并调用newExtraCtrl方法实例化对象。最后你可以采用下面的方式实现FlyCommand:
class FlyCommand extends Command {
public FlyCommand(String id, int flags) {
super(id, flags);
}
protected process(AuRequest request) {
final Component comp = request.getComponent();
if (comp instanceof Flyable)
((Flyable)comp).doFly();
Events.postEvent(new Event(getId(), comp, request.getData()));
}
}
4、处理灵活更新(Process Smart Updates)
处理从服务器端发回的灵活更新的方法很直接,只需在客户端的js代码中声明一个叫setAttr的回调方法。例如,组件的type属性为SuperButton,那么它的setAttr回调方法的写法是这个样子的:
zkSuperButton.setAttr = function (cmp, name, value) {
if ("fly" == name) {
//whatever
}
return false;
}
返回的值代表你是否想让默认的行为发生,即是否想更新相应的属性。更确切地说,当返回值为false的时候zkau.setAttr(cmp, name, value)将被调用。如果你喜欢先更新属性,你可以手动地调用zkau.setAttr方法。例如:
zkSuperButton.setAttr = function (cmp, name, value) {
if ("disabled" == name) {
zkau.setAttr(cmp, name, value); //update cmp.disabled first
//whatever
return true; //attribute has been updated
}
return false;
}
带有多个值的灵活更新(Smart Updates with Multiple Values)
当执行一个比较复杂的组件时,你可能要传多个值(即,一个存值的数组)给某个属性。
在服务器端,你调用 smartUpdateValues()方法。在客户端,带参数的setAttr 回调方法将被调用,例如,在服务器端,你调用:
smartUpdateValues("fly", new String[] {"low", "fast"});
然后,在客户端,你相应的处理是这样的:
zkSuperButton.setAttr = function (cmp, name, value1, value2) {
if ("fly" == name) {
fly(value1, value2);
...
JavaScript提示:你可以应用arguments.length来决定有多少参数被传递。
5、处理Au response回应(Process AU Responses)
在org.zkoss.zk.au.out包中,内嵌了好多你可以用的response回应。而invoke (AuInvoke) 和 script (AuScript) 可能是你主要用到的。
Invoke回应(The invoke Response)
Invoke回应用来调用回调方法。例如,在服务器端你可以发送如下的一个AU response
public void fly() {
response("ctrl", new AuInvoke(this, "fly", "low", "fast"));
}
然后,在客户端,相应的回调方法会被调用
zkSuperButton.fly = function (cmp, height, speed) {
//whatever
}
建议:正如上面显示的,我们可以用AU response和smart update做相同的任务(例如例子中的fly)。但是,它们两个是不同的:如果组件是无效时,AU response发送的是事件,而smart update不是。因此,如果init回调函数处理所有的事情,那smart update应该被使用。(因此,当组件失效时,我们将做2次)
脚本回复 (The script Response)
脚本回复应用执行客户端的任何JavaScript代码,不仅仅是回调函数。例如:
public void fly() {
response("ctrl",
new AuScript(this, "zkSuperButton.fly('" + getUuid() + "','low','fast')"));
}
因为JavaScript代码是硬编码,在有些时候,它不能很容易地支复合模式。例如,假设你想用相同的java类去执行另外的模式。那么,JavaScript代码实现的fly可能会不同。因此你可能必须这样做:
public void fly() {
response("ctrl",
new AuScript(this,
("SuperButton".equals(getMold()) ? "zkSuperButton": "zkGiantButton")
+ ".fly('" + getUuid() + "','low','fast')"));
}
另一方面,如果你用invoke回复方法,你可能有相同的回复但却定义了不同的组件类型。例如 GiantButton. 虽然你不需要修改Java 类.但必须提供另外一个回调函数。
zkGiantButton.fly = function (cmp, height, speed) {
//whatever
}
一般情况下,能用invoke方法的推荐最好用invoke方法。
定义自己的AU Responses回复(Develop Your Own AU Responses)
不像AU request的命令一样,你很少需要开发自己的AU response.因为invoke response可以提供几乎所有的服务功能。但是,如果你喜欢提供一个你自己的回复,你可以看一看下面一节的设施描述。
首先,你必须实现一个java类,该类继承于org.zkoss.zk.au.AuResponse类。
public AuFly extends AuResponse {
public AuFly(SuperButton comp, String height, String speed) {
super("fly", comp, new String[] {comp.getUuid(), height, speed});
}
}
在这里,“fly”是回复(Response)的名字,并且这个回复(Response)的构造函数中的第三个参数(String[])将传送到客户端。回复(Response)的名字必须是唯一的,第三个参数叫做回复数据。然后,在客户端,你可以提供一个叫zkau.cmd1.fly的方法处理它。
zkau.cmd1.fly = function (uuid, cmp, height, speed) {
//whatever
}
存在两种集合的回复(Response)处理器:zkau.cmd0 和 zkau.cmd1。其中 zkau.cmd0用来处理那些应用于整个浏览器窗口的responses,而zkau.cmd1用于处理个体组件。
如果zkau.cmd1被重写,回复数据(response data)的第一个元素必须为组件的UUID(在上面的例子中是comp.getUuid())。然后,zk客户端引擎会将它转化为组件的引用,接着调用含该UUID和引用(在上面的例子中指的是前两个参数,请参考上面例子的zkau.cmd.fly)的回复处理器
发表评论
-
采用java.util.concurrent.Semaphore 类实现对资源限制访问量
2010-10-27 14:59 1953package com.citicbank.vipfund.w ... -
zk中message的封装
2009-06-14 16:23 1967最近,用zk的message老感觉填写东西很麻烦,就对它做了此 ... -
zk如何调用本地javascript代码向服务器端发送数据
2009-06-11 20:55 4846今天,折腾了一个上午,加下午1个小时,终于实现了zk页面中,如 ... -
使用zk的bandbox和listbox实现的一个具有搜索功能的combobox(实现后台分页)
2009-06-10 14:37 3947在图片附件中有效果图,可以先看一下. 先说下来由:因 ... -
试着用zk结合hibernaet实现分页查询
2009-06-10 14:14 2650原文:http://www.iteye.com/topic/2 ... -
根据sql查询实现zk Listbox翻页显示数据
2009-06-10 14:07 2403最近一直在研究如何封装zk的Listbox,实现简单的 ... -
zk中定义布局模版实现
2009-06-02 23:03 3095在项目的开发过程中,有些布局是固定的,为了布局的统一,通常会制 ... -
zk中动态产生多个组件的实现
2009-06-02 22:34 3478zk动态产生多个页面的例子代码: /** * * ... -
zk框架中常见问题的解决续
2009-06-02 21:02 30761、局部刷新 /** * 刷新页面* */ ... -
zk框架中常见问题的解决
2009-06-01 22:10 119111、打开浏览器窗口常用于超链接 /** * 重新打开 ... -
zk中zul页面中组件的id和java类中的变量绑定实现
2009-06-01 21:58 7101zul 页面 index.zul <?xml ver ... -
zk组件开发指南(2)
2009-06-01 20:52 43112. 句柄Handle 句柄是运行在服务器端可以被应 ... -
zk组件开发指南(1)
2009-06-01 20:09 37881.简介 ... -
zk组件开发指南(目录)
2009-06-01 19:40 2803声明:本人翻译水平有限,有误之处请多指教。 ...
相关推荐
在本文中,我们将深入探讨ZK组件开发指南的相关知识点,主要关注展现层的构建、DSP文件的使用以及组件渲染器的工作原理。 首先,展现层是客户端组件的可视化部分,它可以是HTML、JavaScript、MIDLet、XML节点或...
最后,"ZK-3.0.0 devguide.pdf"是ZK 3.0.0版本的开发指南,它提供了更深入的技术细节和最佳实践。这份文档不仅包含基础组件的用法,还涵盖了高级特性和定制化开发,例如自定义组件、国际化、权限控制、移动支持以及...
手册中提到了技术指南,这些指南帮助开发者了解如何使用ZK框架,并且强调了扩展性原则,指出ZK框架支持扩展,用户可以通过自己开发的组件和功能来增强框架的功能。 在UI组合方面,ZK框架提倡使用基于组件的用户界面...
### ZK开发指南知识点概述 #### 一、Overture(序言) ZK是一个高性能的Web应用程序框架,它使用Java语言编写,支持AJAX技术,能够帮助开发者快速构建丰富的交互式用户界面。ZK框架的核心优势在于其轻量级、易用性...
《ZK中文开发手册》是针对ZK框架的详尽指南,主要基于3.5.1版本。ZK是一款流行的Java开发的轻量级、基于组件的用户界面框架,专门用于构建富互联网应用程序(RIA)。它简化了客户端与服务器之间的交互,通过事件驱动...
### ZK开发指南知识点概述 #### 一、ZK框架概览 - **定义与特点**: - ZK是一款以AJAX/XUL/Java为基础的网页应用程序开发框架,其核心优势在于允许开发者以类似桌面应用程序的方式设计AJAX网络应用程序。 - **...
**ZK中文开发手册**是针对ZK框架的详尽指南,旨在帮助开发者更好地理解和使用这一强大的组件库和事件驱动的Java应用框架。ZK框架主要用于构建富客户端Web应用程序,它提供了一种简单的方式来创建交互式的用户界面,...
ZK框架以其独特的事件驱动模型和组件模型,使得在Web应用开发中实现桌面应用般的用户体验成为可能。 **ZK的主要特点** 1. **事件驱动模型**:ZK框架采用事件驱动模型,用户与页面交互时,ZK会自动处理对应的事件,...
在压缩包中,`setup.exe`是安装程序,用于在开发环境中部署ZK4500SDK的相关组件。`ZKFinger SDK_en.pdf`和`ZKFinger SDK_chs.pdf`是SDK的英文和中文用户手册,详尽地介绍了SDK的功能、接口使用方法以及开发过程中的...
除了基础功能外,手册可能还会涉及ZK的事件系统、国际化支持、自定义组件开发、AJAX技术应用、安全机制、性能优化等方面。对于Spring的整合,可能会涵盖如何配置Spring容器、如何注入ZK组件、以及如何使用Spring的...
ZK 8.0.0端参考 ZK 8.0.0组件开发的必需品 ZK 8.0.0组件引用 ZK 8.0.0配置参考 ZK 8.0.0开发人员的参考 ZK 8.0.0风格定制指南 ZK 8.0.0 ZUML参考 zk-mvvm-book
在这个"ZK开发文档中文HTML版"中,你将找到关于如何使用ZK框架进行开发的详细指南。 首先,文档会介绍ZK的基本概念,包括组件系统、事件处理和布局管理。ZK的组件库丰富,涵盖了各种常见的UI元素,如按钮、表格、树...
5. **ZK开发指南**:《ZK-devGuide》可能涵盖ZK的基本概念、API详解、实战案例、性能优化及常见问题解决方案等内容,对于深入理解和应用ZK非常有帮助。 6. **最佳实践** - **合理设计ZNode结构**:避免过多深层...
总之,ZK作为一个强大的Web应用开发框架,通过事件驱动和组件化的创新,为开发者提供了创建高性能、高用户体验的富互联网应用的工具。然而,理解和熟练掌握ZK框架及其独特的开发模式,对于充分利用其潜力来说至关...
风格指南提供了ZK组件的样式定义和定制方法,帮助开发者实现一致且美观的用户界面。它涵盖了CSS类、主题切换和皮肤定制等内容。 8. **ZUML参考(ZK 6.0.1 ZUML Reference.pdf)** ZUML是ZK特有的用户界面标记语言...
最新的前端框架ZK开发必备 ZK 8.0.0端参考 ZK 8.0.0组件开发的必需品 ZK 8.0.0组件引用 ZK 8.0.0配置参考 ZK 8.0.0开发人员的参考 ZK 8.0.0风格定制指南 ZK 8.0.0 ZUML参考 zk-mvvm-book