ZK创建一个真实的组件(称为宏组件)来显示常规宏,即前一章节描述的。
为了描述方便,当在此章节中讨论的宏组件时,即意味着为常规宏组件。
就像window,宏组件为一个ID空间所有者。换言之,在实现宏组件(亦=宏组件的子组件)的页面内使用什么标识来标识组件是自由的。它们不会和定义在使用宏组件的同一页面内的组件相冲突的。
例如,假定我们有如下一个宏定义。
<hbox> Username: <textbox id="who" value="${arg.who}"/> </hbox>
那么下面的代码将会正常工作。
<?component name="username" macro-uri="/WEB-INF/macros/username.zul"?> <zk> <username/> <button id="who"/> <!-- no conflict because it is in a different ID space --> </zk>
但是,下列的代码将不会工作。
<?component name="username" macro-uri="/WEB-INF/macros/username.zul"?> <username id="who"/>
为什么呢?就像任何ID空间所有者,宏组件本身和其子组件在相同的ID空间内。有两种可选的解决方案:
为宏组件的子组件标识使用一个特殊的前缀。例如使用"mc_who"
代替 "who"
。
<hbox> Username: <textbox id="mc_who" value="${arg.who}"/> </hbox>
使用window 组件创建 一个额外的ID空间。
<window> <hbox> Username: <textbox id="who" value="${arg.who}"/> </hbox> </window>
将以使用第一种方案T,简单方便。
就像其它的ID空间所有者,你可以调用getFellow
方法或使用 org.zkoss.zk.ui.Path
来访问其子组件(by use of two getFellow
method invocations or org.zkoss.zk.ui.Path
)。
例如,假定你有一个ID为"username"
的宏组件,那么可以按如下方式访问textbox。
comp.getFellow("username").getFellow("mc_who"); new Path("/username/mc_who");
宏组件像内联扩展一样工作。因此,就像其它组件,(一个宏组件的)子组件可以访问任何定义在父组件ID空间内的变量。
例如,username
的子组件可以直接访问v
。
<zscript> String v = "something"; </zscript> <username/>
但是,并不推荐这样使用(it is not recommended to utilize such visibility),因为这或许会限制宏的使用范围。
宏组件实现了org.zkoss.zk.ui.ext.DynamicPropertie
接口,所以你可以按如下方式使用 getDynamicProperty
方法访问其属性。
<username id="ua" who="John"/> <button label="what?" onClick="alert(ua.getDynamicProperty("who"))"/>
显然使用Dyn
amicPropertied
是很繁琐的。更糟的是,若你使用setDynamicProperty
改变一个属性,却不会被改变宏的子组件。例如,下面的代码会显示John
作为username,而不是Mary
。
<username id="ua" who="John"/> <zscript> ua.setDynamicProperty("who", "Mary"); </zscript>
为什么呢?一个宏组件的所有子组件都是在创建宏组件时被创建的,除非你手动操作这些子组件[58],否则是不会改变它们的。调用setDynamicProperty
仅会影响存储在宏组件中的属性(可以使用getDynamicProperties
获
取的)。textbox
的内容仍没有改变。
因此,最好增设一个方法,例如setWho
,用以直接操作宏组建组件。为了增设你自己的方法,必须为宏组件实现一个class,然后使用component 指令的class
属性指定此类。
[提示]: 可以使用recreate
方法来再创建(recreate) 子组件(包括其当前的属性)。实际上此方法是移除了所有的子组件,然后再重新创建它们。
有两种方式实现一个类。下面的章节描述了细节。
为宏组件增设方法需要两个步骤。
继承org.zkoss.zk.ui.HtmlMacroComponent
实现一个类。
//Username.java package mypack; public class Username extends HtmlMacroComponent { public void setWho(String name) { setDynamicProperty("who", name); //arg.who requires it final Textbox tb = (Textbox)getFellow("mc_who"); if (tb != null) tb.setValue(name); //correct the child if available } public String getWho() { return (String)getDynamicaProperty("who"); } }
正如上面所描述的,你必须在setWho
内调用setDynamicProperty
,因为在宏页面(${arg.who}
)内引用了 ${arg.who}
,${arg.who}
被用于宏组件创建其子组件时。
由于setWho
方法或许会在宏组件创建其子组件之前被创建,因此你必须检查mc_who
是否存在。
由于调用了mc_who
的setValue
,当调用setWho
时,客户端的内容及视觉表现(visual presentation)都会被自动更新。
使用class
属性在宏声明内声明类。
<?component name="username" macro-uri="/WEB-INF/macros/username.zul" class="mypack.Username"?>
除了使用Java文件实现,你也可以在zscript
中实现Java class(es)。优点是不需要编译(compilation),并且可以动态的修改其内容(无需重新部署Web应用程序)。缺点是降低了性能且容易出现打字错误(prone to typos)。
需要几个步骤在zscript
中实现Java class 。
你需要为要实现的类准备一个zscript
文件,例如/zs/username.zs
。注意你可以在相用的zscript
文件内放置任意数量的类及函数。
//username.zs package mypack; public class Username extends HtmlMacroComponent { public void setWho(String name) { setDynamicProperty("who", name); Textbox tb = getFellow("mc_who"); if (tb != null) tb.setValue(name); } public String getWho() { return getDynamicProperty("who"); } }
使用init
指令加载zscript
文件,然后声明组件。
<?init zscript="/zs/username.zs"?> <?component name="username" macro-uri="/WEB-INF/macros/username.zul" class="mypack.Username"?>
实现类(前一个例子中的mypack.Username
)直到宏组件被使用时才会被决定(resolved),所以使用zscript
元素为zscript
文件赋值(evaluate)是没有问题的。
<?component name="username" macro-uri="/WEB-INF/macros/username.zul" class="mypack.Username"?> <zk> <zscript src="/zs/username.zs"/> <username/> </zk>
尽管主观(subjective),init
指令仍更具有可读性。
就像其它的任何组件,你可以为任何特定的实例使用use
属性重写用于实现宏组件的类。
<?component name="username" macro-uri="/WEB-INF/macros/username.zul" class="mypack.Username?> <username use="another.MyAnotherUsername/>
当然在上面的例子中,你必须要提供一个another.MyAnohterUsername
的实现。然后再一次,可以使用单独的Java文件或zscript
来实现此类。
为了手动创建一个宏组件,你必须在所有的初始化完成后调用afterCompose
方法,如下。
HtmlMacroComponent ua = (HtmlMacroComponent) page.getComponentDefinition("username", false).newInstance(page); ua.setParent(wnd); ua.applyProperties(); //apply properties defined in the component definition ua.setDynamicProperty("who", "Joe"); ua.afterCompose(); //then the ZUML page is loaded and child components are created
[注]: getComponentDefinition
方法被用于在一个页面内查到组件定义。
若你为宏实现了一个类,例如Username,那么可按如下方式处理。
Username ua = new Username(); ua.setWho("Joe"); ua.setParent(wnd); ua.afterCompose();