MVVM in ZK6:in Contrast to MVC
Hawk Chen, Engineer, Potix Corporation
December 16, 2011
ZK 6
Introduction: MVVM in ZK6 - In Contrast to MVC
In the article MVVM in ZK 6 - Design Your First MVVM Page, Dennis demonstrated how users can build ZK applications using the MVVM pattern. It’s a big change from using a composer. In this article, we are going to build the same application, an item search application, as described in the article mentioned above, but instead, using a composer.The style of manipulating UI components in a composer is more like the MVP (Model-View-Presenter) pattern which is, a variant of MVC. The composer plays the role as “Presenter” in the MVP pattern. It handles events from users' interaction, retrieves data from “Model”, and updates “View“ by manipulating UI components directly.
Implementation Steps
- Write a ZUL
- The ZUL has little difference from the one in the MVVM pattern; the only difference is that it does not contain ZK bind annotations.
- Create a Composer
- The composer doesn't have a lot of getter and setters, but developers have to declare a lot of variables corresponding to UI components.
- A lot of coding is needed when using public void selectItem() to display details of an item whereas ZK bind saves us a lot of effort for doing the same thing
- No need to implement Converter
- Implement Listbox Rendering
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" /></row>
<row>Total Price <label id="totalPriceLabel" /></row>
</rows>
</grid>
</groupbox>
</vbox>
</window>
</zk>
SearchPresenter.java
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()));
}
}
The following points can be seen from the above sample code
ItemRenderer.java
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);
}
}
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 annotation. This pattern separates the responsibility of ZUL and ViewModel clearly. In MVC, developers have to write them in the composer. Because ZK Bind annotation can apply on the 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. For example, to disable search button when textbox is empty.
In MVVM: search.zul
<button label="Search" disabled="@load(empty vm.filter)" />
In MVC:
SearchPresenter.java
@Listen("onChange = #filterBox")
public void changeButtonStatus(){
searchButton.setDisabled(filterBox.getValue().length()==0);
}
It still need 2 variables for textbox and button component.
It’s also easier to maintain and understand a custom view layout that is written in ZUL using ZK Bind annotation than is implemented in a renderer class.
search.zul
<template name="model" var="item">
<listitem >
<listcell label="@load(item.name)"/>
<listcell label="@load(item.price) @converter('formatedNumber', format='###,##0.00')"/>
<listcell label="@load(item.quantity)" sclass="@load(item.quantity lt 3 ?'red':'')"/>
</listitem>
</template>
- Comparing to ItemRenderer.java , it's more readable.
But it’s easier to write complex presentation logic in Java than in EL. For example, developers have to display quantity in different colors upon complex business logic,
public class ItemRenderer implements ListitemRenderer<Item>{
public void render(Listitem item, Item data){
//...other code
int quantity = data.getQuantity();
if ( complexRule(quantity)){
//set color 1
}else if ( anotherComplexRule(quantity)){
//set color 2
}else {
//set color 3
}
//...other code
}
}
Of course, if developers require more control on ZK components themselves like dynamically creating child components. It’s better to use the composer with auto-wired components.
In ZK MVVM, developers don’t have to declare variables for each ZK components. They manipulate components through ZK Bind annotation 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.
Comparison Table
MVVM | MVC | |
---|---|---|
Coupling with Backend | Loose | Tight |
Where Presentation Logic Locates | ZUL | Java |
Controller Implementation | A POJO | A class that extends ZK's composer |
How to Update UI | Use ZK Bind annotation to notify the binder | Manipulate UI components |
Controller is unit testable | Yes | No |
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
zkbind-examples ( with source code)
See Also
- Envisage ZK 6: The Next Generation Data Binding System
- Hello ZK MVVM
- MVVM in ZK 6 - Design your first MVVM page
- MVVM in ZK 6 - Design CRUD page by MVVM pattern
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |