Composite Component"

From Documentation
m (remove empty version history (via JWB))
 
(25 intermediate revisions by 6 users not shown)
Line 3: Line 3:
 
__TOC__
 
__TOC__
  
Like a [[ZK Developer's Reference/UI Composing/Macro Component|macro component]], a composite component is an approach to compose a component based on a template. Unlike a macro component, a composite component has to create and wire the child components by itself, and handle ID space if necessary. The advantage is that a composite component can extend from any component, such as <javadoc>org.zkoss.zul.Row</javadoc>, such that it is easier to fit to any situation (and no need of the inline concept).
+
Like a [[ZK Developer's Reference/UI Composing/Macro Component|macro component]], a composite component is an approach to compose a component based on a template. Unlike a macro component, a composite component has to create and wire the child components by itself, and handle ID space if necessary. The advantage is that a composite component can extend from any component, such as <javadoc>org.zkoss.zul.Row</javadoc>, such that it is easier to fit to any situation (and no need for the inline concept).
  
In short, it is suggested use a macro component if applicable (since it is easier), while using a composite component otherwise.
+
In short, it is suggested to use a macro component if applicable (since it is easier), while using a composite component otherwise.
 +
 
 +
<blockquote>
 +
----
 +
If you'd like to assemble UI at runtime (aka., templating), please refer to [[ZK Developer's Reference/UI Patterns/Templating|the Templating section]] for more information.
 +
</blockquote>
  
 
=Implement a Composite Component=
 
=Implement a Composite Component=
  
First, you have to decide which component to extend from. <javadoc>org.zkoss.zul.Div</javadoc> is a common choice since it is a simple component. However, here our example extends from <javadoc>org.zkoss.zul.Row</javadoc>, so it can be used under <javadoc>org.zkoss.zul.Rows</javadoc>, which the regular macros cannot.
+
First, you have to decide which component to extend from. <javadoc>org.zkoss.zul.Div</javadoc> is a common choice as it is a simple component. However, here our example extends from <javadoc>org.zkoss.zul.Row</javadoc>, so it can be used under <javadoc>org.zkoss.zul.Rows</javadoc>, which the regular macros cannot.
  
Second, you have implement a template (in a ZUML document) to define what child components the composite component has. Then, you have to implement a Java class to put them together.
+
Second, you have to implement a template (in a ZUML document) to define what child components the composite component has. Then, you have to implement a Java class to put them together.
  
 
==Implement a Template==
 
==Implement a Template==
The implementation of a template is straightforward. There is nothing special to handle. Since it is rendered by <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Execution</javadoc>, you could pass whatever data you prefer to it (thru the <code>arg</code> argument).
+
The implementation of a template is straightforward. There is nothing special to handle. Since it is rendered by <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Execution</javadoc>, you could pass whatever data you prefer to it (through the <code>arg</code> argument).
  
 
Suppose we have a template as follows, and it is placed at <code>/WEB-INF/composite/username.zul</code>.
 
Suppose we have a template as follows, and it is placed at <code>/WEB-INF/composite/username.zul</code>.
Line 26: Line 31:
 
==Implement a Java Class==
 
==Implement a Java Class==
  
To implement the Java class we shall:
+
To implement a Java class we shall:
  
 
# Extend from the component class you want.
 
# Extend from the component class you want.
 
# (Optional) Implement <javadoc>org.zkoss.zk.ui.IdSpace</javadoc> to make it an [[ZK Developer's Reference/UI Composing/Component-based UI#ID_Space|ID space owner]].  
 
# (Optional) Implement <javadoc>org.zkoss.zk.ui.IdSpace</javadoc> to make it an [[ZK Developer's Reference/UI Composing/Component-based UI#ID_Space|ID space owner]].  
# Render the template in the constructor by use of <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Executions</javadoc> or others.
+
# Render the template in the constructor by the use of <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Executions</javadoc> or others.
# (Optional) Wire fellows and event listeners after rendering by use of <javadoc method="wireVariables(org.zkoss.zk.ui.Component, java.lang.Object, char, boolean, boolean)">org.zkoss.zk.ui.Components</javadoc> (wiring variables) and <javadoc method="addForwards(org.zkoss.zk.ui.Component, java.lang.Object, char)">org.zkoss.zk.ui.Components</javadoc> (wiring event listener).
+
# (Optional) Wire variables, components and event listeners after rendering with the use of <javadoc method="wireVariables(org.zkoss.zk.ui.Component, java.lang.Object, java.util.List)">org.zkoss.zk.ui.select.Selectors</javadoc> (wiring variables), <javadoc method="wireComponents(org.zkoss.zk.ui.Component, java.lang.Object, boolean)">org.zkoss.zk.ui.select.Selectors</javadoc> (wiring components) and <javadoc method="wireEventListeners(org.zkoss.zk.ui.Component, java.lang.Object)">org.zkoss.zk.ui.select.Selectors</javadoc> (wiring event listeners).
  
 
For example,
 
For example,
  
<source lang="Java">
+
<source lang="Java" highlight="9,14,17,18,19">
 
package foo;
 
package foo;
  
 
import org.zkoss.zk.ui.IdSpace;
 
import org.zkoss.zk.ui.IdSpace;
 +
import org.zkoss.zk.ui.select.Selectors;
 
import org.zkoss.zul.Row;
 
import org.zkoss.zul.Row;
 
import org.zkoss.zul.Textbox;
 
import org.zkoss.zul.Textbox;
  
 
public class Username extends Row implements IdSpace {
 
public class Username extends Row implements IdSpace {
 +
    @Wire
 
     private Textbox mc_who; //will be wired when Components.wireVariables is called
 
     private Textbox mc_who; //will be wired when Components.wireVariables is called
 +
 
     public Username() {
 
     public Username() {
 
         //1. Render the template
 
         //1. Render the template
 
         Executions.createComponents("/WEB-INF/composite/username.zul", this, null);
 
         Executions.createComponents("/WEB-INF/composite/username.zul", this, null);
  
         //2. Wire variables (optional)
+
         //2. Wire variables, components and event listeners (optional)
         Components.wireVariables(this, this, '$', true, true);
+
         Selectors.wireVariables(this, this, null);
            //ignore zscript and variable resolvers for better performance (optional)
+
         Selectors.wireComponents(this, this, false);
 
+
         Selectors.wireEventListeners(this, this);
         //3. Wire event listeners (optional)
 
         Components.addForwards(this, this, '$');
 
 
     }
 
     }
 
     public String getWho() {
 
     public String getWho() {
Line 65: Line 71:
 
</source>
 
</source>
  
After <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Executions</javadoc> is called, all components specified in the template will be instantiated and become the child component of the composite component (Row). Notice that the URI must match the location of the template correctly.
+
After <javadoc method="createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map)">org.zkoss.zk.ui.Executions</javadoc> is called, all components specified in the template will be instantiated and become the child components of the composite component (Row). Notice that the URI must match the location of the template correctly.
 +
 
 +
Depending on the implementation you want, you could wire the data members (<code>mc_who</code>) by calling <javadoc method="wireComponents(org.zkoss.zk.ui.Component, java.lang.Object, boolean)">org.zkoss.zk.ui.select.Selectors</javadoc>. This method will search all data members and setter methods and ''wire'' the component with the same ID. Similarly, <javadoc method="wireEventListeners(org.zkoss.zk.ui.Component, java.lang.Object)">org.zkoss.zk.ui.select.Selectors</javadoc> is used to wire event listeners.
 +
 
 +
For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Wire Variables|the Wire Components section]] and
 +
[[ZK Developer's Reference/MVC/Controller/Wire Event Listeners|Wire Event the Listeners section]] sections.
 +
 
 +
<blockquote>
 +
----
 +
Notice that there is a utility called [http://github.com/zanyking/ZK-Composite| ZK Composite]. With the help of [http://github.com/zanyking/ZK-Composite| ZK Composite], components are created and wired automatically based on the Java annotations you provide. In other words, Step 3 and 4 are done automatically. For more information, please refer to the [[#Define Components with Java Annotations|Define Components with Java Annotations]] section.
 +
</blockquote>
 +
 
 +
===Wire Spring-managed Beans===
 +
<javadoc method="wireVariables(org.zkoss.zk.ui.Component, java.lang.Object, java.util.List)">org.zkoss.zk.ui.select.Selectors</javadoc> will wire variables that can be resolved by the registered variable resolver. In addition to [[ZUML Reference/ZUML/Processing Instructions/variable-resolver|the variable-resolver directive]], you can create any variable resolver manually and pass it as the third argument. <javadoc method="newVariableResolvers(java.lang.Class, java.lang.Class)">org.zkoss.zk.ui.select.Selectors</javadoc> provides a convenient way to instantiate variable resolvers. For example, let us say we'd like to wire Spring-manage beans, then we can do as follows.
 +
 
 +
<source lang="java" highlight="1,3,10">
 +
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
 +
public class Username extends Row implements IdSpace {
 +
    @WireVariable
 +
    private User user;
 +
 
 +
    public Username() {
 +
        Executions.createComponents("/WEB-INF/composite/username.zul", this, null);
  
Depending on the implementation you want, you could wire the data members (<code>mc_who</code>) by calling <javadoc method="wireVariables(org.zkoss.zk.ui.Component, java.lang.Object, char, boolean, boolean)">org.zkoss.zk.ui.Components</javadoc>. This method will search all data members and setter methods and ''wire'' the component with the same ID. Similarly, <javadoc method="addForwards(org.zkoss.zk.ui.Component, java.lang.Object, char)">org.zkoss.zk.ui.Components</javadoc> is used to wire event listeners.
+
        Selectors.wireVariables(this, this,
 +
            Selectors.newVariableResolvers(getClass(), Row.class));
 +
        Selectors.wireComponents(this, this, false);
 +
        Selectors.wireEventListeners(this, this);
 +
    }
 +
...
 +
</source>
  
In this example, we specify true to the third and fourth arguments of <javadoc method="wireVariables(org.zkoss.zk.ui.Component, java.lang.Object, char, boolean, boolean)">org.zkoss.zk.ui.Components</javadoc>, such that it won't search variables defined in zscript and variable resolvers and then the performance is better.
+
<javadoc method="newVariableResolvers(java.lang.Class, java.lang.Class)">org.zkoss.zk.ui.select.Selectors</javadoc> will look for the <code>@VariableResolver</code> annotation and instantiate it automatically. As shown, we annotate <javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc> to resolve Spring-managed bean.
  
For more information, please refer to the [[ZK Developer's Reference/MVC/Controller/Wire Variables|Wire Variables]] and
+
For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Wire Variables|the Wire Variables section]].
[[ZK Developer's Reference/MVC/Controller/Wire Event Listeners|Wire Event Listeners]] sections.
 
  
 
===ID Space===
 
===ID Space===
  
Unless you extends a component that is an [[ZK Developer's Reference/UI Composing/Component-based UI#ID_Space|ID space owner]] (such as <javadoc>org.zkoss.zul.Window</javadoc>), all child components specified in the template will be in the same ID space as its parent. It might be convenient at the first glance. However, it will cause the ID conflict if we have multiple instances of the same composite component. Thus, it is generally suggested to make the composite component as a space owner
+
Unless you extend a component that is an [[ZK Developer's Reference/UI Composing/Component-based UI#ID_Space|ID space owner]] (such as <javadoc>org.zkoss.zul.Window</javadoc>), all child components specified in the template will be in the same ID space as its parent. It might be convenient at the first glance. However, it will cause ID conflict if we have multiple instances of the same composite component. Thus, it is generally suggested to make the composite component a space owner.
  
It can be done easily by implementing an extra interface <javadoc>org.zkoss.zk.ui.IdSpace</javadoc>. No method needs to be implemented.
+
It can be done easily by implementing an extra interface <javadoc>org.zkoss.zk.ui.IdSpace</javadoc>. No other method needs to be implemented.
  
 
<source lang="java">
 
<source lang="java">
Line 88: Line 121:
  
 
=Use Composite Component=
 
=Use Composite Component=
=Version History=
+
 
Last Update : {{REVISIONYEAR}}/{{REVISIONMONTH}}/{{REVISIONDAY}}
+
Like macros and any other primitive components, you have to declare a composite component before using it. This can be done by using [[ZUML Reference/ZUML/Processing Instructions/component|component directives]]. Then, we could use it the same way (they are actually primitive components). For example,
{| border='1px' | width="100%"
+
 
! Version !! Date !! Content
+
<source lang="xml">
|-
+
<?component name="username" extends="row" class="foo.Username"?>
| &nbsp;
+
 
| &nbsp;
+
<grid>
| &nbsp;
+
    <rows>
|}
+
      <username who="Joe"/>
 +
      <username who="Hellen"/>
 +
    </rows>
 +
</grid>
 +
</source>
 +
 
 +
=Define Composite Components as Standard Components=
 +
 
 +
If a composite component is used in multiple pages, it is better to define it in the application level, such that it can be accessed in any page without any [[ZUML Reference/ZUML/Processing Instructions/component|component directives]].
 +
 
 +
There are basic two approaches to define a component in the application level:
 +
 
 +
#Define it in an XML file which is called [[ZK Client-side Reference/Language Definition|a language addon]].
 +
#Define it with Java annotations.
 +
 
 +
==Define Components in a Language Addon==
 +
 
 +
A language addon is an XML file providing additional component definitions or customizing the standard components. For example, you can define the username component described in the previous section as follows.
 +
 
 +
<source lang="xml">
 +
<language-addon>
 +
    <addon-name>myapp</addon-name>
 +
    <component>
 +
        <component-name>username</component-name>
 +
        <extends>rows</extends>
 +
        <component-class>foo.Username</component-class>
 +
    </component>
 +
</language-addon>
 +
</source>
 +
 
 +
For more information, please refer to [[ZK Developer's Reference/Customization/Component Properties#Application-wide_Initialization|Customization: Component Properties]].
 +
 
 +
==Define Components with Java Annotations==
 +
 
 +
Instead of maintaining the definitions in the language addon as described above, you can define the component with Java annotation with a utility called [https://github.com/zanyking/ZK-Composite ZK Composite]. For example,
 +
 
 +
<source lang="java">
 +
@Composite(name="username", macroURI="/WEB-INF/partial/username.zul")
 +
public class Username extends Rows implements IdSpace {
 +
    @Wire
 +
    private Textbox mc_who; //will be wired when Components.wireVariables is called
 +
 +
    //Note: no need to create components and wire variables/components
 +
 
 +
    public String getWho() {
 +
        return mc_who.getValue();
 +
    }
 +
    public void setWho(String who) {
 +
        mc_who.setValue(who);
 +
    }
 +
}
 +
</source>
 +
 
 +
This approach is suggested if you have to develop several composite components. As shown, it is more convenient since you don't have to maintain a separate XML file (the language addon). Furthermore, it will create the components and wire them automatically based on the annotations.
 +
 
 +
Notice that it requires [http://github.com/zanyking/ZK-Composite/downloads additional JAR files], please refer to [[Small Talks/2011/December/Define Composite Component using Java Annotation in ZK6|Small Talks: Define Composite Component using Java Annotation in ZK6]] for the details.
 +
 
 +
 
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 10:26, 5 February 2024


Composite Component


Like a macro component, a composite component is an approach to compose a component based on a template. Unlike a macro component, a composite component has to create and wire the child components by itself, and handle ID space if necessary. The advantage is that a composite component can extend from any component, such as Row, such that it is easier to fit to any situation (and no need for the inline concept).

In short, it is suggested to use a macro component if applicable (since it is easier), while using a composite component otherwise.


If you'd like to assemble UI at runtime (aka., templating), please refer to the Templating section for more information.

Implement a Composite Component

First, you have to decide which component to extend from. Div is a common choice as it is a simple component. However, here our example extends from Row, so it can be used under Rows, which the regular macros cannot.

Second, you have to implement a template (in a ZUML document) to define what child components the composite component has. Then, you have to implement a Java class to put them together.

Implement a Template

The implementation of a template is straightforward. There is nothing special to handle. Since it is rendered by Execution.createComponents(String, Component, Map), you could pass whatever data you prefer to it (through the arg argument).

Suppose we have a template as follows, and it is placed at /WEB-INF/composite/username.zul.

<zk>
  Usename: <textbox id="mc_who"/>
</zk>

Implement a Java Class

To implement a Java class we shall:

  1. Extend from the component class you want.
  2. (Optional) Implement IdSpace to make it an ID space owner.
  3. Render the template in the constructor by the use of Executions.createComponents(String, Component, Map) or others.
  4. (Optional) Wire variables, components and event listeners after rendering with the use of Selectors.wireVariables(Component, Object, List) (wiring variables), Selectors.wireComponents(Component, Object, boolean) (wiring components) and Selectors.wireEventListeners(Component, Object) (wiring event listeners).

For example,

package foo;

import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zul.Row;
import org.zkoss.zul.Textbox;

public class Username extends Row implements IdSpace {
    @Wire
    private Textbox mc_who; //will be wired when Components.wireVariables is called

    public Username() {
        //1. Render the template
        Executions.createComponents("/WEB-INF/composite/username.zul", this, null);

        //2. Wire variables, components and event listeners (optional)
        Selectors.wireVariables(this, this, null);
        Selectors.wireComponents(this, this, false);
        Selectors.wireEventListeners(this, this);
    }
    public String getWho() {
        return mc_who.getValue();
    }
    public void setWho(String who) {
        mc_who.setValue(who);
    }
    //public void onOK() {..} //Add event listeners if required, and wired by Components.addForwards
}

After Executions.createComponents(String, Component, Map) is called, all components specified in the template will be instantiated and become the child components of the composite component (Row). Notice that the URI must match the location of the template correctly.

Depending on the implementation you want, you could wire the data members (mc_who) by calling Selectors.wireComponents(Component, Object, boolean). This method will search all data members and setter methods and wire the component with the same ID. Similarly, Selectors.wireEventListeners(Component, Object) is used to wire event listeners.

For more information, please refer to the Wire Components section and Wire Event the Listeners section sections.


Notice that there is a utility called ZK Composite. With the help of ZK Composite, components are created and wired automatically based on the Java annotations you provide. In other words, Step 3 and 4 are done automatically. For more information, please refer to the Define Components with Java Annotations section.

Wire Spring-managed Beans

Selectors.wireVariables(Component, Object, List) will wire variables that can be resolved by the registered variable resolver. In addition to the variable-resolver directive, you can create any variable resolver manually and pass it as the third argument. Selectors.newVariableResolvers(Class, Class) provides a convenient way to instantiate variable resolvers. For example, let us say we'd like to wire Spring-manage beans, then we can do as follows.

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class Username extends Row implements IdSpace {
    @WireVariable
    private User user;

    public Username() {
        Executions.createComponents("/WEB-INF/composite/username.zul", this, null);

        Selectors.wireVariables(this, this,
            Selectors.newVariableResolvers(getClass(), Row.class));
        Selectors.wireComponents(this, this, false);
        Selectors.wireEventListeners(this, this);
    }
...

Selectors.newVariableResolvers(Class, Class) will look for the @VariableResolver annotation and instantiate it automatically. As shown, we annotate DelegatingVariableResolver to resolve Spring-managed bean.

For more information, please refer to the Wire Variables section.

ID Space

Unless you extend a component that is an ID space owner (such as Window), all child components specified in the template will be in the same ID space as its parent. It might be convenient at the first glance. However, it will cause ID conflict if we have multiple instances of the same composite component. Thus, it is generally suggested to make the composite component a space owner.

It can be done easily by implementing an extra interface IdSpace. No other method needs to be implemented.

public class Username extends Row implements IdSpace {
...

Of course, if you prefer not to have an additional ID space, you don't need to implement IdSpace.

Use Composite Component

Like macros and any other primitive components, you have to declare a composite component before using it. This can be done by using component directives. Then, we could use it the same way (they are actually primitive components). For example,

<?component name="username" extends="row" class="foo.Username"?>

<grid>
    <rows>
      <username who="Joe"/>
      <username who="Hellen"/>
    </rows>
</grid>

Define Composite Components as Standard Components

If a composite component is used in multiple pages, it is better to define it in the application level, such that it can be accessed in any page without any component directives.

There are basic two approaches to define a component in the application level:

  1. Define it in an XML file which is called a language addon.
  2. Define it with Java annotations.

Define Components in a Language Addon

A language addon is an XML file providing additional component definitions or customizing the standard components. For example, you can define the username component described in the previous section as follows.

<language-addon>
    <addon-name>myapp</addon-name>
    <component>
        <component-name>username</component-name>
        <extends>rows</extends>
        <component-class>foo.Username</component-class>
    </component>
</language-addon>

For more information, please refer to Customization: Component Properties.

Define Components with Java Annotations

Instead of maintaining the definitions in the language addon as described above, you can define the component with Java annotation with a utility called ZK Composite. For example,

@Composite(name="username", macroURI="/WEB-INF/partial/username.zul")
public class Username extends Rows implements IdSpace {
    @Wire
    private Textbox mc_who; //will be wired when Components.wireVariables is called
 
    //Note: no need to create components and wire variables/components

    public String getWho() {
        return mc_who.getValue();
    }
    public void setWho(String who) {
        mc_who.setValue(who);
    }
}

This approach is suggested if you have to develop several composite components. As shown, it is more convenient since you don't have to maintain a separate XML file (the language addon). Furthermore, it will create the components and wire them automatically based on the annotations.

Notice that it requires additional JAR files, please refer to Small Talks: Define Composite Component using Java Annotation in ZK6 for the details.




Last Update : 2024/02/05

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