Zero Code Data Binding with ZUML Annotations

From Documentation
DocumentationSmall Talks2006DecemberZero Code Data Binding with ZUML Annotations
Zero Code Data Binding with ZUML Annotations

Author
Henri Chen, Principal Engineer, Potix Corporation
Date
December 18, 2006
Version
Applicable to ZK 2.2 and later.


The Purpose

As discussed in the previous small talk Two-way Data Binding with ZUML Annotations, we can simplify the tedious data copy plumbing codes to some simple load and save methods of data binding manager by annotate compoments with ZUML annotations. In this article, the ZK team wants to show you one step further. The newer improved data binding manager, org.zkoss.zkplus.databind.AnnotateDataBinder (ready since ZK 2.2.0), will allow designing zero code data binding with ZUML annotations.

The Previous Annotated Example

Let us take a look of the previous annotated person2.zul example first.


person2.zul

<window id="mainwin" xmlns:a="http://www.zkoss.org/2005/zk/annotation">
  <zscript>
    //prepare the example person object
    Person person = new Person();
    person.setFirstName("Hello");
    person.setLastName("ZK");
  </zscript>
  
  <grid width="400px">
  <rows>
  <row>
    First Name:
    <a:bind value="person.firstName"/>
    <textbox id="firstName" onChange="update();"/>
  </row>

  <row>
    Last Name:
    <a:bind value="person.lastName"/>
    <textbox id="lastName" onChange="update();"/>
  </row>
  
  <row>
    Full Name:
    <a:bind value="person.fullName"/>
    <label id="fullName"/>
  </row>
  </rows>
  </grid>
  
  <zscript>
    //prepare the AnnotateDataBinder
    AnnotateDataBinder binder = new AnnotateDataBinder(mainwin);
    binder.bindBean("person", person);

    //initialize UI components
    binder.loadAll();
    
    void update() {
      //copy UI components values to data bean properties in one method call.
      binder.saveAll();
      
      //load an UI component value from data bean property in one method call.
      binder.loadComponent(fullName);
    }
  </zscript>
</window>


The Issues And The Solutions

Observe the above example code, you can find that something should be done by the application programmer rather than zuml page designers (the codes within <zscript/> and onChange)

1. Preparing the backend Person bean: It is developers' decision to write that in a ZUML page or in a seperate Java file. The question is really that how we bind the annotation's beanid "person" to this backend Person bean? No doubt the best place to put this backend bean is the ZUML page's variable map such that no matter the code is written as a zscript or as a seperate Java code, the programmer can have access to it.

2. Preparing the data binder and initializing ZUML page: We have to new a data binder and then call data binder's loadAll method to initialize the UI components of the ZUML page from the backend Person bean. Apparently, this is a must have codes for each data binding page. We wrap it as a page Initializer, org.zkoss.zkplus.databind.AnnotateDataBinderInit. The AnnotateDataBinderInit class will new an AnnotateDataBinder, call it's loadAll method to initilize the UI components of the page and set it as a variable "binder".

3. Copying UI components values to data bean properties: We have to write "onChange" event listener for Textbox components "firstName" and "lastName". The new improved AnnotateDataBinder will automatically add event listeners to save component value to the associated backend bean property.

4. To load the changed bean properties back to component: We have to write such codes within the same onChange event listener. This is a tough one since it is application dependent. However, we find a way to make it work automatically. We will load those components that has bound to the same bean id as those that just been saved. For example, when the onChange event listener save the new Textbox value to the person.firstName, the components associated to person.lastName and person.fullName will be loaded automatically because they associates to the same bean id "person"


So here is the revised Annotated Example:

person3.zul

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<window id="mainwin" xmlns:a="http://www.zkoss.org/2005/zk/annotation"
  <grid width="400px">
  <rows>
  <row>
    First Name:
    <a:bind value="person.firstName"/>
    <textbox/>
  </row>

  <row>
    Last Name:
    <a:bind value="person.lastName"/>
    <textbox/>
  </row>
  
  <row>
    Full Name:
    <a:bind value="person.fullName"/>
    <label/>
  </row>
  </rows>
  </grid>
  
  <zscript>
    //prepare the example person object
    Person person = new Person();
    person.setFirstName("Hello");
    person.setLastName("ZK");
  </zscript>
</window>


If you don't like zscript embedded in ZUML page, you can extends the AnnotateDataBinderInit, say PersonInit, and prepare the Person bean there:

PersonInit.java

public class PersonInit extends org.zkoss.zkplus.databind.AnnotateDataBinderInit {
    //override this
    public void doAfterCompose(Page page) {
        //prepare the example person object
        Person person = new Person();
        person.setFirstName("Hello");
        person.setLastName("ZK");
        
        //bind Person bean to beanid "person"
        page.setVariable("person", person); 
        
        //remember to call the super
        super.doAfterCompose(page); 
    }
}


person4.zul

<?init class="PersonInit" ?>
<window id="mainwin" xmlns:a="http://www.zkoss.org/2005/zk/annotation"
  <grid width="400px">
  <rows>
  <row>
    First Name:
    <a:bind value="person.firstName"/>
    <textbox/>
  </row>

  <row>
    Last Name:
    <a:bind value="person.lastName"/>
    <textbox/>
  </row>
  
  <row>
    Full Name:
    <a:bind value="person.fullName"/>
    <label/>
  </row>
  </rows>
  </grid>
</window>

Test it

The example code is here.

Person.gif


Summary

This article shows the new improved AnnotateDataBinder. You can use the facility provided to annotate the ZUML page and automate data bindings in an even simpler way. As you can see, no needs to write the onChange event listeners and the ZUML page data binder initialization is wrapped into only one line of processing instruction. All beans can be looked up in the variable map so the new AnnotateDataBinder can even access to the native Spring beans as well (when used with DelegatingVariableResolver).

We have talked enough the simple one-to-one data bindings. In the next data binding small talk, we will discuss more complex cases that will bind a Collection of object beans to the ZK Listbox component.




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