Regular Macros"
m |
m (correct highlight (via JWB)) |
||
Line 11: | Line 11: | ||
=== Macro Components and The ID Space === | === Macro Components and The ID Space === | ||
− | Like < | + | Like <code>window</code>, 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. | For example, assume we have a macro defined as follows. | ||
Line 40: | Line 40: | ||
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: | 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, < | + | 1. Use a special prefix for the identifiers of child components of a macro component. For example, <code>"mc_who"</code> instead of <code>"who"</code>. |
<source lang="xml" > | <source lang="xml" > | ||
Line 48: | Line 48: | ||
</source> | </source> | ||
− | 2. Use the < | + | 2. Use the <code>window</code> component to create an additional ID space. |
<source lang="xml" > | <source lang="xml" > | ||
Line 61: | Line 61: | ||
==== Access Child Components From the Outside ==== | ==== Access Child Components From the Outside ==== | ||
− | Like other ID space owner, you can access its child component by use of two < | + | Like other ID space owner, you can access its child component by use of two <code>getFellow</code> method invocations or org.zkoss.zk.ui.Path. |
− | For example, assume you have a macro component whose ID is called < | + | For example, assume you have a macro component whose ID is called <code>"username"</code>, and then you can access the <code>textbox</code> as follows. |
<source lang="java" > | <source lang="java" > | ||
Line 73: | Line 73: | ||
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. | 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, < | + | For example, <code>username</code>'s child component can access <code>v</code> directly. |
<source lang="xml" > | <source lang="xml" > | ||
Line 93: | Line 93: | ||
=== Provide Additional Methods === | === Provide Additional Methods === | ||
− | A macro component implements the <javadoc type="interface">org.zkoss.zk.ui.ext.DynamicPropertied</javadoc> interface, so you can access its properties by use of the < | + | A macro component implements the <javadoc type="interface">org.zkoss.zk.ui.ext.DynamicPropertied</javadoc> interface, so you can access its properties by use of the <code>getDynamicProperty</code> methods as follows. |
<source lang="xml" > | <source lang="xml" > | ||
Line 100: | Line 100: | ||
</source> | </source> | ||
− | Obviously, using < | + | Obviously, using <code>DynamicPropertied </code>is tedious. Worse of all, the macro's child components won't be changed if you use <code>setDynamicProperty</code> to change a property. For example, the following codes still show <code>John</code> as the username, not <code>Mary</code>. |
<source lang="xml" > | <source lang="xml" > | ||
Line 109: | Line 109: | ||
</source> | </source> | ||
− | 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<ref>On the other hand, the child components included by the < | + | 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<ref>On the other hand, the child components included by the <code>include</code> component is created in the rendering phase. In addition, all child components are removed and created each time the <code>include</code> component is invalidated.</ref>. Thus, the invocation to <code>setDynamicProperty</code> affects only the properties stored in a macro component (which you can retrieve with <code>getDynamicProperties</code>). The content of <code>textbox</code> remains intact. |
− | Thus, it is better to provide a method, say < | + | Thus, it is better to provide a method, say <code>setWho</code>, 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 <code>class</code> attribute of the component directive. |
− | '''Tip''': To ''recreate'' child components with the current properties, you can use the < | + | '''Tip''': To ''recreate'' child components with the current properties, you can use the <code>recreate</code> 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. | There are two ways to implement a class. The details are described in the following sections. | ||
Line 141: | Line 141: | ||
</source> | </source> | ||
− | * As depicted above, you have to call < | + | * As depicted above, you have to call <code>setDynamicProperty</code> in <code>setWho</code>, because <code>${arg.who}</code> is referenced in the macro page (<code>${arg.who}</code>), which is used when a macro component are creating its child components. |
− | * Since the < | + | * Since the <code>setWho</code> method might be called before a macro component creates its children, you have to check whether <code>mc_who</code> exists. |
− | * Since < | + | * Since <code>mc_who</code>'s <code>setValue</code> is called, both the content and the visual presentation at the client are updated automatically, when <code>setWho</code> is called. |
− | 2. Declare the class in the macro declaration with the < | + | 2. Declare the class in the macro declaration with the <code>class</code> attribute. |
<source lang="xml" > | <source lang="xml" > | ||
Line 153: | Line 153: | ||
==== Override the Implementation Class When Instantiation ==== | ==== Override the Implementation Class When Instantiation ==== | ||
− | Like any other component, you can use the < | + | Like any other component, you can use the <code>use</code> attribute to override the class used to implement a macro component for any particular instance. |
<source lang="xml" > | <source lang="xml" > | ||
Line 164: | Line 164: | ||
</source> | </source> | ||
− | Of course, you have to provide the implementation of < | + | Of course, you have to provide the implementation of <code>another.MyAnohterUsername</code> in the above example. Once again the class can be implemented with separate Java file, or by use of <code>zscript</code>. |
==== Create a Macro Component Manually ==== | ==== Create a Macro Component Manually ==== | ||
− | To create a macro component manually, you have to invoke the < | + | To create a macro component manually, you have to invoke the <code>afterCompose</code> method after all the initialization as follows. |
<source lang="java" > | <source lang="java" > | ||
Line 178: | Line 178: | ||
</source> | </source> | ||
− | '''Note''': The < | + | '''Note''': The <code>getComponentDefinition</code> 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. | If you implement a class, say Username, for the macro, then you can do as follow. |
Latest revision as of 10:40, 19 January 2022
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.