Regular Macros
This documentation is for an older version of ZK. For the latest one, please click here.
This documentation is for an older version of ZK. For the latest one, please click here.
ZK created a real component (called a macro component) to represent the regular macro as described in the previous section.
For sake of convenience, when we talk about macro components in this section, we mean the regular macro components.
Macro Components and The ID Space
Like window
, a macro component is an ID space owner. In other words, it is free to use whatever identifiers to identify components inside the page implementing a macro component (a.k.a., child components of the macro component). They won't conflict with components defined in the same page with the macro component.
For example, assume we have a macro defined as follows.
<hbox>
Username: <textbox id="who" value="${arg.who}"/>
</hbox>
Then, the following codes work correctly.
<?component name="username" macroURI="/WEB-INF/macros/username.zul"?>
<zk>
<username/>
<button id="who"/> <!-- no conflict because it is in a different ID space -->
</zk>
However, the following codes don't work.
<?component name="username" macroURI="/WEB-INF/macros/username.zul"?>
<username id="who"/>
Why? Like any ID space owner, the macro component itself is in the same ID space with its child components. There are two alternative solutions:
1. Use a special prefix for the identifiers of child components of a macro component. For example, "mc_who"
instead of "who"
.
<hbox>
Username: <textbox id="mc_who" value="${arg.who}"/>
</hbox>
2. Use the window
component to create an additional ID space.
<window>
<hbox>
Username: <textbox id="who" value="${arg.who}"/>
</hbox>
</window>
The first solution is suggested, if applicable, due to the simplicity.
Access Child Components From the Outside
Like other ID space owner, you can access its child component by use of two getFellow
method invocations or org.zkoss.zk.ui.Path.
For example, assume you have a macro component whose ID is called "username"
, and then you can access the textbox
as follows.
comp.getFellow("username").getFellow("mc_who");
new Path("/username/mc_who");
Access Variables Defined in the Ancestors
Macro components work as inline-expansion. Thus, like other components, a child component (of a macro component) can access any variable defined in the parent's ID space.
For example, username
's child component can access v
directly.
<zscript>
String v = "something";
</zscript>
<username/>
However, it is not recommended to utilize such visibility because it might limit where a macro can be used.
Change macroURI At the Runtime
You can change the macro URI dynamically as follows.
<username id="ua"/>
<button onClick="ua.setMacroURI("another.zul")"/>
Provide Additional Methods
A macro component implements the DynamicPropertied interface, so you can access its properties by use of the getDynamicProperty
methods as follows.
<username id="ua" who="John"/>
<button label="what?" onClick="alert(ua.getDynamicProperty("who"))"/>
Obviously, using DynamicPropertied
is tedious. Worse of all, the macro's child components won't be changed if you use setDynamicProperty
to change a property. For example, the following codes still show John
as the username, not Mary
.
<username id="ua" who="John"/>
<zscript>
ua.setDynamicProperty("who", "Mary");
</zscript>
Why? All child components of a macro component are created when the macro component is created, and they won't be changed unless you manipulate them manually[1]. Thus, the invocation to setDynamicProperty
affects only the properties stored in a macro component (which you can retrieve with getDynamicProperties
). The content of textbox
remains intact.
Thus, it is better to provide a method, say setWho
, to manipulate the macro component directly. To provide your own methods, you have to implement a class for the macro components, and then specify it in the class
attribute of the component directive.
Tip: To recreate child components with the current properties, you can use the recreate
method. It actually detaches all child components, and then create them again.
There are two ways to implement a class. The details are described in the following sections.
Provide Additional Methods in Java
It takes two steps to provide additional methods for a macro component.
1. Implement a class by extending from the HtmlMacroComponent class.
//Username.java
package mypack;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Textbox;
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)getDynamicProperty("who");
}
}
- As depicted above, you have to call
setDynamicProperty
insetWho
, because${arg.who}
is referenced in the macro page (${arg.who}
), which is used when a macro component are creating its child components. - Since the
setWho
method might be called before a macro component creates its children, you have to check whethermc_who
exists. - Since
mc_who
'ssetValue
is called, both the content and the visual presentation at the client are updated automatically, whensetWho
is called.
2. Declare the class in the macro declaration with the class
attribute.
<?component name="username" macroURI="/WEB-INF/macros/username.zul"
class="mypack.Username"?>
Override the Implementation Class When Instantiation
Like any other component, you can use the use
attribute to override the class used to implement a macro component for any particular instance.
<?component name="username" macroURI="/WEB-INF/macros/username.zul"
class="mypack.Username?>
<username use="another.MyAnotherUsername/>
Of course, you have to provide the implementation of another.MyAnohterUsername
in the above example. Once again the class can be implemented with separate Java file, or by use of zscript
.
Create a Macro Component Manually
To create a macro component manually, you have to invoke the afterCompose
method after all the initialization as follows.
HtmlMacroComponent ua = (HtmlMacroComponent)
page.getComponentDefinition("username", false).newInstance(page, null);
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
Note: The getComponentDefinition
method is used to look up the component definitions defined in a page.
If you implement a class, say Username, for the macro, then you can do as follow.
Username ua = new Username();
ua.setWho("Joe");
ua.setParent(wnd);
ua.afterCompose();
Notes
- ↑ On the other hand, the child components included by the
include
component is created in the rendering phase. In addition, all child components are removed and created each time theinclude
component is invalidated.