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();