ZK MVVM CRUD With Spring 3 + JPA + Hibernate 4 Entity Manager–Part 6

ZK UI Presentation layer.

In the Part 5 article, we have completed our DAO and service layer to do CRUD Operation. In this post, we will design our UI using ZK Framework.

Step 1:
Now let us design the Listing Page which will show all the records from the DB. And also, we will have one Add New button to add new record and for each record, we will have “EDIT”, “View” and “Delete” button as shown here.
image

Select the webapp folder, right click, select new-> folder and enter the folder name as pages as shown

image

Now select the Pages folder, right click, Select New –> Other-> Zul as shown

image

Enter the zul file name as AddressList.zul as shown

image
Copy the following code to list the records from the DB

<?page title="Address List" contentType="text/html;charset=UTF-8"?>
<zk>
<window id="addressList" title="Address List" border="none"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkoss.vm.AddressListVM')">
<separator />
<button label="New Address" onClick="@command('onAddNew')"></button>
<separator />
<listbox id="" mold="paging" pageSize="11" pagingPosition="top"
selectedItem="@bind(vm.selectedItem)" model="@load(vm.dataSet)">
<listhead sizable="true">
<listheader label="Full Name" sortDirection="ascending"
sort="auto(fullName)" />
<listheader label="Email" sort="auto(email)" />
<listheader label="Mobile" sort="auto(mobile)" />
<listheader label="Action" />
</listhead>
<template name="model" var="p1">
<listitem>
<listcell label="@load(p1.fullName)" />
<listcell label="@load(p1.email)" />
<listcell label="@load(p1.mobile)" />
<listcell>
<hbox spacing="20px">
<button label="Edit"
onClick="@command('onEdit',addressRecord=p1)">
</button>
<button label="View"
onClick="@command('openAsReadOnly',addressRecord=p1)">
</button>
<button label="Delete"
onClick="@command('onDelete',addressRecord=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


As you see in the following statement


apply="org.zkoss.bind.BindComposer"
        viewModel="@id('vm') @init('zkoss.vm.AddressListVM')">


We need to create View Model in the name of AddressListVM (java class)


Select “java” folder, create a new package called “zkoss.vm” and create a called AddressListVM as shown


image
Here is the code for AddressListVM.Java


package zkoss.vm;

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.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
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 addressbook.domain.AddressBook;
import addressbook.service.CRUDService;

public class AddressListVM {

@WireVariable
private CRUDService CRUDService;

private AddressBook selectedItem;
private List<AddressBook> allReordsInDB = null;
private Integer selectedItemIndex;

public AddressBook getSelectedItem() {
return selectedItem;
}

/*This method will be called when user select a record in the list*/
public void setSelectedItem(AddressBook selectedItem) {
this.selectedItem = selectedItem;
}

public Integer getSelectedItemIndex() {
return selectedItemIndex;
}

public void setSelectedItemIndex(Integer selectedItemIndex) {
this.selectedItemIndex = selectedItemIndex;
}

/*Only one @AfterCompose-annotated method is allowed at the most in a ViewModel class*/
/* Marker annotation to identify a life-cycle method in View Model.
this method will be called after host component composition has been done (AfterCompose).
*/
@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view) {
Selectors.wireComponents(view, this, false);
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");
allReordsInDB = CRUDService.getAll(AddressBook.class);
}

/* Using this method, the list box will be populated with records
Please note that, we have already initialized this list in the aftercompose.
Whenever, the list of the records has changed, then we will annotate with @NotifyChange("dataSet"), so that,
again this will be called to update the UI
*/
public List<AddressBook> getDataSet() {
return allReordsInDB;
}

/*This method will be called when user press the new button in the Listing screen*/
@Command
public void onAddNew() {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", null);
map.put("recordMode", "NEW");
Executions.createComponents("AddressCRUD.zul", null, map);
}

@Command
public void onEdit(@BindingParam("addressRecord") AddressBook addressBook) {
final HashMap<String, Object> map = new HashMap<String, Object>();
this.selectedItem = addressBook;
map.put("selectedRecord", addressBook);
map.put("recordMode", "EDIT");
setSelectedItemIndex(allReordsInDB.indexOf(selectedItem));
Executions.createComponents("AddressCRUD.zul", null, map);
}

@Command
public void openAsReadOnly(
@BindingParam("addressRecord") AddressBook addressBook) {
final HashMap<String, Object> map = new HashMap<String, Object>();
this.selectedItem = addressBook;
map.put("selectedRecord", addressBook);
map.put("recordMode", "READ");
Executions.createComponents("AddressCRUD.zul", null, map);
}

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

str = "The \""
+ addressBook.getFullName()
+ "\" 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,
AddressListVM.this, "dataSet");

}
}
});
}

// note this will be executed from AddressListCRUDVM.java

@GlobalCommand
@NotifyChange("dataSet")
public void refreshList(
@BindingParam("selectedRecord") AddressBook addressbook,
@BindingParam("recordMode") String recordMode) {
if (recordMode.equals("NEW")) {
allReordsInDB.add(addressbook);
}

if (recordMode.equals("EDIT")) {
allReordsInDB.set(this.selectedItemIndex, addressbook);
}
}

}

Next step, we will create our CRUD form where user can add/edit/view the existing record. As you can see in the addresslistvm.java, we have been calling a zul file name called “AddressCRUD.zul” using ZK Create component utility.


Step 2:
In the same pages folder, create another zul file named as “AddressCRUD.Zul”


image
image



<?page title="new page title" contentType="text/html;charset=UTF-8"?>
<zk>
<window title="Address" id="addressCRUD" border="normal" width="30%"
mode="modal" maximizable="false" closable="true"
position="center,parent" apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkoss.vm.AddressListCRUDVM')">
<panel width="100%">
<panelchildren>
<separator />
<grid>
<columns>
<column></column>
<column></column>
</columns>
<rows>
<row>
<cell colspan="2">
<vlayout>
<label value="Full Name" />
<textbox id="fullName" hflex="1"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.fullName) @save(vm.selectedRecord.fullName, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<cell colspan="2">
<vlayout>
<label value="Address" />
<textbox id="address1" hflex="1"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.address1) @save(vm.selectedRecord.address2, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<cell colspan="2">
<vlayout>
<textbox id="address2" hflex="1"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.address2) @save(vm.selectedRecord.address2, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<vlayout>
<label value="City" />
<textbox id="City" hflex="1"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.city) @save(vm.selectedRecord.city, before='saveThis')"
mold="rounded" />
</vlayout>
<vlayout>
<grid sclass="vgrid">
<columns>
<column width="30%"></column>
<column></column>
</columns>
<rows>
<row>
<vlayout>
<label value="State" />
<textbox id="State"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.state) @save(vm.selectedRecord.state, before='saveThis')"
hflex="1" mold="rounded" />
</vlayout>
<vlayout>
<label value="ZipCode" />
<textbox id="zipcode"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.zipCode) @save(vm.selectedRecord.zipCode, before='saveThis')"
hflex="1" mold="rounded" />
</vlayout>
</row>
</rows>
</grid>
</vlayout>
</row>
<row>
<cell colspan="2">
<vlayout>
<label value="Email" />
<textbox id="email" hflex="1"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.email) @save(vm.selectedRecord.email, before='saveThis')"
mold="rounded" />
</vlayout>
</cell>
</row>
<row>
<vlayout>
<label value="Mobile" />
<textbox id="Mobile" hflex="1"
readonly="@load(vm.makeAsReadOnly)"
value="@load(vm.selectedRecord.mobile) @save(vm.selectedRecord.mobile, before='saveThis')"
mold="rounded" />
</vlayout>
</row>

</rows>
</grid>
</panelchildren>
</panel>
<separator />
<separator />
<separator />
<div align="center">
<button label="Save" visible="@load(not vm.makeAsReadOnly)"
onClick="@command('saveThis')" mold="trendy" />
<button label="@load(vm.makeAsReadOnly ?'Ok':'Cancel')"
onClick="@command('closeThis')" mold="trendy" />
</div>
<separator />
</window>
</zk>


Next we will create our VM Java class to support the above UI. In the zkoss.vm package, create a class named “AddressListCRUDVM.java”


package zkoss.vm;

import java.util.HashMap;
import java.util.Map;

import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.ExecutionArgParam;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Window;

import addressbook.domain.AddressBook;
import addressbook.service.CRUDService;


public class AddressListCRUDVM {

@Wire("#addressCRUD")
private Window win;

@WireVariable
private CRUDService CRUDService;

private AddressBook selectedRecord;
private String recordMode;
private boolean makeAsReadOnly;

public AddressBook getSelectedRecord() {
return selectedRecord;
}

public void setSelectedRecord(AddressBook 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;
}

@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view,
@ExecutionArgParam("selectedRecord") AddressBook addressBook,
@ExecutionArgParam("recordMode") String recordMode)
throws CloneNotSupportedException {
Selectors.wireComponents(view, this, false);
setRecordMode(recordMode);
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");

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

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

if (recordMode == "READ") {
setMakeAsReadOnly(true);
this.selectedRecord = addressBook;
win.setTitle(win.getTitle() + " (Readonly)");
}
}

@Command
public void saveThis() {
Map<String, Object> args = new HashMap<String, Object>();
CRUDService.save(this.selectedRecord);
args.put("selectedRecord", this.selectedRecord);
args.put("recordMode", this.recordMode);
BindUtils.postGlobalCommand(null, null, "refreshList", args);
win.detach();
}

@Command
public void closeThis() {
win.detach();
}
}

That’s all. Now you can select the file “AddressList.zul” and right click,Select “Run on Server” (Using tomcat as web server). The output will be as follows
image


image


In the next article, we will see how to change the look and feel by override default ZK CSS.