Implement Custom Java Class"

From Documentation
(10 intermediate revisions by 4 users not shown)
Line 8: Line 8:
 
=Implement Custom Java Class for Macro=
 
=Implement Custom Java Class for Macro=
  
The implementation is straightforward. First, the custom Java class for macro components must extend from <javadoc>org.zkoss.zk.ui.HtmlMacroComponent</javadoc>. Second, it shall invoke <javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> in the constructor<ref><javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> is available in 5.0.5. For 5.0.4 or earlier, please invoke <javadoc method="afterCompose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> instead.</ref>, such that the template will be applied in the constructor.
+
The implementation is straightforward. First, the custom Java class for macro components must extend from <javadoc>org.zkoss.zk.ui.HtmlMacroComponent</javadoc>. Second, thought optional, it is suggested to invoke <javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> in the constructor<ref>By default, <javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> is invoked when <javadoc method="afterCompose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> is called. In many cases, it is generally too late, so we suggest to invoke it in the contructor.</ref><ref><javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> is available in 5.0.5. For 5.0.4 or earlier, please invoke <javadoc method="afterCompose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> instead.</ref>, 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.
 
For example, suppose we have a macro template as follows.
  
 
<source lang="xml" >
 
<source lang="xml" >
<hlayout>
+
<hlayout id="mc_layout">
 
Username: <textbox id="mc_who"/>
 
Username: <textbox id="mc_who"/>
 
</hlayout>
 
</hlayout>
Line 20: Line 20:
 
Then, we could implement a Java class for it:
 
Then, we could implement a Java class for it:
  
<source lang="java">
+
<source lang="java" high="14">
 
package foo;
 
package foo;
  
 +
import org.zkoss.zk.ui.select.annotation.*;
 
import org.zkoss.zk.ui.HtmlMacroComponent;
 
import org.zkoss.zk.ui.HtmlMacroComponent;
 
import org.zkoss.zul.Textbox;
 
import org.zkoss.zul.Textbox;
  
 +
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
 
public class Username extends HtmlMacroComponent {
 
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
 
     private Textbox mc_who; //will be wired when compose() is called
 
     public Username() {
 
     public Username() {
         compose(); //fore the template to be applied
+
         compose(); //fore the template to be applied, and to wire members automatically
 
     }
 
     }
 
     public String getWho() {
 
     public String getWho() {
Line 37: Line 42:
 
         mc_who.setValue(who);
 
         mc_who.setValue(who);
 
     }
 
     }
     //public void onOK() {..} //you could add event listeners too
+
     @Listen("onClick=#submit")
 +
    public void submit() { //will be wired when compose() is called.
 +
    }
 
}
 
}
 
</source>
 
</source>
  
Notice that <javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> will wire fellows and event listener automatically, so we could access them directly (such as the <code>mc_who</code> member). For more information, please refer to the [[ZK Developer's Reference/MVC/Controller/Wire Variables|Wire Variables]] and
+
As shown, <javadoc method="compose()">org.zkoss.zk.ui.HtmlMacroComponent</javadoc> will wire variables, components and event listener automatically, so we could access them directly (such as the <code>mc_who</code> member). For more information, please refer to the [[ZK Developer's Reference/MVC/Controller/Wire Components|the Wire Components  section]], [[ZK Developer's Reference/MVC/Controller/Wire Variables|the Wire Variables section]] and
 
[[ZK Developer's Reference/MVC/Controller/Wire Event Listeners|Wire Event Listeners]] sections.
 
[[ZK Developer's Reference/MVC/Controller/Wire Event Listeners|Wire Event Listeners]] sections.
  
Also notice that the <code>arg</code> variable is still available to the template so as to represent properties set by <javadoc method="setDynamicProperty(java.lang.String, java.lang.Object)">org.zkoss.zk.ui.ext.DynamicPropertied</javadoc>. However, it is pointless to use it if we provide all required setters.
+
Also notice that the <code>arg</code> variable is still available to the template so as to represent properties set by <javadoc method="setDynamicProperty(java.lang.String, java.lang.Object)">org.zkoss.zk.ui.ext.DynamicPropertied</javadoc>, though it is really useful if a custom implementation is provided.
  
 
<blockquote>
 
<blockquote>
Line 77: Line 84:
 
=Macro Component and ID Space=
 
=Macro Component and ID Space=
  
Like <javadoc>org.zkoss.zul.Window</javadoc>, <javadoc>org.zkoss.zk.ui.HtmlMacroComponent</javadoc> also implements <javadoc>org.zkoss.zk.ui.IdSpacce</javadoc>. It means that a macro component (excluding inline macros) is a space owner.  In other words, it is free to use whatever identifiers to identify components inside the template.
+
Like <javadoc>org.zkoss.zul.Window</javadoc>, <javadoc>org.zkoss.zk.ui.HtmlMacroComponent</javadoc> also implements <javadoc>org.zkoss.zk.ui.IdSpace</javadoc>. 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.
 
For example, assume we have a macro defined as follows.
Line 97: Line 104:
 
</source>
 
</source>
  
However, the following codes ''don't'' work.
+
However, the following codes ''do not'' work.
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 125: Line 132:
  
 
The first solution is suggested, if applicable, due to the simplicity.
 
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,
 +
<source lang="java" high="25,26,27">
 +
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);
 +
    }
 +
}
 +
</source>
 +
Also, you can add a forward event to the newly added component and forward the event to a macro component.
 +
<source lang="java" high="11">
 +
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());
 +
    }
 +
}
 +
</source>
 +
Then use the forward event to communicate with other components.
 +
<source lang="xml" high="3">
 +
<?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>
 +
</source>
  
 
=Version History=
 
=Version History=

Revision as of 08:42, 28 August 2014


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 : 2014/08/28


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



Last Update : 2014/08/28

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