Displaying Information from the View Model
This article is out of date, please refer to http://books.zkoss.org/zkessentials-book/master/ for more up to date information.
In this section and the next, we'll implement the Shopping Cart View using the MVVM pattern.
File:ZKEss ShoppingCartView.png
To start with the author will now give an overview of what the final ZUL fragment for the MVVM will look like. This section will then teach developers how to create this step by step.
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('demo.web.ui.ctrl.ShoppingCartViewModel')">
<listbox id="shoppingCartListbox"
model="@load(vm.cartItems)"
selectedItem="@bind(vm.selectedItem)">
<listhead sizable="true">
<listheader label="Name" />
<listheader label="Price" />
<listheader label="Amount" />
<listheader label="subTotal" />
<listheader align="center" />
</listhead>
<template name="model" var="cartItem">
<listitem>
<listcell label="@load(cartItem.product.name)" />
<listcell label="@load(cartItem.product.price)" />
<listcell label="@load(cartItem.amount)" />
<listcell label="@load(cartItem.product.price * cartItem.amount)" style="word-wrap: word-break" />
<listcell>
<button image="/image/DeleteCross-16x16.png" onClick="@command('deleteOrder', cartItem=cartItem)" />
</listcell>
</listitem>
</template>
<listfoot>
<listfooter span="2" id="subtotalFooter" label="@load(vm.shoppingCart.totalPrice)">
</listfooter>
<listfooter>
<button id="submitOrderBtn" label="submit"
onClick="@command('submitOrder')"
disabled="@load(empty vm.cartItems)" />
</listfooter>
<listfooter>
<button id="clearBtn" label="clear"
onClick="@command('clearOrders')"
disabled="@load(empty vm.cartItems)" />
</listfooter>
</listfoot>
</listbox>
...
</div>
Initializing the BindComposer and ViewModel
The first thing that needs to be done is the initialization of the BindComposer which will manage binding for it's child components. This can be done simply using the familiar apply attribute to the parent control of your choice. In the case of this example the div which contains the listbox was chosen.
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('demo.web.ui.ctrl.ShoppingCartViewModel')">
Line 2 in the above snippet assigns a value to a viewModel property. This property is used for two things
- Tell the binder which ViewModel class to instantiate
- Assign a name to the instantiated class
In this case @id is used to set the name of the instantiated view model to 'vm' while @init is used to specify the class to instantiate. Having specified a view model class one now needs to create the said class.
Implementing The View Model
In the MVVM pattern a view model is an abstraction of Model. It extracts the necessary data to be displayed on the View from one or more Model classes. Those data are exposed through getter and setter method like JavaBean's property. To create a view model is easy, there is no need to extend from nor implement any other class. A view model is a POJO and exposes its data using getter and setter methods.
public class ShoppingCartViewModel {
...
private CartItem selectedItem;
public CartItem getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(CartItem selectedItem) {
this.selectedItem = selectedItem;
}
public List<CartItem> getCartItems() {
return UserUtils.getShoppingCart().getItems();
}
public ShoppingCart getShoppingCart() {
return UserUtils.getShoppingCart();
}
...
}
Binding Data to View Model
For loading & saving data ZK's MVVM pattern provides two general commands, @load and @bind. @load command will only load data and any change to the data from the UI will not be saved to the bean. @bind on the other hand provides both loading and saving of the bean so any changes to the values driven by the UI will be reflected server-side.
With this in mind it is time to start loading the data into the shopping cart. First thing to be done is load the model and selected item. This is easily done by accessing the getters and setters we implemented for the View Model:
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('demo.web.ui.ctrl.ShoppingCartViewModel')">
<listbox id="shoppingCartListbox"
model="@load(vm.cartItems)"
selectedItem="@bind(vm.selectedItem)">
...
</listbox>
</div>
It is now possible to implement a template as outlined in previous sections to create each Listitem. Using databinding the resulting template is extremely simple.
<listbox id="shoppingCartListbox"
model="@load(vm.cartItems)"
selectedItem="@bind(vm.selectedItem)">
<listhead sizable="true">
...
</listhead>
<template name="model" var="cartItem">
<listitem>
<listcell label="@load(cartItem.product.name)" />
<listcell label="@load(cartItem.product.price)" />
<listcell label="@load(cartItem.amount)" />
<listcell label="@load(cartItem.product.price * cartItem.amount)" style="word-wrap: word-break" />
<listcell>
<button image="/image/DeleteCross-16x16.png" onClick="@command('deleteOrder', cartItem=cartItem)" />
</listcell>
</listitem>
</template>
...
</listbox>
Line 6 brings about an intersting use case as instead of just specifying a bean's property it specifies an equation. The binder is capable of handling expressions. There are many more expressions available for more information please click here link needed.
This takes care of basic loading and saving functionality, but what about actions such as clicking buttons? The next section introduces how to give commands based on component actions.