Data binding

From Documentation

Jump to: navigation, search

Contents

Basic Concept

Data binding is a mechanism that automates the data-copy plumbing code between UI components and the data source. Application developers only have to tell data binding manager about the associations between UI components and the data source. Then, data -binding manager will do the loading (load data from the data source to UI components) and saving (save data from UI component into the data source) job automatically.

Adding a Data Source

First of all, we have to define a data source as a data bean. In this example, we use Person class as an example that holds the information of a person, say, first name, and last name.

Person.java

public class Person {
	private String _firstName = "";
	private String _lastName = "";
 
	// getter and setters
	public void setFirstName(String firstName) {
		_firstName = firstName;
	}
 
	public String getFirstName() {
		return _firstName;
	}
 
	public void setLastName(String lastName) {
		_lastName = lastName;
	}
 
	public String getLastName() {
		return _lastName;
	}
 
	public void setFullName(String f) {
		// do nothing.
	}
 
	public String getFullName() {
		return _firstName + " " + _lastName;
	}
}

Then, declare a data source in the page as follows,

<zscript><![CDATA[
	//prepare the example person object
	Person person = new Person();
	person.setFirstName("Tom");
	person.setLastName("Hanks");
]]>
</zscript>

Activates Data Binding Manager

Activates Data Binding Manager by defining the page Initializer at the top of the page.

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

This initiator class do following things:

  1. Creates an AnnotateDataBinder instance.
  2. Sets the AnnotateDataBinder instance as a variable with the name "binder" stored in the component as specified in arg0 "component-path".(if arg0 is not specified, use Page instead.)
  3. Calls DataBinder.loadAll() to initiate all UI components from the associated data source.

Associate UI Components with Data Source

After adding source data, activating data-binding manager, you have to define required UI objects, and associate them with the data source. Use ZUML annotation expression to tell data-binding manager the relationship between the data source and UI components. Its usage is very straightforward, simply declare the annotation into the component's attribute directly.

<component-name attribute-name="@{bean-name.attribute-name}"/>

  • component-name represents a UI component
  • attribute-name represents an attribute of UI component or the data source
  • bean-name represents a data source

We use Grid as an example, and associate it with the data source, a Person instance. In this example, data-binding manager will automates the synchronization between UI components and the data source automatically.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
 
<window>
	<zscript><![CDATA[
		//prepare the example person object
		Person person = new Person();
		person.setFirstName("George");
		person.setLastName("Bush");
	]]>
	</zscript>
 
	<grid width="400px">
		<rows>
			<row> First Name: <textbox value="@{person.firstName}"/></row>
			<row> Last Name: <textbox value="@{person.lastName}"/></row>
			<row> Full Name: <label value="@{person.fullName}"/></row>
		</rows>
	</grid>
</window>

When to Load Data from Data Source to UI

Data Binding Manager is triggered by events, or users' activities. Thus, you must specify events in the ZUML annotation expression with load-when tag expression to tell Data Binding Manager when to load data from data source into the component's attribute.

<component-name attribute-name="@{bean-name.attribute-name,

load-when='component-id.event-name'}"/>

  • component-id represents the ID of a UI component
  • event-name represents the event name

Multiple definition is allowed and would be called one by one.

In the following example, we demonstrate an example that the fullname of a Person will be updated automatically once his/her first name or last name has been modified.

Data Binding Manager will re-load value of Label whose id is fullName, from person.fullName when the either the value of Textbox whose id is firstName or lastName has been changed, in other words, onChange event is triggered.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
 
<window>
	<zscript><![CDATA[
	//prepare the example person object
		Person person = new Person();
		person.setFirstName("George");
		person.setLastName("Bush");
	]]>
	</zscript>
 
	<grid width="400px">
		<rows>
			<row> First Name: 
				<textbox id="firstName" value="@{person.firstName}"/>
			</row>
			<row> Last Name: 
				<textbox id="lastName" value="@{person.lastName}"/>
			</row>
			<row> Full Name: 
				<label id="fullName" value="@{person.fullName,
				load-when='firstName.onChange,lastName.onChange'}"/>
			</row>
		</rows>
	</grid>
</window>

When to Save Data from UI Components to the Data Source

Data Binding Manager is triggered by events, or users' activities. Thus, you must specify events in the ZUML annotation expression with save-when tag expression to tell Data Binding Manager when to save the attribute of the component into the data source.

<component-name attribute-name="@{bean-name.attribute-name,save-when='component-id.event-name''}"/>


  • component-id represents the ID of a UI component
  • event-name represents the event name

Multiple definition is allowed and would be called one by one.

In the following example, Data Binding Manager will save the attribute "value" of Textbox "firstName" into "person.firstName" when the Textbox itself fires "onChange" event.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
 
<window width="500px">
	<zscript><![CDATA[
		Person person = new Person();
		person.setFirstName("Bill");
		person.setLastName("Gates");
	]]>
	</zscript>
 
	<listbox>
		<listhead>
			<listheader label="First Name" width="100px"/>
			<listheader label="Last Name" width="100px"/>
			<listheader label="Full Name" width="100px"/>
		</listhead>
		<listitem>
			<listcell>
				<textbox id="firstName" value="@{person.firstName, save-when='self.onChange'}"/>
			</listcell>
			<listcell>
				<textbox id="lastName" value="@{person.lastName, save-when='self.onChange'}"/>
			</listcell>
			<listcell label="@{person.fullName}"/>
		</listitem>
	</listbox>
</window>

Associate the Same Data Source with Multiple UI Components

One data source could be associated with multiple UI components. Once the data source had been modified, those associated UI components will be updated automatically by Data Binding Manager.

In the following example. we use ZUML annotation expression to associate a data source, a Person instance, "selected" with multiple UI components, including Listbox, and Grid. Once the user selects an item in the Listbox, the Grid will display information of the selected person accordingly.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
 
<window width="500px">
	<zscript><![CDATA[
		//prepare the example person
		Person selected = new Person();
	]]>	
	</zscript>
 
	<listbox rows="4" selectedItem="@{selected}">
		<listhead>
			<listheader label="First Name" width="100px" />
			<listheader label="Last Name" width="100px" />
			<listheader label="Full Name" width="100px" />
		</listhead>
		<listitem>
			<listcell label="George" />
			<listcell label="Bush" />
		</listitem>
		<listitem>
			<listcell label="Bill" />
			<listcell label="Gates" />
		</listitem>
	</listbox>
	<!-- show the detail of the selected person -->
	<grid>
		<rows>
			<row>
				First Name:
				<textbox value="@{selected.firstName}" />
			</row>
			<row>
				Last Name:
				<textbox value="@{selected.lastName}" />
			</row>
		</rows>
	</grid>
</window>

Associate UI Components with a Collection

It can be very useful to associate a collection with a UI components, and Data Binding Manager will convert the collection into UI components accordingly.

  1. Prepare the data source of Collection
  2. Associate the collection with model attribute of those supported UI components, ex. Listbox, Grid, and Tree.
  3. Define a template of UI component
    1. Define a variable, whatever you want, to represent each instance in the Collection with self attribute.<component-name self="@{each='variable-name'}"/> The variable-name could only be seen by component-name and its child components.
    2. Associate UI components with the variable
      <component-name attribute-name="@{variable-name.attribute-name}"/>

In the following example, we demonstrate how to associate a collection with Listbox to display a list of persons.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
 
<window width="500px">
	<zscript><![CDATA[ 
     //prepare the example persons List
		int count = 30;
		List persons = new ArrayList();
		for(int j= 0; j &lt; count; ++j) {
			Person personx = new Person();
			personx.setFirstName("Tom"+j);
			personx.setLastName("Hanks"+j);      
			persons.add(personx);
		}
     ]]>
 	</zscript>
 
	<listbox rows="4" model="@{persons}">
		<listhead>
			<listheader label="First Name" width="100px" />
			<listheader label="Last Name" width="100px" />
			<listheader label="Full Name" width="100px" />
		</listhead>
		<!-- define variable person here-->
		<listitem self="@{each='person'}">
			<listcell>
				<textbox value="@{person.firstName}" />
			</listcell>
			<listcell>
				<textbox value="@{person.lastName}" />
			</listcell>
			<listcell label="@{person.fullName}" />
		</listitem>
	</listbox>
</window>

Customization of Conversion between the Data Source and UI Components

If you want to do the conversion between the Data Source and UI components by yourself, you could specify the class name of the converter in the converter tag expression to tell Data Binding Manager to use your own way to do the conversion between the data source and UI components.

<component-name attribute-name="@{bean-name.attribute- name,converter='class-name'}"/>


Multiple definition is NOT allowed and the later defined would override the previous defined one.

  1. Define a class that implements TypeConverter with the following methods
  • coerceToUI, converts an value object into UI component attribute type.
  • coerceToBean, converts an value object to bean property type.
  1. Specify the class name of converter into the converter tag expression
    In the following example, we demonstrate you how to convert a boolean value into different images instead of pure text.
    First of all, you have to define a class that implements TypeConverter. myTypeConverter converts the boolean into different images accordingly.
import org.zkoss.zkplus.databind.TypeConverter;
import org.zkoss.zul.Listcell;
 
public class myTypeConverter implements TypeConverter {
	public Object coerceToBean(java.lang.Object val,
			org.zkoss.zk.ui.Component comp) {
		return null;
	}
 
	public Object coerceToUi(java.lang.Object val,
			org.zkoss.zk.ui.Component comp) {
		boolean married = (Boolean) val;
		if (married)
			((Listcell) comp).setImage("/img/true.png");
		else
			((Listcell) comp).setImage("/img/false.png");
		return null;
	}
}

Specify myTypeConverter with the convert tag expression to be associated with the married attribute of Person instance.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
 
<window width="500px">
	<zscript><![CDATA[
     //prepare the example persons List
     List persons = new ArrayList();
     persons.add(new Person("Tom", "Yeh", true));
     persons.add(new Person("Henri", "Chen", true));
     persons.add(new Person("Jumper", "Chen", false));
     persons.add(new Person("Robbie", "Cheng", false));
     ]]>
	</zscript>
 
	<listbox rows="4" model="@{persons}">
		<listhead>
			<listheader label="First Name" width="100px" />
			<listheader label="Last Name" width="100px" />
			<listheader label="Married" width="100px" />
		</listhead>
		<listitem self="@{each=person}">
			<listcell label="@{person.firstName}" />
			<listcell label="@{person.lastName}" />
			<listcell
				label="@{person.married, converter='myTypeConverter'}" />
		</listitem>
	</listbox>
</window>

Define the Access Privilege of Data Binding Manager

For better control of data-binding manager, you can set the access mode of the attribute-name of the component-name to be both(load/save), load(load Only), save(save Only), or none(neither) .

<component-name attribute-name="@{bean-name.attribute-name,access='type-name'}"/>

  • type-name represents a certain kind of access mode

Multiple definition is NOT allowed and the later defined would override the previous defined one. 

In the following example, if the value of Textbox, "firstName", and "lastName" has been modified, the value of Listcell, "fullname", will remain unchanged because Data Binding manager is informed not to update its value.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
 
<window width="500px">
	<zscript><![CDATA[  
	    Person person = new Person();
	    person.setFirstName("Bill");
	    person.setLastName("Gates");
    ]]>
	</zscript>
 
	<listbox>
		<listhead>
			<listheader label="First Name" width="100px" />
			<listheader label="Last Name" width="100px" />
			<listheader label="Full Name" width="100px" />
		</listhead>
		<listitem>
			<listcell>
				<textbox id="firstName" value="@{person.firstName}" />
			</listcell>
			<listcell>
				<textbox id="lastName" value="@{person.lastName}" />
			</listcell>
			<listcell id="fullName"
				label="@{person.fullName, access='none'}" />
		</listitem>
	</listbox>
</window>


See Also

From Java Doc:

From ZK Forum:


From ZK Smalltalk: