Improved AnnotateDataBinder Initializer

From Documentation
DocumentationSmall Talks2010JulyImproved AnnotateDataBinder Initializer
Improved AnnotateDataBinder Initializer

Author
Herbert Daume, DIMOCO GmbH, Austria


Herbert profilfoto.jpg
Herbert has been a professional JAVA programmer for several years. At present he is responsible for the application software department of DIMOCO Austria. One of his main tasks is to use sophisticated state-of-the-art technologies to underscore DIMOCO as a competent technology leader. This is the reason why ZK is used for administration and call-center interfaces of DIMOCO services.

Date
July 10, 2010
Version
ZK 3.6.3

Introduction

To use annotated data binding in your ZK project is very easy; first you need to activate the data binding manager with a page Initializer in your *.zul file:

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>

and then you are free to use data binding like

<row> First Name: <textbox value="@{person.firstName}"/></row>

But if you have more than one Window using this data binder configuration on your page you will encouter following Exception:

org.zkoss.zk.ui.UiException: Page is already covered by another Data Binder. Cannot be covered by this Data Binder again. Page:z_u0_0
	at org.zkoss.zkplus.databind.AnnotateDataBinderInit.doAfterCompose(AnnotateDataBinderInit.java:123) [zkplus-3.6.3.jar!/:3.6.3]

This is because the data binder Initializer binds on the Page Component if no other Component specified. To avoid this Exception, you are free to bind the data binder on the Window Component like this:

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="activewin" ?>
<window id="activewin" xmlns="http://www.zkoss.org/2005/zul"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		width="600px" closable="false" maximizable="true" border="normal" title="My very own databinder window" position="top, left" mode="overlapped"
		xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">

Now every thing works fine until you want to open the window more than once, then you will encounter an Exception like this:

Caught Exception
org.zkoss.zk.ui.UiException: Not unique in the ID space of [Page z_1j_0]
	at org.zkoss.zk.ui.AbstractComponent.checkIdSpaces(AbstractComponent.java:304) [zk-3.6.3.jar!/:3.6.3]
	at org.zkoss.zk.ui.AbstractComponent.setId(AbstractComponent.java:615) [zk-3.6.3.jar!/:3.6.3]

Now you are in a dilemma: you need to bind the data binder on your window, but you may not provide a static ID as reference for the data binder when you are planning to open the window more than once. There is currently no solution in standard ZK 3.6.3 for this.

The background

Let's have a closer look at the AnnotateDataBinderInit of ZK 3.6.3. When you decompile its class file, you can see that it is implementing the Initiator and InitiatorExt interface, and as such, is implementing some required methods. Two of them are

 public void doInit(Page page, Map args);
 public void doAfterCompose(Page page, Component comps[]) throws Exception;

When you investigate these methods you will find out that doInit(...) is setting and verifying the name to bind the data binder, but nothing more. There is no instantiating of a data binder yet. In doAfterCompose(...) this previously determined name is then used for instantiating the data binder on the given component.

The solution

Now we have all the information we need to write our very own data binder initializer. The primary requirement for this new initializer is no need for supplying an ID to bind to a Window component. Furthermore, this new initializer has to bind automatically to the window without stating the window identifier.

public class WindowDataBinderInit implements Initiator, InitiatorExt {

    @SuppressWarnings("unchecked")
    public void doInit(Page page, Map args) throws Exception {}

    public void doAfterCompose(Page page) throws Exception {}

    @Override
    public void doAfterCompose(Page page, Component[] components) throws Exception {
        for ( Component c : components ) {
            if ( c instanceof Window ) {
                AnnotateDataBinder binder = new AnnotateDataBinder(c, true);
                c.setVariable("binder", binder, true);
                binder.loadAll();
            }
        }
    }

    public boolean doCatch(Throwable throwable) throws Exception {}

    public void doFinally() throws Exception {}
}

And that's it ! This initializer searches in the AfterCompose stage for Window components. Since you are initializing for a Window component, the components array shall have one element only, the newly created Window component. A new data binder is created, saved as variable in the WIndow component, and the loading from the datasources for all components in the Window is initiated.

To use this data binder initializer just replace the page initializer in your *.zul file

<?init class="WindowDataBinderInit" ?>

and now you are free to create Window components as much as you like.

Comments



Copyright © DIMOCO GmbH. This article is licensed under GNU Free Documentation License.