Implement Custom Java Class

From Documentation
Revision as of 14:15, 12 January 2022 by Hawk (talk | contribs) (replace tt with code (via JWB))


Implement Custom Java Class


Overview

As described in the earlier sections, a macro component is instantiated to represent a regular macro. By default, HtmlMacroComponent is assumed (and instantiated). However, you provide a custom Java class to provide a better API to simplify the access and to encapsulate the implementation.

Implement Custom Java Class for Macro

The implementation is straightforward. First, the custom Java class for macro components must extend from HtmlMacroComponent. Second, thought optional, it is suggested to invoke HtmlMacroComponent.compose() in the constructor[1][2], such that the template and the wiring of the data members will be applied in the constructor.

For example, suppose we have a macro template as follows.

<hlayout id="mc_layout">
	Username: <textbox id="mc_who"/>
</hlayout>

Then, we could implement a Java class for it:

package foo;

import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Textbox;

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class Username extends HtmlMacroComponent {
    @WireVariable
    private User currentUser; //will be wired if currentUser is a Spring-managed bean, when compose() is called
    @Wire
    private Textbox mc_who; //will be wired when compose() is called
    public Username() {
        compose(); //fore the template to be applied, and to wire members automatically
    }
    public String getWho() {
        return mc_who.getValue();
    }
    public void setWho(String who) {
        mc_who.setValue(who);
    }
    @Listen("onClick=#submit")
    public void submit() { //will be wired when compose() is called.
    }
}

As shown, HtmlMacroComponent.compose() will wire variables, components and event listener automatically, so we could access them directly (such as the mc_who member). For more information, please refer to the the Wire Components section, the Wire Variables section and Wire Event Listeners sections.

Also notice that the arg variable is still available to the template so as to represent properties set by DynamicPropertied.setDynamicProperty(String, Object), though it is really useful if a custom implementation is provided.


  1. By default, HtmlMacroComponent.compose() is invoked when HtmlMacroComponent.afterCompose() is called. In many cases, it is generally too late, so we suggest to invoke it in the contructor.
  2. HtmlMacroComponent.compose() is available in 5.0.5. For 5.0.4 or earlier, please invoke HtmlMacroComponent.afterCompose() instead.

Declare Macro with Custom Java Class

To make ZK Loader know which custom Java class to use, we have to specify the class attribute when declaring it in the component directives. For example,

<?component name="username" macroURI="/WEB-INF/macros/username.zul"
   class="foo.Username"?>

Use Macro with Custom Java Class

In ZUML

The use of the macro component with a custom Java class in a ZUML page is the same as other macro components.

In Java

The main purpose of introducing a custom Java class is to simplify the use of a macro component in Java. For example, you could invoke a more meaningful setter, say, setWho, directly rather than DynamicPropertied.setDynamicProperty(String, Object). In addition, the instantiation could be as simple as follows:

Username ua = new Username();
ua.setParent(wnd);
ua.setWho("Joe");

Macro Component and ID Space

Like Window, HtmlMacroComponent also implements IdSpace. It means that a macro component (excluding inline macros) is a space owner. In other words, it is free to use whatever the identifiers to identify components inside the template.

For example, assume we have a macro defined as follows.

<hlayout>
	Username: <textbox id="who" value="${arg.who}"/>
</hlayout>

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 do not 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".

<hlayout>
	Username: <textbox id="mc_who" value="${arg.who}"/>
</hlayout>

2. Use the window component to create an additional ID space.

<window>
	<hlayout>
		Username: <textbox id="who" value="${arg.who}"/>
	</hlayout>
</window>

The first solution is suggested, if applicable, due to the simplicity.

Manipulate component inside macro component

As the code described above, the component is wired and composed in a constructor. Thus, you can append to wired component or remove from wired component in setProperty method.

For example,

package foo;

import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Textbox;

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class Username extends HtmlMacroComponent {
    @WireVariable
    private User currentUser; //will be wired if currentUser is a Spring-managed bean, when compose() is called

    //Wire existing components
    @Wire
    private Textbox mc_who;
    @Wire
    private Hlayout mc_layout;
    public Username() {
        compose(); //fore the template to be applied, and to wire members automatically
    }
    public String getGender() {
        return currentUser.getGender();
    }
    public void setGender(String gender) {
        // append another textbox to hlayout
        Textbox genderTbx = new Textbox();
        genderTbx.setValue(gender);
        genderTbx.setParent(mc_layout);
    }
}

Also, you can add a forward event to the newly added component and forward the event to a macro component.

public class Username extends HtmlMacroComponent {
    // omitted

    @Wire
    private Hlayout mc_layout;
    public void setGender(String gender) {
        Textbox genderTbx = new Textbox();
        genderTbx.setValue(gender);
        genderTbx.setParent(mc_layout);
        // listen onChange event to the textbox and forward to macro component
        genderTbx.addForward(Events.ON_CHANGE, this, "onGenderChange", genderTbx.getValue());
    }
}

Then use the forward event to communicate with other components.

<?component name="username" macroURI="/WEB-INF/macros/username.zul" class="foo.Username"?>
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('foo.MacroVM')">
    <username who="John" label="Username" gender="@load(vm.gender)" onGenderChange="@command('changeGender')" />
</window>

Version History

Last Update : 2022/01/12


Version Date Content
5.0.5 October, 2010 HtmlMacroComponent.compose() was introduced.



Last Update : 2022/01/12

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