Implementing Data Binding"

From Documentation
 
(11 intermediate revisions by the same user not shown)
Line 3: Line 3:
  
  
In the previous sections, we implemented a "View-Model-Renderer" approach to populate tabular data in components (<javadoc>org.zkoss.zul.Grid</javadoc>, <javadoc>org.zkoss.zul.Listbox</javadoc>). In this section, we explore how to leverage ZK's convenient annotated data binding to achieve the same end.
+
In the previous sections, we implemented a "View-Model-Renderer" approach to populate tabular data into components (<javadoc>org.zkoss.zul.Grid</javadoc>, <javadoc>org.zkoss.zul.Listbox</javadoc>). In this section, we will explore how to leverage ZK's convenient annotated data binding to achieve the same end.
  
 
===The Orders View===
 
===The Orders View===
Line 11: Line 11:
  
 
===How Data Binding and Controller Work Together===
 
===How Data Binding and Controller Work Together===
In the [[ZK Essentials/Displaying information using the Listbox/Using a ListModel and ListitemRenderer|previous section]], we looked at how the controller works in the View-Model-Render approach. Using annotated data binding, we no longer need to wrap a data collection with a ZK utility model class and implementing a renderer, therefore, we could simplify the code in the controller class:
+
In the [[ZK Essentials/Displaying information using the Listbox/Using a ListModel and ListitemRenderer|previous section]], we looked at how the controller worked in the View-Model-Render approach. Using annotated data binding, we no longer need to wrap a data collection with a ZK utility model class and implementing a renderer. We could simplify the code in the controller class:
 
'''DatabindingOrderViewCtrl.java'''
 
'''DatabindingOrderViewCtrl.java'''
 
<source lang="java" highlight="11,17,18,19,20">
 
<source lang="java" highlight="11,17,18,19,20">
Line 63: Line 63:
 
</source>
 
</source>
 
<br>
 
<br>
* At line 11, we save this controller class as a desktop attribute so it could be retrieved anywhere within the desktop scope
+
* At line 11, we save this controller class as a desktop attribute so that it could be retrieved anywhere within the desktop scope
* Since we'll need to supply the annotated data binder the data collection we'll use for the grid, we keep the code at line 17 so the data binder could call this code and fetch the data collection.  
+
* Since we'll need to supply the annotated data binder in the data collection we use for the grid, we keep the code at line 17 so the data binder could call out this code and fetch the data collection.  
 
<br>
 
<br>
 
Now we proceed to applying data binding to the Orders View.
 
Now we proceed to applying data binding to the Orders View.
Line 116: Line 116:
 
===Converting Data Types When Binding===
 
===Converting Data Types When Binding===
  
In Orders View we need to display the purchased date as a String, but the Order object gets us the <javadoc>java.util.Date</javadoc> object only, not a String which could be attached to a label in listcell. <br>
+
In Orders View we need to display the purchased date as a String, but the Order object gets us the <javadoc>java.util.Date</javadoc> object only, not a String which can be attached to a label in listcell. <br>
 
[[Image:ZKEssentials_DisplayInGrid_OrdersList.png]]
 
[[Image:ZKEssentials_DisplayInGrid_OrdersList.png]]
  
Line 148: Line 148:
 
}
 
}
 
</source>
 
</source>
There are two methods that we must implement, one is '''coerceToBean''' (converting data type and save to bean), the other is '''coerceToUi''' (converting data type and load to UI component). In our case, we have the Date object in bean and need to convert that to String and display it in an UI component, so we implement the '''coerceToUi''' method as shown in the snippet above.<br>
+
There are two methods that we must implement, one is '''coerceToBean''' (converting data type and save into bean), the other is '''coerceToUi''' (converting data type and load to UI component). In our case, we have a Date object in bean and need to convert that to String and then display it in an UI component, so we implement the '''coerceToUi''' method as shown in the snippet above.<br>
 
In our UI markup, index.zul, we then include the converter in the databinding annotation as such:
 
In our UI markup, index.zul, we then include the converter in the databinding annotation as such:
 
<source lang="xml">
 
<source lang="xml">
Line 155: Line 155:
  
 
===Getting and Passing a Selected List Item in ZUL===
 
===Getting and Passing a Selected List Item in ZUL===
<javadoc>org.zkoss.zul.Listbox</javadoc> differs from <javadoc>org.zkoss.zul.Grid</javadoc> in that it supports the method <javadoc method="getSelectedItem()">org.zkoss.zul.Listbox</javadoc>. We summarize the key points behind how this works below:
+
<javadoc>org.zkoss.zul.Listbox</javadoc> differs from <javadoc>org.zkoss.zul.Grid</javadoc> in that it supports the method <javadoc method="getSelectedItem()">org.zkoss.zul.Listbox</javadoc>. We summarize the key points to show how this works. See below:
 
# We could call  <javadoc method="getSelectedItem()">org.zkoss.zul.Listbox</javadoc> in ZUL (line 4) using annotations to retrieve the list item selected: '''selectedItem="@{selectedOrder}" '''
 
# We could call  <javadoc method="getSelectedItem()">org.zkoss.zul.Listbox</javadoc> in ZUL (line 4) using annotations to retrieve the list item selected: '''selectedItem="@{selectedOrder}" '''
 
## here '''selectedOrder''' is just an arbitrary variable we declare to reference the '''Order''' data object
 
## here '''selectedOrder''' is just an arbitrary variable we declare to reference the '''Order''' data object
# At line 18, we call the '''Order''' object's '''getItems''' method to retrieve the list of "OrderItem" data objects and assign that list to the grid component as its model.
+
# At line 18, we call the '''Order''' object's '''getItems''' method to retrieve a list of "OrderItem" data objects and assign that list to the grid component as its model.
## '''OrderItem''' is a wrapper for the '''Product''' object, with additional information like the quantity being ordered, the price, and the sub total.
+
## '''OrderItem''' is a wrapper for the '''Product''' object, with additional information such as the quantity being ordered, the price, and the subtotal.
 
# At line 26, we associate each row with each '''OrderItem'''; a row is created automatically for each '''OrderItem'''
 
# At line 26, we associate each row with each '''OrderItem'''; a row is created automatically for each '''OrderItem'''
 
# From line 27 through 30, the getter methods for the OrderItem's properties are called and their values are assigned to the labels.
 
# From line 27 through 30, the getter methods for the OrderItem's properties are called and their values are assigned to the labels.
  
As it's illustrated here, ZK's data binder automatically does a lot of wiring work for us behind the scenes. In the next subsection, we proceed to discuss when is it a good idea to implement data binding and when it is not.
+
As it's illustrated here, ZK's data binder automatically does a lot of wiring work for us behind the scenes. In the next subsection, we will discuss when it is a good idea to implement data binding and when it is not.
  
 
===When to Use Data Binding===
 
===When to Use Data Binding===

Latest revision as of 10:47, 19 July 2011

Stop.png This article is out of date, please refer to http://books.zkoss.org/zkessentials-book/master/ for more up to date information.


In the previous sections, we implemented a "View-Model-Renderer" approach to populate tabular data into components (Grid, Listbox). In this section, we will explore how to leverage ZK's convenient annotated data binding to achieve the same end.

The Orders View

In the Orders View, we present users with all the records of the orders placed. The upper portion of the view, implemented using a Listbox, discloses information on the orders placed. When users click on an item in the list, the details pertaining to the products purchased in that order is shown in the grid below. Users could cancel an order by clicking the button Cancel Selected Order.

ZKEssentials DataBinding OrdersView.png

How Data Binding and Controller Work Together

In the previous section, we looked at how the controller worked in the View-Model-Render approach. Using annotated data binding, we no longer need to wrap a data collection with a ZK utility model class and implementing a renderer. We could simplify the code in the controller class: DatabindingOrderViewCtrl.java

public class DatabindingOrderViewCtrl extends GenericForwardComposer implements OrderCtrl{

	private static final String KEY_ORDER_VIEW_CTRL = "KEY_ORDER_VIEW_CTRL";
	
	private Listbox orderLibox;
	private Button cancelOrderBtn;
	
	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		desktop.setAttribute(KEY_ORDER_VIEW_CTRL, this);
		cancelOrderBtn.setDisabled(true);
	}
	
	//no more model or renderer code needed
	
        public List<Order> getOrders(){
		List<Order> orders = getOrderDAO().findByUser(getCurrentUserId());
		return orders;
	}
	
	private static OrderDAO getOrderDAO(){
		return new OrderDAO();
	}

	public void onClick$cancelOrderBtn(){
		if(orderLibox.getSelectedItem()==null){
			return;
		}
		Order order = (Order) orderLibox.getSelectedItem().getValue();
		cancelOrder(order.getId());
	}

	public void onSelect$orderLibox(){
		if(cancelOrderBtn.isDisabled())
			cancelOrderBtn.setDisabled(false);
	}

	private void cancelOrder(Long orderId) {
		//1. Do the update using DAO
		getOrderDAO().cancelOrder(orderId);
		//2. update UI
		AnnotateDataBinder binder = (AnnotateDataBinder) page.getAttribute("binder");
		binder.loadAll();
	}
	
}


  • At line 11, we save this controller class as a desktop attribute so that it could be retrieved anywhere within the desktop scope
  • Since we'll need to supply the annotated data binder in the data collection we use for the grid, we keep the code at line 17 so the data binder could call out this code and fetch the data collection.


Now we proceed to applying data binding to the Orders View.

Data Binding Grid with a Data Collection

An introduction on data binding with data collection was given in the previous section. We'll apply that technique here:

  • At line 2, we apply our simplified controller class
  • At line 4, we assign the list orders from the controller class to the Listbox as its data model; the annotation orderArea$composer.orders translates to "getting the parent Div component's controller class and run the method getOrders" (the attribute "selectedItem" will be covered in the subsection below)
  • At line 11, we iterate through the data collection and assign the order data object to each Listitem component that's been generated automatically.
  • From line 12 through 14, we get the order object's properties and append it to the labels in the list cells. (the attribute "converter" will be covered in the subsection to follow)

index.zul

<south  size="250px" flex="true" border="0" splittable="true" collapsible="true" style="overflow:scroll">
		<div id="orderArea" style="background:#E6D92C; height:100%" apply="demo.web.ui.ctrl.DatabindingOrderViewCtrl" >
		
		<listbox id="orderLibox" model="@{orderArea$composer.orders}" selectedItem="@{selectedOrder}">
			<listhead>
				<listheader label="info"/>
				<listheader label="description"/>
				<listheader label="Sub Total"/>
			</listhead>
			
			<listitem self="@{each='order'}" value="@{order}">
				<listcell label="@{order, converter='demo.web.ui.OrderInfoTypeConverter'}" />
				<listcell label="@{order.description}" />
				<listcell label="@{order.total}" />
			</listitem>
		</listbox>

		<grid id="orderItemsGrid" model="@{selectedOrder.items}">
			<columns sizable="true">
				<column label="Name"/>
				<column label="Quantity"/>
				<column label="Price"/>
				<column label="Sub Total"/>
		    </columns>
		    <rows>
		    	<row self="@{each='orderItem'}">
		    		<label  value="@{orderItem.name}"/>
					<label value="@{orderItem.quantity}"/>
					<label value="@{orderItem.price}"/>
					<label value="@{orderItem, converter='demo.web.ui.OrderItemSubTotalTypeConverter'}" maxlength="8" />
		    	</row>
		    </rows>
		</grid>
		<button id="cancelOrderBtn" label="Cancel Selected Order"/>
		</div>
	</south>

Converting Data Types When Binding

In Orders View we need to display the purchased date as a String, but the Order object gets us the Date object only, not a String which can be attached to a label in listcell.
ZKEssentials DisplayInGrid OrdersList.png

To convert Date to String, we'll need to implement the TypeConverter interface.
OrderInfoTypeConverter.java

package demo.web.ui;

import org.zkoss.zk.ui.Component;
import org.zkoss.zkplus.databind.TypeConverter;

import com.sun.jna.FromNativeContext;

import demo.model.OrderDAO;
import demo.model.bean.Order;

public class OrderInfoTypeConverter implements TypeConverter {

	// Save 
	public Object coerceToBean(Object val, Component comp) {
		throw new UnsupportedOperationException();
	}

	//Load
	public Object coerceToUi(Object val, Component comp) {
		Order order = (Order) val;
		String info = order.getStatus()+" : "+
		Consts.YYYY_MM_DD_hh_ss.format(order.getCreateDate());
		return info;
	}
}

There are two methods that we must implement, one is coerceToBean (converting data type and save into bean), the other is coerceToUi (converting data type and load to UI component). In our case, we have a Date object in bean and need to convert that to String and then display it in an UI component, so we implement the coerceToUi method as shown in the snippet above.
In our UI markup, index.zul, we then include the converter in the databinding annotation as such:

<listcell label="@{order, converter='demo.web.ui.OrderInfoTypeConverter'}" />

Getting and Passing a Selected List Item in ZUL

Listbox differs from Grid in that it supports the method Listbox.getSelectedItem(). We summarize the key points to show how this works. See below:

  1. We could call Listbox.getSelectedItem() in ZUL (line 4) using annotations to retrieve the list item selected: selectedItem="@{selectedOrder}"
    1. here selectedOrder is just an arbitrary variable we declare to reference the Order data object
  2. At line 18, we call the Order object's getItems method to retrieve a list of "OrderItem" data objects and assign that list to the grid component as its model.
    1. OrderItem is a wrapper for the Product object, with additional information such as the quantity being ordered, the price, and the subtotal.
  3. At line 26, we associate each row with each OrderItem; a row is created automatically for each OrderItem
  4. From line 27 through 30, the getter methods for the OrderItem's properties are called and their values are assigned to the labels.

As it's illustrated here, ZK's data binder automatically does a lot of wiring work for us behind the scenes. In the next subsection, we will discuss when it is a good idea to implement data binding and when it is not.

When to Use Data Binding

As a general rule of thumb, use data binding when:

  • the components do not need to be dynamically created (eg. components created using Executions.createComponents())
  • the data in grid / listbox can be easily fetched using getter methods of the associated data object



Last Update : 2011/07/19

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