ZK MVVM Form Binding CRUD with Spring and Hibernate - Part 4

Presentation Layer using ZK Framework

        


In part 3, We have completed Spring integration with hibernate. In this post, we will design our presentation layer using ZK Framework.

Step 1:
We will first design our listing screens which will show all the users in the database with typical CRUD Operation as shown here.
image 

Under the webapp folder, create a zul file and name as userList.zul as shown.
image

<?page title="Users List" contentType="text/html;charset=UTF-8"?>
<zk>
<window id="userList" border="none"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkexample.zkoss.UserListVM')">
<separator />
<separator />
<div width="100%">
<div sclass="sectionTitle">
<separator />
<label value="Users" sclass="sectionTitleLabel" />
<separator />
</div>
<div style="float:right">
<button label="Add New" onClick="@command('onAddNew')"
mold="trendy" sclass="mybutton button blue small" />
</div>
<div style="clear: both;"></div>
<div sclass="sectionSeperator"></div>
</div>
<separator />
<listbox id="" mold="paging" pageSize="11" pagingPosition="top"
sclass="mylist" selectedItem="@bind(vm.selectedItem)"
model="@load(vm.dataSet)">
<listhead sizable="true">
<listheader label="First Name" sortDirection="ascending"
sort="auto(firstName)" />
<listheader label="last Name" sort="auto(lastName)" />
<listheader label="Email" sort="auto(email)" />
<listheader label="Login ID" sort="auto(userLoginID)" />
<listheader label="Action" />
</listhead>
<template name="model" var="p1">
<listitem>
<listcell label="@load(p1.firstName)" />
<listcell label="@load(p1.lastName)" />
<listcell label="@load(p1.email)" />
<listcell label="@load(p1.userLoginID)" />
<listcell>
<hbox spacing="20px">
<button label="Edit"
onClick="@command('onEdit',userRecord=p1)">
</button>
<button label="View"
onClick="@command('openAsReadOnly',userRecord=p1)">
</button>
<button label="Delete"
onClick="@command('onDelete',userRecord=p1)">
</button>
</hbox>
</listcell>
</listitem>
</template>
</listbox>
</window>
</zk>


1. We are using MVVM as design pattern.
2. And also, to display the values, we are using MVVM Data binding concept.
3. For event handling, we are using MVVM Command Binding.
4. We are using ZK List box to show the record from the DB.
5. We will display the records using MVVM View Modal

Now let us create the view model to support the view. Create one more package named as zkoss under the existing package zkexample as shown.
image

Under the package, create a class called UserListVM
image



package zkexample.zkoss;

import java.util.HashMap;
import java.util.List;

import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Messagebox;

import zkexample.domain.UserProfile;
import zkexample.service.CRUDService;

public class UserListVM {

@WireVariable
private CRUDService CRUDService;

private UserProfile selectedItem;
private List<UserProfile> allReordsInDB = null;

public UserProfile getSelectedItem() {
return selectedItem;
}

public void setSelectedItem(UserProfile selectedItem) {
this.selectedItem = selectedItem;
}

@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view) {
Selectors.wireComponents(view, this, false);
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");
allReordsInDB = CRUDService.getAll(UserProfile.class);
}

public List<UserProfile> getDataSet() {
return allReordsInDB;
}

@Command
public void onAddNew() {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", null);
map.put("recordMode", "NEW");
Sessions.getCurrent().setAttribute("allmyvalues", map);
Executions.sendRedirect("UserCRUD.zul");
}

@Command
public void onEdit(@BindingParam("userRecord") UserProfile userProfile) {

final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", userProfile);
map.put("recordMode", "EDIT");
Sessions.getCurrent().setAttribute("allmyvalues", map);
Executions.sendRedirect("UserCRUD.zul");
}

@Command
public void openAsReadOnly(
@BindingParam("userRecord") UserProfile userProfile) {

final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", userProfile);
map.put("recordMode", "READ");
Sessions.getCurrent().setAttribute("allmyvalues", map);
Executions.sendRedirect("UserCRUD.zul");
}

@SuppressWarnings({ "rawtypes", "unchecked" })
@Command
public void onDelete(@BindingParam("userRecord") UserProfile userProfile) {
int OkCancel;
this.selectedItem = userProfile;
String str = "The Selected \"" + userProfile.getUserLoginID()
+ "\" will be deleted.";
OkCancel = Messagebox.show(str, "Confirm", Messagebox.OK
| Messagebox.CANCEL, Messagebox.QUESTION);
if (OkCancel == Messagebox.CANCEL) {
return;
}

str = "The \""
+ userProfile.getUserLoginID()
+ "\" will be permanently deleted and the action cannot be undone.";

Messagebox.show(str, "Confirm", Messagebox.OK | Messagebox.CANCEL,
Messagebox.QUESTION, new EventListener() {
public void onEvent(Event event) throws Exception {
if (((Integer) event.getData()).intValue() == Messagebox.OK) {

CRUDService.delete(selectedItem);
allReordsInDB.remove(allReordsInDB
.indexOf(selectedItem));
BindUtils.postNotifyChange(null, null,
UserListVM.this, "dataSet");

}
}
});
}

}


Step 2:
Next we will design our CRUD Page where we can add new users and Edit existing users. This CRUD ZUL will be called from userList.zul when user click on Add/Edit/View buttons.
image



Create UserCRUD.Zul file under the webapp folder as shown.

image



<?page title="" contentType="text/html;charset=UTF-8"?>
<zk>
<window id="userCRUD" border="none"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkexample.zkoss.UserCRUDVM')">
<div
form="@id('fx') @load(vm.selectedRecord) @save(vm.selectedRecord, before='saveThis')">
<separator />
<div width="96%">
<span width="100%">
<div sclass="sectionTitle">
<separator />
<label id="lcaption" sclass="sectionTitleLabel"
value="Add/Edit Users" />
<label id="readonly" sclass="sectionTitleLabel"
value="(ReadOnly)" visible="@load(vm.makeAsReadOnly)" />
<image
src="@load(fxStatus.dirty?'images/unsaved32x32.png':'')" />
<separator />
</div>
</span>
<div id="buttonsDiv"
style="float:right;margin-top:6px;">
<button label="Save and Close" mold="trendy"
visible="@load(not vm.makeAsReadOnly)"
onClick="@command('saveThis', action=0)"
sclass="mybutton button blue small">
</button>
<button label="Save and Add" mold="trendy"
visible="@load(not vm.makeAsReadOnly)"
onClick="@command('saveThis', action=1)"
sclass="mybutton button blue small">
</button>
<button
label="@load(vm.makeAsReadOnly ?'Close':'Cancel')" mold="trendy"
onClick="@command('cancel')"
sclass="mybutton button blue small">
</button>
</div>
<div style="clear: both;"></div>
<div sclass="sectionSeperator"></div>
</div>

<span width="100%">
<div sclass="sectionTitle">
<separator />
<label value="Personel Information"
sclass="sectionTitleLabel" />
<separator />
</div>
</span>

<panel width="95%" sclass="sectionPanel">
<panelchildren>
<separator />
<grid sclass="vgrid">
<columns>
<column hflex="10%"></column>
<column hflex="10%"></column>
<column hflex="10%"></column>
<column hflex="10%"></column>
</columns>
<rows>
<row>
<vlayout>
<label value="User Account No"
sclass="flabel" />
<textbox id="accountNo"
readonly="@load(vm.makeAsReadOnly)" mold="rounded" hflex="1"
value="@bind(fx.userAccountNumber)" />
</vlayout>
<vlayout>
<label value="Last Name"
sclass="flabel" />
<textbox id="lastname"
readonly="@load(vm.makeAsReadOnly)" mold="rounded" hflex="1"
value="@bind(fx.lastName)" />
</vlayout>
<vlayout>
<label value="First Name"
sclass="flabel" />
<textbox id="firstname"
readonly="@load(vm.makeAsReadOnly)" mold="rounded" hflex="1"
value="@bind(fx.firstName)" />
</vlayout>
<vlayout>
<label value="Middle Name"
sclass="flabel" />
<textbox id="middleName"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.middleName)" />
</vlayout>

</row>
<row>

<vlayout>
<label value="Email"
sclass="flabel" />
<textbox id="email" hflex="1"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.email)" />
</vlayout>
<cell colspan="2">
<hbox>
<vlayout>
<label value="SSN"
sclass="flabel" />
<textbox id="SSN"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
width="115%" value="@bind(fx.SSN)" />
</vlayout>
<vlayout>
<label value="DOB"
sclass="flabel" />
<textbox id="DOB"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
width="115%" value="@bind(fx.DOB)" />
</vlayout>
</hbox>
</cell>
</row>
</rows>
</grid>
</panelchildren>
</panel>
<separator />
<separator />
<separator />
<span width="100%">
<div sclass="sectionTitle">
<separator />
<label value="Address Information"
sclass="sectionTitleLabel" />
<separator />
</div>
</span>
<panel width="95%" sclass="sectionPanel">
<panelchildren>
<grid sclass="vgrid">
<columns>
<column hflex="1"></column>
<column hflex="1"></column>
<column hflex="1"></column>
</columns>
<rows>
<row>
<cell colspan="2">
<vlayout>
<label value="Address"
sclass="flabel" />
<textbox id="address1" hflex="2"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.address1)" />
</vlayout>
</cell>
</row>
<row>
<cell colspan="2">
<vlayout>
<textbox id="address2" hflex="2"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.address2)" />
</vlayout>
</cell>
</row>
<row>
<vlayout>
<label value="City" sclass="flabel" />
<textbox id="City" hflex="1"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.city)" />
</vlayout>
<vlayout>
<grid>
<columns>
<column width="60%"></column>
<column></column>
</columns>
<rows>
<row>
<vlayout>
<label value="State"
sclass="flabel" />
<textbox id="State"
readonly="@load(vm.makeAsReadOnly)" hflex="1"
mold="rounded" value="@bind(fx.state)" />
</vlayout>
<vlayout>
<label
value="ZipCode" sclass="flabel" />
<textbox
id="zipcode" hflex="1" mold="rounded"
readonly="@load(vm.makeAsReadOnly)"
value="@bind(fx.zipCode)" />
</vlayout>
</row>
</rows>
</grid>
</vlayout>
</row>
</rows>
</grid>
</panelchildren>
</panel>
<separator />
<separator />
<separator />
<span width="100%">
<div sclass="sectionTitle">
<separator />
<label value="Login information"
sclass="sectionTitleLabel" />
<separator />
</div>
</span>
<panel width="95%" sclass="sectionPanel">
<panelchildren>
<separator />
<grid sclass="vgrid">
<columns>
<column width="20%"></column>
<column width="24%"></column>
</columns>
<rows>
<row>
<vlayout>
<label value="Login ID"
sclass="flabel" />
<textbox id="loginid" hflex="1"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.userLoginID)" />
</vlayout>
<vlayout>
<label value="Password"
sclass="flabel" />
<textbox id="password" hflex="1"
readonly="@load(vm.makeAsReadOnly)" mold="rounded"
value="@bind(fx.password)" />
</vlayout>
</row>
</rows>
</grid>
</panelchildren>
</panel>
</div>
</window>
</zk>

We have used MVVM Form binding Method to build our CRUD Page.
Next we will add View Model to control the above zul file. Under the zkexample.zkoss package, create a class called UserCRUDVM

package zkexample.zkoss;

import java.util.HashMap;

import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zkplus.spring.SpringUtil;

import zkexample.domain.UserProfile;
import zkexample.service.CRUDService;

public class UserCRUDVM {

@WireVariable
private CRUDService CRUDService;

private UserProfile selectedRecord;
private String recordMode;
private boolean makeAsReadOnly;

public UserProfile getSelectedRecord() {
return selectedRecord;
}

public void setSelectedRecord(UserProfile selectedRecord) {
this.selectedRecord = selectedRecord;
}

public String getRecordMode() {
return recordMode;
}

public void setRecordMode(String recordMode) {
this.recordMode = recordMode;
}

public boolean isMakeAsReadOnly() {
return makeAsReadOnly;
}

public void setMakeAsReadOnly(boolean makeAsReadOnly) {
this.makeAsReadOnly = makeAsReadOnly;
}

@SuppressWarnings("unchecked")
@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view) {

UserProfile userProfile;
Selectors.wireComponents(view, this, false);

final HashMap<String, Object> map = (HashMap<String, Object>) Sessions
.getCurrent().getAttribute("allmyvalues");
this.recordMode = (String) map.get("recordMode");
userProfile = (UserProfile) map.get("selectedRecord");
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");

if (recordMode.equals("NEW")) {
this.selectedRecord = new UserProfile();
}

if (recordMode.equals("EDIT")) {
this.selectedRecord = userProfile;
}

if (recordMode == "READ") {
setMakeAsReadOnly(true);
this.selectedRecord = userProfile;
}
}

@Command
public void saveThis(@BindingParam("action") Integer action) {
CRUDService.Save(this.selectedRecord);
if (action==0)
{
Executions.sendRedirect("userList.zul");
}
if (action==1)
{
this.selectedRecord = new UserProfile();
BindUtils.postNotifyChange(null, null, UserCRUDVM.this,"selectedRecord");

}
}

@Command
public void cancel()
{
Executions.sendRedirect("userList.zul");
}

}


So we have completed our CRUD operation. Now you can select the userList.zul and Run using tomcat server.

You can download source here.



Video Demo
http://screencast.com/t/dYgpCAGGUqu1



In the next part 5, We will change the look and feel using CSS