Regular Macros"

From Documentation
m (Created page with '{{ZKDevelopersGuidePageHeader}} __TOC__ ZK created a real component (called a macro component) to represent the regular macro as described in the previous section. For sake of…')
 
m (correct highlight (via JWB))
 
(3 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
{{ZKDevelopersGuidePageHeader}}
 
{{ZKDevelopersGuidePageHeader}}
 +
{{Old Version
 +
|url=http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/UI_Composing/Macro_Component/Implement_Custom_Java_Class
 +
|}}
  
 
__TOC__
 
__TOC__
Line 8: Line 11:
  
 
=== Macro Components and The ID Space ===
 
=== Macro Components and The ID Space ===
Like <tt>window</tt>, 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.
+
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 37: 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, <tt>"mc_who"</tt> instead of <tt>"who"</tt>.  
+
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 45: Line 48:
 
</source>
 
</source>
  
2. Use the <tt>window</tt> component to create an additional ID space.  
+
2. Use the <code>window</code> component to create an additional ID space.  
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 58: 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 <tt>getFellow</tt> method invocations or org.zkoss.zk.ui.Path.
+
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 <tt>"username"</tt>, and then you can access the <tt>textbox</tt> as follows.
+
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 70: 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, <tt>username</tt>'s child component can access <tt>v</tt> directly.
+
For example, <code>username</code>'s child component can access <code>v</code> directly.
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 90: 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 <tt>getDynamicProperty</tt> methods as follows.
+
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 97: Line 100:
 
</source>
 
</source>
  
Obviously, using <tt>DynamicPropertied </tt>is tedious. Worse of all, the macro's child components won't be changed if you use <tt>setDynamicProperty</tt> to change a property. For example, the following codes still show <tt>John</tt> as the username, not <tt>Mary</tt>.
+
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 106: 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 <tt>include</tt> component is created in the rendering phase. In addition, all child components are removed and created each time the <tt>include</tt> component is invalidated.</ref>. Thus, the invocation to <tt>setDynamicProperty</tt> affects only the properties stored in a macro component (which you can retrieve with <tt>getDynamicProperties</tt>). The content of <tt>textbox</tt> remains intact.
+
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 <tt>setWho</tt>, 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 <tt>class</tt> attribute of the component directive.
+
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 <tt>recreate</tt> method. It actually detaches all child components, and then create them again.
+
'''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 119: Line 122:
 
1. Implement a class by extending from the <javadoc>org.zkoss.zk.ui.HtmlMacroComponent</javadoc> class.
 
1. Implement a class by extending from the <javadoc>org.zkoss.zk.ui.HtmlMacroComponent</javadoc> class.
  
//Username.java
 
 
<source lang="java" >
 
<source lang="java" >
 +
//Username.java
 
package mypack;
 
package mypack;
  
Line 138: Line 141:
 
</source>
 
</source>
  
* As depicted above, you have to call <tt>setDynamicProperty</tt> in <tt>setWho</tt>, because <tt>${arg.who}</tt> is referenced in the macro page (<tt>${arg.who}</tt>), which is used when a macro component are creating its child components.
+
* 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 <tt>setWho</tt> method might be called before a macro component creates its children, you have to check whether <tt>mc_who</tt> exists.
+
* 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 <tt>mc_who</tt>'s <tt>setValue</tt> is called, both the content and the visual presentation at the client are updated automatically, when <tt>setWho</tt> is called.
+
* 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 <tt>class</tt> attribute.  
+
2. Declare the class in the macro declaration with the <code>class</code> attribute.
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 148: Line 151:
 
   class="mypack.Username"?>
 
   class="mypack.Username"?>
 
</source>
 
</source>
 
==== Provide Additional Methods in zscript ====
 
In addition to implementing with a Java file, you can implement the Java class(es) in <tt>zscript</tt>. The advantage is that no compilation is required and you can modify its content dynamically (without re-deploying the Web application). The disadvantage is the performance downgrade and prone to typos.
 
 
It takes a few steps to implement a Java class in <tt>zscript</tt>.
 
 
1. You have to prepare a zscript file, say <tt>/zs/username.zs</tt>, for the class to implement. Notice that you can put any number of classes and functions in the same <tt>zscript</tt> file.
 
 
//username.zs
 
<source lang="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");
 
}
 
}
 
</source>
 
 
2. Use the <tt>init</tt> directive to load the <tt>zscript</tt> file, and then declare the component
 
 
<source lang="xml" >
 
<?init zscript="/zs/username.zs"?>
 
<?component name="username" macroURI="/WEB-INF/macros/username.zul"
 
class="mypack.Username"?>
 
</source>
 
 
The implementation class (<tt>mypack.Username</tt> in the previous example) is resolved as late as the macro component is really used, so it is also OK to use the <tt>zscript</tt> element to evaluate the <tt>zscript</tt> file.
 
 
<source lang="xml" >
 
<?component name="username" macroURI="/WEB-INF/macros/username.zul"
 
class="mypack.Username"?>
 
<zk>
 
<zscript src="/zs/username.zs"/>
 
<username/>
 
</zk>
 
</source>
 
 
Though subjective, the <tt>init</tt> directive is more readable.
 
  
 
==== Override the Implementation Class When Instantiation ====
 
==== Override the Implementation Class When Instantiation ====
Like any other component, you can use the <tt>use</tt> attribute to override the class used to implement a macro component for any particular instance.
+
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 208: Line 164:
 
</source>
 
</source>
  
Of course, you have to provide the implementation of <tt>another.MyAnohterUsername</tt> in the above example. Once again the class can be implemented with separate Java file, or by use of <tt>zscript</tt>.
+
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 <tt>afterCompose</tt> method after all the initialization as follows.
+
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 222: Line 178:
 
</source>
 
</source>
  
'''Note''': The <tt>getComponentDefinition</tt> method is used to look up the component definitions defined in a page.
+
'''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

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.