Sunday, 20 January 2013

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

Build UI using ZK Framework.

        

In the Part 5 article,
we have completed our DAO and service Layer. In this post, we will create our Presentation Layer using ZK Framework.

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

Step 1:
Now select the webapp folder and right click-> Select New->Other->Zul file and enter the name of the zul file as UserList.zul
Copy the below code.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./userList" ?>
<window width="100%" border="none" id="userList"
apply="crudexample.controller.UserListContrroller">
<separator />
<separator />
<div width="100%">
<div sclass="sectionTitle">
<separator />
<label value="Address Book" sclass="sectionTitleLabel" />
<separator />
</div>
<div style="float:right">
<button label="Add New" mold="trendy" id="btnNew"
sclass="mybutton button blue small" />
</div>
<div style="clear: both;"></div>
<div sclass="sectionSeperator"></div>
</div>
<separator />
<listbox id="UserListbox" model="@{controller.appUsersList}"
selectedItem="@{controller.selectedUser}" multiple="false"
emptyMessage="No User found in the database">
<listhead sizable="true">
<listheader label="User ID" sort="auto" />
<listheader label="Last Name" sort="auto" />
<listheader label="First Name" sort="auto" />
<listheader label="Action" />
</listhead>

<listitem self="@{each='users'}" value="@{users}"
forward="onDoubleClick=onDoubleClickedPersonItem">
<listcell label="@{users.userID}" />
<listcell label="@{users.lastName}" />
<listcell label="@{users.firstName}" />
<listcell>
<hbox spacing="20px">
<button label="Edit" forward="onClick=UserListbox.onEdit" mold="trendy" />
<button label="View" forward="onClick=UserListbox.onView" mold="trendy" />
<button label="Delete" forward="onClick=UserListbox.onDelete" mold="trendy" />
</hbox>
</listcell>
</listitem>

</listbox>


</window>

Step 2:
Next we will define the controller for the above zul file. Select the package “crudexample”, right click –> Select New->Other->Package. Enter the package name as “controller”. Then select the newly created package controller and right click->Select New->Other->Class and enter the class name as “UserListContrroller”
Here is the code


package crudexample.controller;

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

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.Events;
import org.zkoss.zk.ui.event.ForwardEvent;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.databind.AnnotateDataBinder;
import org.zkoss.zkplus.databind.BindingListModelList;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Button;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;

import crudexample.domain.Appusers;
import crudexample.service.CRUDService;

@SuppressWarnings("rawtypes")
public class UserListContrroller extends GenericForwardComposer {

@WireVariable
private CRUDService CRUDService;

private static final long serialVersionUID = 1L;

protected Listbox UserListbox; // autowired
protected Window userList; // autowired

// Databinding
private AnnotateDataBinder binder;
private BindingListModelList<Appusers> appUsersList;
private Appusers appUsers;
private Appusers selectedUser;

public UserListContrroller() {
super();
}

@Override
public void doAfterCompose(Component window) throws Exception {
super.doAfterCompose(window);
this.self.setAttribute("controller", this, false);
}

public void onCreate$userList(Event event) throws Exception {

this.binder = (AnnotateDataBinder) event.getTarget().getAttribute(
"binder", true);

setSelectedUser(null);
doFillListbox();

this.binder.loadAll();
}

public void doFillListbox() {

CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");
List<Appusers> list = CRUDService.getAll(Appusers.class);
appUsersList = new BindingListModelList<Appusers>(list, true);
setAppUsersList(appUsersList);
this.UserListbox.setModel(appUsersList);
}

/*This method will be called when user press the new button in the Listing screen*/

public void onClick$btnNew(Event event) {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", null);
map.put("recordMode", "NEW");
map.put("parentWindow",userList);
Executions.createComponents("UserCRUD.zul", null, map);
}

public void onEdit$UserListbox(ForwardEvent evt) {
Event origin = Events.getRealOrigin(evt);
Button btn = (Button) origin.getTarget();
Listitem litem = (Listitem) btn.getParent().getParent().getParent();
Appusers curUser = (Appusers) litem.getValue();
setSelectedUser(curUser);
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", curUser);
map.put("recordMode", "EDIT");
map.put("parentWindow",userList);
Executions.createComponents("UserCRUD.zul", null, map);
}

public void onView$UserListbox(ForwardEvent evt) {
Event origin = Events.getRealOrigin(evt);
Button btn = (Button) origin.getTarget();
Listitem litem = (Listitem) btn.getParent().getParent().getParent();
Appusers curUser = (Appusers) litem.getValue();
setSelectedUser(curUser);
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", curUser);
map.put("recordMode", "READ");
map.put("parentWindow",userList);
Executions.createComponents("UserCRUD.zul", null, map);
}


/**
* This method is actualy an event handler triggered only from the edit
* dialog and it is responsible to reflect the data changes made to the
* list.
*/
@SuppressWarnings({ "unchecked" })
public void onSaved(Event event) {
Map<String, Object> args = (Map<String, Object>) event.getData();
String recordMode = (String) args.get("recordMode");
Appusers curUser = (Appusers) args.get("selectedRecord");
if (recordMode.equals("NEW")) {
appUsersList.add(curUser);
}

if (recordMode.equals("EDIT")) {
appUsersList.set(appUsersList.indexOf(selectedUser),curUser);
}

}

@SuppressWarnings("unchecked")
public void onDelete$UserListbox(ForwardEvent evt) {
Event origin = Events.getRealOrigin(evt);
Button btn = (Button) origin.getTarget();
Listitem litem = (Listitem) btn.getParent().getParent().getParent();
Appusers curUser = (Appusers) litem.getValue();
setAppUsers(curUser);
Messagebox.show("Are you sure to delete the user..?", "Question",
Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION,
new org.zkoss.zk.ui.event.EventListener() {
public void onEvent(Event e) {
if (Messagebox.ON_OK.equals(e.getName())) {

CRUDService.delete(appUsers);
// update the model of listbox
appUsersList.remove(appUsers);
setAppUsers(null);

} else if (Messagebox.ON_CANCEL.equals(e.getName())) {
// Cancel is clicked
}
}
});
}

public BindingListModelList<Appusers> getAppUsersList() {
return appUsersList;
}

public void setAppUsersList(BindingListModelList<Appusers> appUsersList) {
this.appUsersList = appUsersList;
}

public Appusers getSelectedUser() {
return selectedUser;
}

public void setSelectedUser(Appusers selectedUser) {
this.selectedUser = selectedUser;
}

public Appusers getAppUsers() {
return appUsers;
}

public void setAppUsers(Appusers appUsers) {
this.appUsers = appUsers;
}

}

Step 3:
Next we will create our CRUD Screen where we will add/edit new users. Now select the webapp folder and right click-> Select New->Other->Zul file and enter the name of the zul file as UserCRUD.zul
Copy the below code.


<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./userCRUD" ?>
<zk>
<window title="Users" id="userCRUD" border="normal" width="30%"
apply="crudexample.controller.UserCRUDController" mode="modal"
maximizable="false" closable="true" sclass="mymodal">
<panel width="100%">
<panelchildren>
<separator />
<grid sclass="vgrid">
<columns>
<column></column>
</columns>
<rows>
<row>
<vlayout>
<label value="First Name" />
<textbox id="firstName" hflex="1"
value="@{controller.selectedUser.firstName, access='both', save-when='none'}"
readonly="@{controller.makeAsReadOnly}" mold="rounded" />
</vlayout>
</row>
<row>
<vlayout>
<label value="Last Name" />
<textbox id="LastName" hflex="1"
value="@{controller.selectedUser.lastName, access='both', save-when='none'}"
readonly="@{controller.makeAsReadOnly}" mold="rounded" />
</vlayout>
</row>
<row>
<vlayout>
<label value="User ID" />
<textbox id="UserID" hflex="1"
value="@{controller.selectedUser.userID, access='both', save-when='none'}"
readonly="@{controller.makeAsReadOnly}" mold="rounded" />
</vlayout>
</row>
<row>
<vlayout>
<label value="Password" />
<textbox id="Password" hflex="1"
value="@{controller.selectedUser.password, access='both', save-when='none'}"
readonly="@{controller.makeAsReadOnly}" mold="rounded" />
</vlayout>
</row>

</rows>
</grid>
</panelchildren>
</panel>
<separator />
<separator />
<separator />
<div align="center">
<button label="Save" mold="trendy" id="save"
sclass="mybutton button blue small" />
<button label="Cancel" mold="trendy" id="cancel"
sclass="mybutton button blue small" />
</div>
<separator />
</window>
</zk>

Step 4:
Next we will define the controller for the above zul file. Select the package controller and right click->Select New->Other->Class and enter the class name as “UserCRUDController”
Here is the code



package crudexample.controller;

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

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.databind.AnnotateDataBinder;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Button;
import org.zkoss.zul.Window;

import crudexample.domain.Appusers;
import crudexample.service.CRUDService;

@SuppressWarnings({ "rawtypes", "serial" })
public class UserCRUDController extends GenericForwardComposer {

@WireVariable
private CRUDService CRUDService;

private Appusers selectedUser;
private String recordMode;
private AnnotateDataBinder binder;
protected Window userCRUD; // autowired
protected Button save; // autowired
protected Button cancel; // autowired
private Window parentWindow;
private boolean makeAsReadOnly;

@SuppressWarnings("unchecked")
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");
this.self.setAttribute("controller", this, false);

final Execution execution = Executions.getCurrent();
setSelectedUser((Appusers) execution.getArg().get("selectedRecord"));
setRecordMode((String) execution.getArg().get("recordMode"));
setParentWindow((Window) execution.getArg().get("parentWindow"));
setMakeAsReadOnly(false);
if (recordMode.equals("NEW")) {
this.selectedUser = new Appusers();
}
if (recordMode.equals("EDIT")) {
this.selectedUser = getSelectedUser();
}
if (recordMode == "READ") {
setMakeAsReadOnly(true);
save.setVisible(false);
cancel.setLabel("Ok");
this.selectedUser = getSelectedUser();
userCRUD.setTitle(userCRUD.getTitle() + " (Readonly)");
}

}

public void onCreate(Event event) {
this.binder = (AnnotateDataBinder) event.getTarget().getAttribute(
"binder", true);
}

public void onClick$save(Event event) {
Map<String, Object> args = new HashMap<String, Object>();
binder.saveAll();
CRUDService.save(this.selectedUser);
userCRUD.detach();
args.put("selectedRecord", getSelectedUser());
args.put("recordMode", this.recordMode);
Events.sendEvent(new Event("onSaved", parentWindow,args));
}

public void onClick$cancel(Event event) {
userCRUD.detach();
}

public Appusers getSelectedUser() {
return selectedUser;
}

public void setSelectedUser(Appusers selectedUser) {
this.selectedUser = selectedUser;
}

public String getRecordMode() {
return recordMode;
}

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

public Window getParentWindow() {
return parentWindow;
}

public void setParentWindow(Window parentWindow) {
this.parentWindow = parentWindow;
}

public boolean isMakeAsReadOnly() {
return makeAsReadOnly;
}

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


That’s all. Now you can select the userlist.zul and run using tomcat server.

Output:
image

image




We have completed all our CRUD operation. Now we will see how to override the default ZK CSS and change the look and feel our application.
Changing the style of any ZK Component is pretty easy, all we need to override the css class and define our CSS Properties. There are different ways to you can change the CSS as described here. In this , we are going to define sclass styling.



First let us create the following two folders in webapp folder. Folders are “css” and “images” as shown.

image

Under the folder css, let us create css file named as “style.css” and copy the code from here.
Now let us refer the above CSS in our project. In order to do, please do the following steps.
1. Add ZKAddon.xml(it can be any name), under the webapp folder as shown

image


Copy the below code.

<?xml version="1.0" encoding="UTF-8"?>
<language-addon>
        <addon-name>myaddon</addon-name>
        <language-name>xul/html</language-name>
        <stylesheet href="/css/style.css" type="text/css" />
 
</language-addon>


2. Open the zk.xml and add the following line

<?xml version="1.0" encoding="UTF-8"?>
<zk>
        <language-config>
               <addon-uri>/ZKAddon.xml</addon-uri>
        </language-config>
</zk>





Finally, instead of the button, we will use image for the action. Change as follows.
Modify the userList.zul as follows
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./userList" ?>
<window width="100%" border="none" id="userList"
apply="crudexample.controller.UserListContrroller">
<separator />
<separator />
<div width="100%">
<div sclass="sectionTitle">
<separator />
<label value="Address Book" sclass="sectionTitleLabel" />
<separator />
</div>
<div style="float:right">
<button label="Add New" mold="trendy" id="btnNew"
sclass="mybutton button blue small" />
</div>
<div style="clear: both;"></div>
<div sclass="sectionSeperator"></div>
</div>
<separator />
<listbox id="UserListbox" model="@{controller.appUsersList}"
sclass="mylist" selectedItem="@{controller.selectedUser}"
multiple="false" emptyMessage="No User found in the database">
<listhead sizable="true">
<listheader label="User ID" sort="auto" />
<listheader label="Last Name" sort="auto" />
<listheader label="First Name" sort="auto" />
<listheader label="Action" />
</listhead>

<listitem self="@{each='users'}" value="@{users}"
forward="onDoubleClick=onDoubleClickedPersonItem">
<listcell label="@{users.userID}" />
<listcell label="@{users.lastName}" />
<listcell label="@{users.firstName}" />
<listcell>
<hbox spacing="20px">
<image forward="onClick=UserListbox.onEdit"
sclass="fimageedit">
</image>
<image forward="onClick=UserListbox.onView"
sclass="fimageView">
</image>

<image forward="onClick=UserListbox.onDelete"
sclass="fimageDelete">
</image>
</hbox>
</listcell>
</listitem>

</listbox>


</window>



Modify the UserListContrroller.java as follows


package crudexample.controller;

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

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.Events;
import org.zkoss.zk.ui.event.ForwardEvent;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.databind.AnnotateDataBinder;
import org.zkoss.zkplus.databind.BindingListModelList;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Button;
import org.zkoss.zul.Image;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;

import crudexample.domain.Appusers;
import crudexample.service.CRUDService;

@SuppressWarnings("rawtypes")
public class UserListContrroller extends GenericForwardComposer {

@WireVariable
private CRUDService CRUDService;

private static final long serialVersionUID = 1L;

protected Listbox UserListbox; // autowired
protected Window userList; // autowired

// Databinding
private AnnotateDataBinder binder;
private BindingListModelList<Appusers> appUsersList;
private Appusers appUsers;
private Appusers selectedUser;

public UserListContrroller() {
super();
}

@Override
public void doAfterCompose(Component window) throws Exception {
super.doAfterCompose(window);
this.self.setAttribute("controller", this, false);
}

public void onCreate$userList(Event event) throws Exception {

this.binder = (AnnotateDataBinder) event.getTarget().getAttribute(
"binder", true);

setSelectedUser(null);
doFillListbox();

this.binder.loadAll();
}

public void doFillListbox() {

CRUDService = (CRUDService) SpringUtil.getBean("CRUDService");
List<Appusers> list = CRUDService.getAll(Appusers.class);
appUsersList = new BindingListModelList<Appusers>(list, true);
setAppUsersList(appUsersList);
this.UserListbox.setModel(appUsersList);
}

/*This method will be called when user press the new button in the Listing screen*/

public void onClick$btnNew(Event event) {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", null);
map.put("recordMode", "NEW");
map.put("parentWindow",userList);
Executions.createComponents("UserCRUD.zul", null, map);
}

public void onEdit$UserListbox(ForwardEvent evt) {
Event origin = Events.getRealOrigin(evt);
Image btn = (Image) origin.getTarget();
Listitem litem = (Listitem) btn.getParent().getParent().getParent();
Appusers curUser = (Appusers) litem.getValue();
setSelectedUser(curUser);
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", curUser);
map.put("recordMode", "EDIT");
map.put("parentWindow",userList);
Executions.createComponents("UserCRUD.zul", null, map);
}

public void onView$UserListbox(ForwardEvent evt) {
Event origin = Events.getRealOrigin(evt);
Image btn = (Image) origin.getTarget();
Listitem litem = (Listitem) btn.getParent().getParent().getParent();
Appusers curUser = (Appusers) litem.getValue();
setSelectedUser(curUser);
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put("selectedRecord", curUser);
map.put("recordMode", "READ");
map.put("parentWindow",userList);
Executions.createComponents("UserCRUD.zul", null, map);
}


/**
* This method is actualy an event handler triggered only from the edit
* dialog and it is responsible to reflect the data changes made to the
* list.
*/
@SuppressWarnings({ "unchecked" })
public void onSaved(Event event) {
Map<String, Object> args = (Map<String, Object>) event.getData();
String recordMode = (String) args.get("recordMode");
Appusers curUser = (Appusers) args.get("selectedRecord");
if (recordMode.equals("NEW")) {
appUsersList.add(curUser);
}

if (recordMode.equals("EDIT")) {
appUsersList.set(appUsersList.indexOf(selectedUser),curUser);
}

}

@SuppressWarnings("unchecked")
public void onDelete$UserListbox(ForwardEvent evt) {
Event origin = Events.getRealOrigin(evt);
Image btn = (Image) origin.getTarget();
Listitem litem = (Listitem) btn.getParent().getParent().getParent();
Appusers curUser = (Appusers) litem.getValue();
setAppUsers(curUser);
Messagebox.show("Are you sure to delete the user..?", "Question",
Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION,
new org.zkoss.zk.ui.event.EventListener() {
public void onEvent(Event e) {
if (Messagebox.ON_OK.equals(e.getName())) {

CRUDService.delete(appUsers);
// update the model of listbox
appUsersList.remove(appUsers);
setAppUsers(null);

} else if (Messagebox.ON_CANCEL.equals(e.getName())) {
// Cancel is clicked
}
}
});
}

public BindingListModelList<Appusers> getAppUsersList() {
return appUsersList;
}

public void setAppUsersList(BindingListModelList<Appusers> appUsersList) {
this.appUsersList = appUsersList;
}

public Appusers getSelectedUser() {
return selectedUser;
}

public void setSelectedUser(Appusers selectedUser) {
this.selectedUser = selectedUser;
}

public Appusers getAppUsers() {
return appUsers;
}

public void setAppUsers(Appusers appUsers) {
this.appUsers = appUsers;
}

}



Now you can run the application and you can see the following output
image

image




You can download the source code here.



    

5 comments:

  1. Appreciated for your time to do all the work and posting. Working fine initially but when I add the last CSS then getting 404 error for all the files.

    ReplyDelete
  2. working fine with css too

    ReplyDelete
  3. I'm get this error : FAIL - Deployed application at context path / but context failed to start

    ReplyDelete
  4. Note : I import this project to NetBeans..

    ReplyDelete
  5. I m getting this error :
    org.zkoss.zk.ui.UiException: CRUDExample.MyViewModel
    cause mère
    java.lang.ClassNotFoundException: CRUDExample.MyViewModel
    any help please.

    ReplyDelete