Controller"

From Documentation
m
 
(10 intermediate revisions by 4 users not shown)
Line 4: Line 4:
 
The ''controller'' is a Java program that is used to glue UI (view) and Data (model) together.
 
The ''controller'' is a Java program that is used to glue UI (view) and Data (model) together.
  
For a simple UI, there is no need of controller. For example, the data of a <javadoc>org.zkoss.zul.Listbox</javadoc> could be abstracted by implementing <javadoc type="interface">org.zkoss.zul.ListModel</javadoc>.
+
A simple UI does not require any controllers. For example, the data of a <javadoc>org.zkoss.zul.Listbox</javadoc> could be abstracted by implementing <javadoc type="interface">org.zkoss.zul.ListModel</javadoc> as described in [[ZK Developer's Reference/MVC/Model|the Model section]].
  
For typical database access, the glue logic (i.e., control) can be handled by a generic feature called [[ZK Developer's Reference/Data Binding|Data Binding]]. In other words, the read and write operations can be handled automatically by a generic Data Binding, and you don't need to write the glue logic at all.
+
For typical database access, the glue logic (i.e., controller) can be handled by a generic feature called [[ZK Developer's Reference/MVVM/Data Binding|Data Binding]]. In other words, the create, read, update and delete operations (CRUD) can be handled automatically by a generic Data Binding mechanism, and you don't need to write the glue logic at all as described in [[ZK Developer's Reference/MVVM/Data Binding|the Data Binding section]].
  
In this section we will discuss how to implement a custom controller (aka., a composer).
+
If none of the above fulfills your requirement, you could implement a custom controller (which is called a composer in ZK terminology). In the following sections we will discuss how to implement a custom controller in details.
  
 
{{ZKDevelopersReferenceHeadingToc}}
 
{{ZKDevelopersReferenceHeadingToc}}
 
=Custom Controller=
 
 
A custom controller is also know as a composer (in ZK).  To implement a composer, you could extend from <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc>, or implement <javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc> from scratch. Then, specify it in the element it wants to handle in a ZUML document.
 
 
To implement the ''logic'' to glue UI and data, a composer usually do:
 
 
*Post-process components after ZK Loader renders a ZUML document. It can be done by overriding <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc>.
 
*Handle events and manipulate components if necessary.
 
 
In additions, a composer can be used to involve the lifecycle of ZK Loader for doing:
 
 
*Exception handling
 
*Component Instantiation monitoring and filtering
 
 
A composer be configured as system-level, such that it will be called when ZK Loader has processed a ZUML document.
 
 
=GenericForwardComposer=
 
 
Implementing <javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc> is straightfoward: just override <javadoc type="interface" method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc> and do whatever you want.
 
 
However, it is suggested to extend from <javadoc>org.zkoss.zk.ui.GenericForwardComposer</javadoc> since the default implementation of <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> wires variables and event listener automatically.
 
 
For example,
 
 
<source lang="java">
 
package foo;
 
import org.zkoss.zk.ui.Component;
 
import org.zkoss.zk.ui.util.GenericForwardComposer;
 
import org.zkoss.zul.*;
 
 
public class MyComposer extends GenericForwardComposer {
 
    Textbox input;
 
    Label output;
 
 
    void onClick$submit() {
 
        output.setValue(input.getValue());
 
    }
 
    void onClick$reset() {
 
        output.setValue("");
 
    }
 
}
 
</source>
 
 
where <code>input</code> will be wired to a fellow named <code>input</code>, and <code>onClick$submit</code> will be registered as an event listener for an event named <code>onClick</code> and to a fellow named <code>submit</code>.
 
 
To associate a composer to a component, just specify the [[ZUML Reference/ZUML/Attributes/apply|apply attribute]] to the element you want to control. For example,
 
 
<source lang="xml">
 
<grid apply="foo.MyComposer">
 
    <rows>
 
        <row>
 
            <textbox id="input"/>
 
            <button label="Submit" id="submit"/>
 
            <button label="Reset" id="reset"/>
 
        </row>
 
    </rows>
 
</grid>
 
</source>
 
 
If you have to post-process the components after ZK Loader initializes them, you could override <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc>. It is important to call back <code>super.doAfterCompose(comp)</code>. Otherwise, the wiring won't work.
 
 
<source lang="java">
 
public void doAfterCompose(Component comp) {
 
  super.doAfterCompose(comp); //wire variables and event listners
 
  //do whatever you want (you could access wired variables here)
 
}
 
</source>
 
 
where <code>comp</code> is the component that the composer is applied to. In this example, it is the grid. As the name indicates, <code>doAfterCompose</code> is called after the grid and all its descendants are instantiated.
 
 
==The apply Attribute==
 
 
If you could specify multiple composers, just separate them with comma. They will be called from left to right.
 
 
In additions to the class name, you could specify an instance too. For example, suppose you have an instance called <code>fooComposer</code>, then
 
 
<source lang="xml">
 
<grid apply="${fooComposer}">
 
</source>
 
 
If a class name is specified, each time the component is instantiated, an instance of the class is instantiated too. Thus, you don't have to worry about the concurrency issue. However, if you specify an instance, it will be used directly. Thus, you have to either create an instance for each request, or make it thread-safe.
 
 
=Composer with More Control=
 
 
A composer could also handle the exceptions, if any, involve the life cycle of rendering, and monitor and even control how a child component is instantiated. It can be done by implementing the corresponding interfaces.
 
 
== Exception and Lifecycle Handling with ComposerExt ==
 
 
If you want a composer to handle the exception and/or involve the life cycle of rendering, you could also implement <javadoc type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc>. Since <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> already implements this interface<ref>In ZK 5, GenericForwardComposer implements ComposerExt. However, it does ''not'' in ZK 6 (for better performance), so it is better to implement it if you need it.</ref>, you only need to override the method you care if you extends from it.
 
 
For example, we could handle the exception by overriding <javadoc method="doCatch(java.lang.Throwable)">org.zkoss.zk.ui.util.ComposerExt</javadoc> and/or <javadoc method="doFinally()">org.zkoss.zk.ui.util.ComposerExt</javadoc>.
 
 
<source lang="xml">
 
public class MyComposer extends GenericForwardComposer implements ComposerExt {
 
    public boolean doCatch(Throwable ex) {
 
        return _ignorable(ex); //return true if ex could be ingored
 
    }
 
}
 
</source>
 
 
For involving the life cycle, you could override <javadoc method="doBeforeCompose(org.zkoss.zk.ui.Page, org.zkoss.zk.ui.Component, org.zkoss.zk.ui.metainfo.ComponentInfo)" type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc> and/or <javadoc method="doBeforeComposeChildren(org.zkoss.zk.ui.Component)" type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc>.
 
 
<blockquote>
 
----
 
<references/>
 
</blockquote>
 
 
== Fine-grained Full Control with FullComposer ==
 
 
In addition to controlling the give component, a composer can monitor the instantiation and exceptions for each child and descendant component. It can be done by implementing <javadoc type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc>. <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> does not implement this interface by default. Thus, you have to implement it explicitly.
 
 
There is no method need to implement in this interface. It is like a decorative interface to indicate that it requires the fine-grained full control. In other words, all methods declared in <javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc> and <javadoc type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc> will be invoked one-by-one against each child and descendant component.
 
 
For example, suppose we have a composer implementing both <javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc> and <javadoc type="interface">org.zkoss.zk.ui.util.FullComposer</javadoc>, and it is assigned as followed
 
 
<source lang="java">
 
<panel apply="foo.MyComposer">
 
    <div>
 
        <datebox/>
 
        <textbox/>
 
    </div>
 
</panel>
 
</source>
 
 
then, <javadoc type="interface" method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc> will be called for datebox, textbox, div and then panel (in the order of ''child-first-parent-last''). If <javadoc type="interface">org.zkoss.zk.ui.util.FullComposer</javadoc> is not implemented, only panel will be called.
 
 
=System-level Composer=
 
 
You could register a system-level composer, such that it will be notified for every ZUML document being rendered.
 
 
It could be done by specifying the composer you implemented in <code>WEB-INF/zk.xml</code><ref>For more information, please refer to [[ZK Configuration Reference/zk.xml/The listener Element|ZK Configuration Reference]]</ref>:
 
 
<source lang="xml">
 
<listener>
 
    <listener-class>foo.MyComposer</listener-class>
 
</listener>
 
</source>
 
 
Each time a ZK page, including ZK pages and richlets, is created, ZK will instantiate one instance for each registered system-level composer and the invoke the <tt>doAfterCompose</tt> method with each root component. The system-level composer is usually used to post-process ZK pages, such as adding a trademark. If you want to process only certain pages, you can check the request path by calling <javadoc method="getRequestPath()" type="interface">org.zkoss.zk.ui.Desktop</javadoc> (the desktop instance can be found thru the give component).
 
 
If the system-level composer also implements <javadoc type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc>, it can be used to handle more situations, such as exceptions, like any other composer can do.
 
 
If the system-level composer also implements <javadoc type="interface">org.zkoss.zk.ui.util.FullComposer</javadoc>, it will be invoked when each component is created. It provides the finest grain of control but a wrong implementation might degrade the performance.
 
 
Notice that since a new instance of the composer is created for each page, there is no concurrency issues.
 
 
<blockquote>
 
<references/>
 
</blockquote>
 
 
== Richlet ==
 
 
A system-level composer can implement <javadoc type="interface">org.zkoss.zk.ui.util.ComposerExt</javadoc> to handle exceptions for a richlet, such as <tt>doCatch</tt> and <tt>doFinally</tt>. However, <tt>doBeforeCompose</tt> and <tt>doBeforeComposeChildren</tt>  won't be called.
 
 
<javadoc type="interface">org.zkoss.zk.ui.util.FullComposer</javadoc> is not applicable to richlets. In other words, system-level composers are called only for the root components.
 
 
=Version History=
 
Last Update : {{REVISIONYEAR}}/{{REVISIONMONTH}}/{{REVISIONDAY}}
 
{| border='1px' | width="100%"
 
! Version !! Date !! Content
 
|-
 
| &nbsp;
 
| &nbsp;
 
| &nbsp;
 
|}
 
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 03:27, 29 January 2024

Overview

The controller is a Java program that is used to glue UI (view) and Data (model) together.

A simple UI does not require any controllers. For example, the data of a Listbox could be abstracted by implementing ListModel as described in the Model section.

For typical database access, the glue logic (i.e., controller) can be handled by a generic feature called Data Binding. In other words, the create, read, update and delete operations (CRUD) can be handled automatically by a generic Data Binding mechanism, and you don't need to write the glue logic at all as described in the Data Binding section.

If none of the above fulfills your requirement, you could implement a custom controller (which is called a composer in ZK terminology). In the following sections we will discuss how to implement a custom controller in details.





Last Update : 2024/01/29

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