天天看点

ZK程序员开发指南(二)

第二章    进入ZK

本章描述了如何编写你的第一个ZUML页面。我们建议即使你很忙,也至少需要将本章阅读完。

本章介绍了如何使用XUL来描述ZK的特性,当然对于ZK支持的其他标签语言也是适用的。

Hello World!

当你在自己喜欢的Web服务器上安装了ZK后,编写应用就变得很简单了。你只要创建一个文件,如下面所示的hello.zul,关键是部署时你要把这个文件放在正确的目录下。

<window title="Hello" >

Hello World!

</window>

然后,在浏览器中输入正确的路径(URL),如:http://localhost/myapp/hello.zul,你将获得下图所示的结果。

ZK程序员开发指南(二)

在ZUML页面中,XML中的元素描述了你想要创建的组件。在这个例子中,它是一个窗口(org.zkoss.zul.Window)。XML元素的属性值用于标识窗口组件的属性。在我们的例子中,我们创建了一个窗口组件,包含一个title和一个边框,而仅仅需要分别设置title和border属性的值为“Hello”和“normal”就可以了。

XML元素中的文本同样也被ZK引擎解释为一个特殊的组件,我们称之为label(org.zkoss.zul.Label)。这样,上述的例子其实与下面这个例子等价。

<window title="Hello" >

<label value="Hello World!"/>

</window>

互动性

现在我们来给页面添加一些互动特性。

<window title="Hello" >

<button label="Say Hello" onClick="alert(&quot;Hello World!&quot;)"/>

</window>

这样,当你点击页面上的按钮时,你将看到下面的结果。

ZK程序员开发指南(二)

OnClick属性是一个特殊的属性,主要用于给组件添加事件监听特性,而属性值可以是任何合法的java代码。这里要注意的是,我们使用了&quot;来表示双引号(")来保证我们的文本是一个合法的XML文件。如果你对XML不熟悉,你可能需要看看“ZK用户接口标签语言”这一章的XML部分。

Alert函数是一个全局函数,用于显示一个消息对话框。它其实是org.zkoss.zul.Messagebox类的show方法的一个快捷调用方式。

<button label="Say Hello" onClick="Messagebox.show(&quot;Hello World!&quot;)"/>

注意:

1。ZUML页面中嵌入的脚本可以用不同的语言来编写,包括但不限于Java,JavaScript,Ruby和Groovy,而且你所编写的脚本均在服务器上运行。

2。ZK使用BeanShell来在运行时解释执行Java程序,因此你可以为此声明一些全局方法,如alert。类似的,几乎所有脚本语言都提供了一种很简单的方式定义全局函数,有时候或者是全局类。

3。java.lang, java.util, org.zkoss.zk.ui, org.zkoss.zk.ui.event 和 org.zkoss.zul包中的类在解释器解释ZUML页面中嵌入的脚本代码之前都会被系统导入。

zscript元素

zscript是一个特殊的元素,用于定义ZUML页面被渲染时将要被验证的脚本代码。典型的应用包括初始化和声明全局变量和方法。

         注意:你不能在zscript标识的代码中使用EL表达式。

例如,以下的例子演示了一个页面上的按钮每次被点击时显示不同的消息。

<window title="Hello" >

<button label="Say Hello" onClick="sayHello()"/>

<zscript>

int count = 0;

void sayHello() { //declare a global function

alert("Hello World! "+ ++count);

}

</zscript>

</window>

        注意:zscript标识的脚本代码只是在页面被初次装载的时候才被解释器所处理,所以其常常用来定义方法和初始变量。

脚本语言

缺省情况下,默认的脚本语言是Java。然而,你可以通过下例中的方法指定language属性来选择使用不同的脚本语言。languange属性的值是大小写敏感的。

<zscript language="javascript">

alert('Say Hi in JavaScript');

new Label("Hi, JavaScript!").setParent(win);

</zscript>

为了用特定的脚本语言来处理事件,你可以在处理函数前添加相关的前缀,如下面的例子使用了javascript。需要注意的是,语言名称的前后不能有空格键存在。

<button onClick="javascript: do_something_in_js();"/>

在同一页面内,你可以使用不同的脚本语言,这是不受限制的。

将脚本代码与页面分离

程序开发人员可能希望将脚本代码和显示代码分离开(如用sayHello.zs文件保存代码),以区分系统的代码和显示层,这时,开发人员可以用src属性指向相应的脚本代码。如下例所示。

<window title="Hello" >

<button label="Say Hello" onClick="sayHello()"/>

<zscript src="sayHello.zs"/>

</window>

相应的sayHello.zs文件内容如下:

int count = 0;

void sayHello() { //declare a global function

alert("Hello World! "+ ++count);

}

属性(attribute)元素

属性元素是一个用于定义闭包元素的XML属性的特殊元素。只要使用得当,可以是页面代码的可读性增加。下面这个使用了属性元素的例子与之前的hello.zul例子等价。

<button label="Say Hello">

 <attribute name="onClick">alert("Hello World!");</attribute>

</button>

你能够控制通过指定trim属性值来确定是否需要把属性前后的空格键去掉。缺省情况下是不做处理。

<button>

<attribute label="value" trim="true">

The leading and trailing whitespaces will be omitted.

</attribute>

</button>

EL表达式

与JSP类似,你一样可以在ZUML除了属性名、元素名和处理指令的任何地方使用EL表达式。

EL表达式使用的语法形式为${expr},如下例所示。

<element attr1=”${bean.property}”.../>

${map[entry]}

<another-element>${3+counter} is ${empty map}</another-element>

注意:1。empty是一个操作符,用于测试一个map,collection,array或string是否为空。

      2.map[entry]是一个存取map中的元素的方法。换句话说,它的语意相当于Java中的 map.get(entry)。

当EL表达式被用于作为属性值的时候,只要组件接受这个表达式,表达式就能返回任意类型的对象。如下例所示,EL表达式返回的是一个布尔型对象。

<window if="${some > 10}">

注意:+运算符在EL中是数学算符,并不能处理字符串连接。如果你想实现字符串的连接,只要使用表达式“${expr1} is added with ${expr2}”即可。

标准的隐性对象,如param和requestScope,以及ZK隐性对象,如self和page,支持一下的简化使用方式。

<textbox value="${param.who} does ${param.what}"/>

如果需要从TLD文件中引入EL函数,你可以使用tablib这个处理指令,如下例所示。

<?taglib uri="/WEB-INF/tld/web/core.tld" prefix="p" ?>

ZK程序员指南提供了关于EL表达式的更多的信息。当然,你也可以参考JSP2.0参考文献来获取更多的信息。

id属性

如果需要在java代码和EL表达式中访问组件,你可以给组件赋一个id,这可以通过使用id属性来实现。在下面的例子中,我们给一个label设置了一个id,这样我们就可以在页面上的按钮被按下的时候改变它的值。

<window title="Vote" >

Do you like ZK? <label id="label"/>

<separator/>

<button label="Yes" onClick="label.value = self.label"/>

<button label="No" onClick="label.value = self.label"/>

</window>

当按下Yes按钮时,你可以看到:

ZK程序员开发指南(二)

下面这个例子适用于在EL表达式中引用组件时使用。

<textbox id="source" value="ABC"/>

<label value="${source.value}"/>

if 和 unless 属性

if 和 unless 属性用于控制是否创建一个组件。在下面的例子中,只有当用户提交的请求包含vote这个参数时,这两个组件才会被创建。

<label value="Vote 1" if="${param.vote}"/>

<label value="Vote 2" unless="${!param.vote}"/>

如果两个属性都被指定,只有当两个表达式的结果都为真的时候,组件才会被创建。

foreach属性

foreach属性用来控制将会被创建的组件的数目。如果你给这个属性制定一个对象集合,ZK Loader将会为这个集合内的每一个元素创建一个组建对象。例如,在下面的ZUML页面中,listitem元素将会访问3次,每次分别访问“Monday”,“Tuesday”,“Wednesday”,然后这3个列表对象就会被创建出来。

<zscript>contacts = new String[] {"Monday", "Tuesday", "Wednesday"};</zscript>

<listbox width="100px">

<listitem label="${each}" forEach="${contacts}"/>

</listbox>

当你使用foreach属性来访问集合中的元素时,each变量会被集合中的元素一次次的赋值,也就是说,上例中的contacts集合会被一次一次的访问到。它与下述的例子等价。

<listbox>

  <listitem label="Monday"/>

  <listitem label="Tuesday"/>

  <listitem label="Wednesday"/>

</listbox>

对于foreach,你还可以使用forEachBegin和forEachEnd来控制迭代。关于更多的细节信息,请参考章节—ZK用户接口标记语言中的ZK属性部分。

use属性

在页面中不恰当的嵌入代码脚本会给页面的维护带来头痛的问题。有两种方法将代码和表示层分离开来。

一种方式是监听感兴趣的事件,然后调用相应的处理方法。例如,你可以创建自己的方法来处理初始化事件,处理事件和取消事件,这些可以通过对onCreate,onOK和onCancel消息的处理来实现。

<window id="main" onCreate="MyClass.init(main)"

onOK="MyClass.process(main)" onCancel="MyClass.cancel(main)"/>

当然,你必须要定义一个名为MyClass的Java类,如下所示。

import org.zkoss.zul.Window;

public class MyClass {

public static void init(Window main) { //does initialization

}

public static void save(Window main) { //saves the result

}

public static void cancel(Window main) { //cancel any changes

}

}

另一种方法是使用use属性来制定一个类代替缺省的组件类。

<window use="MyWindow"/>

同样的,你也需要定义一个Java类叫MyWindow。

import org.zkoss.zul.Window;

public class MyWindow extends Window {

public void onCreate() { //does initialization

}

public void onOK() { //save the result

}

public void onCancel() { //cancel any changes

}

}

两种方法各有各的好处。在MVC模式中,这两个类都承担着类似控制器的角色,如何选择一切在你。

在zscript中实现Java类

由于BeanShell的强大功能,我们可以再zscript中很轻易的用下面的方式实现java类。

<zscript>

public class MyWindow extends Window {

}

</zscript>

<window use="MyWindow"/>

提示:很多脚本语言,如JRuby,同样允许开发人员定义类,而这些类可以被JVM所访问到。具体的细节请参见相关的开发手册.

如果要将脚本代码和表示层分开,你可以将所有的zscript代码保存到一个独立的文件中,如mywnd.zs,然后用以下的方式引用,

<zscript src="/zs/mywnd.zs"/>

<window use="MyWindow"/>

提示:你也可以用init命令来指定一个zscript文件,

手工创建组件

除了在ZUML页面中描述需要创建什么组件外,开发人员同样可以手工的创建组件。所有的组件类都是实体类,你只要通过调用它们的构造函数就可以直接将其创建出来。

<window id="main">

<button label="Add Item">

<attribute name="onClick">

new Label("Added at "+new Date()).setParent(main);

new Separator().setParent(main);

</attribute>

</button>

<separator bar="true"/>

</window>

当你手工创建一个组件时,逐渐不会被自动的创建到页面中。换句话说,在用户的浏览器中你是看不到刚才手工创建的组件的。要将创建的组件加入页面,你可以调用setParent、appendChild 或者 insertBefore方法来给创建好的组件一个父节点,如果这个父节点是页面的一部分,那么组件就会成为页面的一部分。

对于组件,没有销毁或是关闭的方法。只要组件从页面中解除关联关系,组件就会被移除。移除的效果是即时的。如下例所示。

<window id="main">

<zscript>Component detached = null;</zscript>

<button id="btn" label="Detach">

<attribute name="onClick">

if(detached != null) {

detached.setParent(main);

detached = null;

btn.label = "Detach";

} else {

(detached = target).setParent(null);

btn.label = "Attach";

}

</attribute>

</button>

<separator bar="true"/>

<label id="target" value="You see this if it is attached."/>

</window>

在上面的例子中,你可以使用setVisible方法获得类似的效果。然而,setVisible(false)并不会将组件从页面中移除,而仅仅是让组件不可见而已。

当组件在页面中被移除后,它所占据的内存将会被JVM的GC回收,这时,应用已经不再引用这块内存了。

不使用ZUML开发ZK应用程序

对于完全不想使用ZUML来开发应用的程序员,可以使用所谓的richlet来手工创建所有的组件。

import org.zkoss.zul.*;

public class TestRichlet extends org.zkoss.zk.ui.GenericRichlet {

public void service(Page page) {

page.setTitle("Richlet Test");

final Window w = new Window("Richlet Test", "normal", false);

new Label("Hello World!").setParent(w);

final Label l = new Label();

l.setParent(w);

//...

w.setPage(page);

}

}

具体细节请参考章节《高级特性》中的Richlets部分。

为特定的页面定义新的组件

如前文所述,给通过XML属性组件赋予相应属性是很简单的一件事。

<button label="OK" style="border:1px solid blue"/>

ZK提供了一个功能强大但是简单的方式使得开发人员可以为特定的页面定义新的组件。这对于同样类型的大部分组件共享一个属性集的情况是很有用的。

首先,使用component指令定义新的组件。

<?component name="bluebutton" extends="button" style="border:1px solid blue" label="OK"?>

<bluebutton/>

<bluebutton label="Cancel"/>

上面的代码等价于

<bluebutton style="border:1px solid blue" label="OK"/>

<bluebutton style="border:1px solid blue" label="Cancel"/>

而且,你可以用如下的方式重载button的定义,当然,这个定义对其他的页面是没有任何影响的。

<?component name="button" extends="button" style="border:1px solid blue" label="OK"?>

<button/>

<button label="Cancel"/>

更多的信息,请参看《ZK 用户接口标记语言》一章的component指令部分。

继续阅读