Using a ListModel and ListitemRenderer"

From Documentation
Line 10: Line 10:
 
===Where the View, Model, and Renderer Come Together===
 
===Where the View, Model, and Renderer Come Together===
  
To associate our Shopping cart view (<javadoc>org.zkoss.zul.Listbox</javadoc> component) with a Model and a Renderer, we'll implement a controller class which extends the ZK utility class <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc>. In its <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> method, the ZK components declared in mark up are wired with the component instances declared in the controller for our manipulation, while the events fired are automatically forwarded to this controller for event handling. Therefore, in our controller class, we override and call <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> method of its super class and implement the code that assigns a model and renderer to our Shopping cart <javadoc>org.zkoss.zul.Listbox</javadoc>.
+
To associate our Shopping cart view (<javadoc>org.zkoss.zul.Listbox</javadoc> component) with a Model and a Renderer, we'll implement a controller class which extends the ZK utility class <javadoc>org.zkoss.zk.ui.util.GenericForwardComposer</javadoc>. With its <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> method, the ZK components declared in mark up are wired with the component instances declared in the controller for our manipulation, while the events fired are automatically forwarded to this controller for event handling. Therefore, in our controller class, we override and call <javadoc method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.GenericForwardComposer</javadoc> method of its super class and implement the code that assigns a model and renderer to our Shopping cart <javadoc>org.zkoss.zul.Listbox</javadoc>.
  
 
'''ProductViewCtrl.java'''
 
'''ProductViewCtrl.java'''

Revision as of 04:13, 23 June 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.


As previously addressed, presentation of data can be done using appropriate renderers or databinding. In this case, as the information is very dynamic, we choose to use the rendering method. For more information on choosing databinding and renderers, please visit this section.

The ListModel and ListitemRenderer work in a similar manner to the RowRenderer.

ZKEss model.png


Where the View, Model, and Renderer Come Together

To associate our Shopping cart view (Listbox component) with a Model and a Renderer, we'll implement a controller class which extends the ZK utility class GenericForwardComposer. With its GenericForwardComposer.doAfterCompose(Component) method, the ZK components declared in mark up are wired with the component instances declared in the controller for our manipulation, while the events fired are automatically forwarded to this controller for event handling. Therefore, in our controller class, we override and call GenericForwardComposer.doAfterCompose(Component) method of its super class and implement the code that assigns a model and renderer to our Shopping cart Listbox.

ProductViewCtrl.java

public class ProductViewCtrl extends GenericForwardComposer {

	private Listbox shoppingCartListbox;
	
 	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		//create model
        shoppingCartListbox.setModel(...); //assign model to Grid

		//create renderer
        shoppingCartListbox.setItemRenderer(...);//assign renderer to Grid
		
        ...
	}
}
<div style="background:#E6D92C; height:100%" apply="demo.web.ui.ctrl.ShoppingCartViewCtrl">
		
			<listbox id="shoppingCartListbox">
				<listhead sizable="true">
					<listheader label="Name" />
					<listheader label="Price" />
					<listheader label="Amount" />
					<listheader label="subTotal"/>
					<listheader align="center"/>
				</listhead>
				<listfoot>
					<listfooter span="2" id="subtotalFooter"></listfooter>
					<listfooter><button id="submitOrderBtn" label="submit"/></listfooter>
					<listfooter><button id="clearBtn" label="clear"/></listfooter>
				</listfoot>
			</listbox>
			<textbox id="descTxb" rows="10" width="200px" value="Note for this order." />
			<image id="cartItemImage" />
		</div>

Using an ListitemRenderer to Populate Data

To populate the data we create an anonymous ListitemRenderer class and implement its render method in a similar manner to the RowRenderer.

From here we know that the data passed to the methodis a CartItem as set in our model. We need to populate 5 columns and therefore need to create 5 Listcells. The first 3 items can be extracted from the data bean and easily placed into Listcells, however, the last two items consist of a sub total and a cancel button.

To implement the subtotal and cancel button we create two private functions in our anonymous class named initSubTotal and initCancelBtn respectively.

            shoppingCartListbox.setItemRenderer(new ListitemRenderer() {
			public void render(Listitem listItem, Object data) throws Exception {
				
				final CartItem cartItem = (CartItem)data;
				final Product prod = cartItem.getProduct();
				listItem.setValue(cartItem);
				
				new Listcell(prod.getName()).setParent(listItem);
				new Listcell(""+prod.getPrice()).setParent(listItem);
				new Listcell(""+cartItem.getAmount()).setParent(listItem);
				
				initSubTotal(cartItem, new Listcell()).setParent(listItem);
				initCancelBtn(cartItem, prod, new Listcell()).setParent(listItem);
			}
			
			private Listcell initSubTotal(CartItem cartItem, Listcell cell){
				float price = cartItem.getProduct().getPrice();
				float subTotal = price * cartItem.getAmount();
				
				Label subTotalLb = new Label("$ "+subTotal);
				subTotalLb.setStyle("word-wrap: word-break");	
				subTotalLb.setParent(cell);
				return cell;
			}

			private Listcell initCancelBtn(final CartItem cartItem, final Product prod, Listcell cell){
				Button button = new Button();
				button.setImage("/image/DeleteCross-16x16.png");
				button.addEventListener(ON_CLICK, new EventListener() {
					public void onEvent(Event event) throws Exception {
						_cartModel.remove(cartItem);
						refresh();
					}
				});
				button.setParent(cell);
				return cell;
			}
		});

Let’s look more in depth at the two functions.

private Listcell initSubTotal(CartItem cartItem, Listcell cell) {
        float price = cartItem.getProduct().getPrice();
	float subTotal = price * cartItem.getAmount();
				
	Label subTotalLb = new Label("$ "+subTotal);
	subTotalLb.setStyle("word-wrap: word-break");	
	subTotalLb.setParent(cell);
	return cell;
}

private Listcell initCancelBtn(final CartItem cartItem, final Product prod, Listcell cell){
        Button button = new Button();
	button.setImage("/image/DeleteCross-16x16.png");
	button.addEventListener(ON_CLICK, new EventListener() {
		public void onEvent(Event event) throws Exception {
			_cartModel.remove(cartItem);
			refresh();
		}
	});

Both these functions take the cartitem along with a Listcell and return a Listcell. In the method initSubTotal the subtotal is calculated, placed into a Label and then has the style set. Finally the Labels parent is set and returned.

Label subTotalLb = new Label("$ "+subTotal);
subTotalLb.setStyle("word-wrap: word-break");	
subTotalLb.setParent(cell);
return cell;

The cancel function is slightly more indepth as it constructs a button, sets its image and then adds an EventListener to itself to listen for the ON_CLICK event. The advantage of a renderer is displayed here as a reference to the Cartitem is available therefore it is very easy to create an anonymous EventListener implementation which can remove the cartitem from the model as the object reference is freely available.

Finally if we return to the original implementation of the ListItemRenderer class all the Listcell set their parent to the given Listitem so they are displayed appropriately.

                        public void render(Listitem listItem, Object data) throws Exception {
				
				final CartItem cartItem = (CartItem)data;
				final Product prod = cartItem.getProduct();
				listItem.setValue(cartItem);
				
				new Listcell(prod.getName()).setParent(listItem);
				new Listcell(""+prod.getPrice()).setParent(listItem);
				new Listcell(""+cartItem.getAmount()).setParent(listItem);
				
				initSubTotal(cartItem, new Listcell()).setParent(listItem);
				initCancelBtn(cartItem, prod, new Listcell()).setParent(listItem);
			}

Just as with the Grid’s RowRenderer, the ListItemRenderer is a series of instructions assigning the UI components with values; using a View-Model-Renderer approach, any changes in the data model translates to updates in the UI component when the renderer code is run.



Last Update : 2011/06/23

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