Using a ListModel and ListitemRenderer

From Documentation
Revision as of 04:44, 4 August 2011 by Tonyq (talk | contribs)

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 method is 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 into these 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 which has a 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 than the subtotal function because 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 displayed here as a reference to the Cartitem is that it becomes 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 will set their parent to the given Listitem so they can be 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 will be translated to update in the UI component when the renderer code is run.


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