Regular Macros

From Documentation

Stop.png This documentation is for an older version of ZK. For the latest one, please click here.


Stop.png 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(&quot;another.zul&quot;)"/>

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(&quot;who&quot;))"/>

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 in setWho, 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 whether mc_who exists.
  • Since mc_who's setValue is called, both the content and the visual presentation at the client are updated automatically, when setWho 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

  1. 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 the include component is invalidated.



Last Update : 2022/01/19

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.