MVVM in ZK6:in Contrast to MVC

From Documentation
Revision as of 07:03, 8 December 2011 by Hawk (talk | contribs) (→‎Write a ZUL)

MVVM in ZK6-in Contrast to MVC

Foreword

In the article: MVVM in ZK 6 - Design Your First MVVM Page, the author demonstrate how to build ZK application in MVVM patterns. It’s a big change from using a composer. The style of manipulating UI components in a composer is more like MVP (Model-View-Presenter) pattern, a variant of MVC. The composer plays the role as “Presenter” in MVP pattern. It handles events from users interaction, retrieves data from “Model”, and update the “View“ by manipulating UI components directly. In this article, we are going to build the same application, an item search application, as described in previous article, but using a composer.


Steps to Implement

Write a ZUL

searchMvc.zul

<zk>
<style>
.z-listcell.red .z-listcell-cnt, .z-label.red{
	color:red;
}
</style>
<window id="searchWin" title="Search Storage Item" border="normal" width="600px"
	apply="org.zkoss.bind.examples.search.mvp.SearchPresenter" >
	<vbox hflex="true">
		<hbox>
			Filter : 
			<textbox id="filterBox" value="*" instant="true"/> 
			<button id="searchButton" label="Search" />
		</hbox>
		<listbox id="itemListbox" hflex="true" height="300px">
			<listhead>
				<listheader label="Name"/>
				<listheader label="Price" align="center" width="80px" />
				<listheader label="Quantity" align="center" width="80px" />
			</listhead>
		</listbox>
		<groupbox id="detailBox" visible="false" hflex="true" mold="3d">
			<caption id="detailCaption" />
			<grid hflex="true" >
				<columns>
					<column width="120px"/>
					<column/>
				</columns>
				<rows>
					<row>Description <label id="descriptionLabel"/></row>
					<row>Price <label id="priceLabel" /></row>
					<row>Quantity <label id="quantityLabel" value="@bind(presenter.selected.quantity)" /></row>
					<row>Total Price <label id="totalPriceLabel" /></row>
				</rows>
			</grid>
		</groupbox>
	</vbox>
</window>
</zk>

The ZUL has little difference the one in MVVM pattern, it just lacks of ZK bind annotation.

Create a Composer

public class SearchPresenter extends SelectorComposer<Component>{
	//the search result
	private ListModelList<Item> items;

	//the selected item
	private Item selected;
	//UI component
	@Wire("#filterBox")
	private Textbox filterBox;
	@Wire("button")
	private Button searchButton;
	@Wire("listbox")
	private Listbox itemListbox;
	@Wire("groupbox")
	private Groupbox detailBox;
	@Wire("caption")
	private Caption detailCaption;
	@Wire("#descriptionLabel")
	private Label descriptionLabel;
	@Wire("#priceLabel")
	private Label priceLabel;
	@Wire("#quantityLabel")
	private Label quantityLabel;
	@Wire("#totalPriceLabel")
	private Label totalPriceLabel;

	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		doSearch();
		itemListbox.setModel(items);
		itemListbox.setItemRenderer(new ItemRenderer());
	}
	
	protected SearchService getSearchService(){
		return new FakeSearchService();
	}
	
	@Listen("onClick = button")
	public void doSearch(){
		items = new ListModelList<Item>();
		items.addAll(getSearchService().search(filterBox.getValue()));
		itemListbox.setModel(items);
		detailBox.setVisible(false);
	}
	
	@Listen("onChange = #filterBox")
	public void changeButtonStatus(){
		searchButton.setDisabled(filterBox.getValue().length()==0);
	}
	@Listen("onSelect = listbox")
	public void selectItem(){
		selected = items.get(itemListbox.getSelectedIndex());
		//display item detail
		detailBox.setVisible(true);
		detailCaption.setLabel(selected.getName());
		descriptionLabel.setValue(selected.getDescription());
		priceLabel.setValue(ItemRenderer.priceFormatter.format(selected.getPrice()));
		quantityLabel.setValue(Integer.toString(selected.getQuantity()));
		quantityLabel.setSclass(selected.getQuantity()<3?"red":"");
		totalPriceLabel.setValue(ItemRenderer.priceFormatter.format(selected.getTotalPrice()));
	}
}

Implement Listbox Rendering

public class ItemRenderer implements ListitemRenderer<Item>{

	static DecimalFormat priceFormatter = new DecimalFormat("$ ###,###,###,##0.00");

	public void render(Listitem item, Item data){
		
		Listcell nameCell = new Listcell();
		nameCell.setLabel(data.getName());
		Listcell priceCell = new Listcell();
		priceCell.setLabel(priceFormatter.format(data.getPrice()));
		Listcell quantityCell = new Listcell();
		quantityCell.setLabel(Integer.toString(data.getQuantity()));
		if (data.getQuantity()<3){
			quantityCell.setSclass("red");
		}
		
		item.appendChild(nameCell);
		item.appendChild(priceCell);
		item.appendChild(quantityCell);
		
	}
}

The steps to implement in MVC are similar to those in MVVM, but it requires an extra class to achieve Listbox custom rendering.


Comparison

Because ViewModel doesn’t hold references to ZK components, after requirements and functions are confirmed. UI designers and programmers can work in parallel. This pattern is very suitable for “design by contract” approach. Even UI design is modified afterward. If the modification doesn’t change the contract (confirmed functions), it doesn’t affect programmer’s work. This approach reduces errors that are caused by frequent UI requirement change.

ZK MVVM forces developers to write presentation logic on ZUL with ZK Bind expression. This pattern separates the responsibility of ZUL and ViewModel clearly. In MVC, developers have to write them in the composer. Because ZK Bind expression can apply on component’s attributes, developers can achieve many dynamically interaction effect by altering component’s attributes. It’s harder to maintain presentation logic in the composer but developers can gain more flexibility. It’s easier to write complex presentation logic in Java than in EL. It’s also easier to maintain and understand a custom view layout that is implemented within ZUL using ZK Bind than in a Renderer. (any ‘for examples’ in this paragraph?)

Of course, if developers require more control on ZK components themselves like dynamically creating child components. It’s convenient to use the composer.

In ZK MVVM, developers don’t have to declare variables for each ZK components. They manipulate components through ZK Bind expression on component’s attributes. In MVC with composer, developers don’t need to provide lots of setter and getter. They change view content by manipulating ZK components themselves.

Developers can do unit test on ViewModel easily because it’s separated with ZUL while composers can only be tested through browsers. Possible to provide a grid for the comparison?

Conclusion

Adopting which pattern depends on developer’s context and requirement. If developers needs clear separation between view layer and its backend and to perform unit test on every layer of application, the MVVM suits this case. If developers prefer more flexibility and want to implement complex presentation logic, the MVC pattern suits this case.


Download