Monday, 13 January 2014

ZK MVVM Combo Box value Converter

Normally, Combo box will be loaded with the List of any objects and selected item will be bind into one of the property of the main class. Here is the typical example

Department.java (this will be shown as Drop down)

package zkexamples.mvvm;

public class Department {

private Long ID;
private String departmentName;

public Department(Long ID, String departmentName)
{
this.ID = ID;
this.departmentName = departmentName;
}
public Long getID() {
return ID;
}

public void setID(Long iD) {
ID = iD;
}

public String getDepartmentName() {
return departmentName;
}

public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}

}

Employer.java


package zkexamples.mvvm;

public class Employer {

private String code;
private String firstName;
private String lastName;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "departmentID")
private Department department;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Department getDepartment() {
return department;
}

public void setDepartment(Department department) {
this.department = department;
}



}


Here is the zul which shows the example


<?page title="Auto Generated index.zul"?>
<window title="Example for ZK MVVM Combo Box" width="800px"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkexamples.mvvm.IndexVM')">
<label value="You are using: ${desktop.webApp.version}" />
<separator></separator>
<grid>
<columns>
<column width="10%"></column>
<column width="12%"></column>
<column width="16%"></column>
<column width="16%"></column>
</columns>
<rows>
<row>
<vlayout>
<label value="Code" />
<textbox id="code" hflex="1" name="code"
value="@bind(vm.employer.code)" maxlength="25" />
</vlayout>
<vlayout>
<label value="First Name" />
<textbox id="firstname" hflex="1" name="firstName"
value="@bind(vm.employer.firstName)" maxlength="100" />
</vlayout>
<vlayout>
<label value="Last Name" />
<textbox id="lastname" hflex="1" name="lastname"
value="@bind(vm.employer.lastName)" maxlength="100" />
</vlayout>

<vlayout>
<label value="Department" />
<combobox model="@load(vm.allDepartments)" hflex="1"
selectedItem="@bind(vm.employer.department)"
mold="rounded" autodrop="true" autocomplete="true">
<template name="model">
<comboitem
label="@load(each.departmentName)" value="@load(each.ID)" />
</template>
</combobox>
</vlayout>
</row>
</rows>
</grid>
<separator></separator>
<separator></separator>
<button label="Show Data" onClick="@command('saveThis')"></button>
</window>




But, for some reason, if we do not want to use ManytoOne or OneToMany fetch, and just we need the departmentID, then there is no simple way to do with ZK Combo. We need to write the converter.

Now let us modify the Employer class as follows


package zkexamples.mvvm;

public class Employer {

private String code;
private String firstName;
private String lastName;
private Long departmentID;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Long getDepartmentID() {
return departmentID;
}

public void setDepartmentID(Long departmentID) {
this.departmentID = departmentID;
}

}


Department.class remains the same, now let us modify the zul file


<?page title="Auto Generated index.zul"?>
<window title="Example for ZK MVVM Combo Box" width="800px"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('zkexamples.mvvm.IndexVM')">
<label value="You are using: ${desktop.webApp.version}" />
<separator></separator>
<grid>
<columns>
<column width="10%"></column>
<column width="12%"></column>
<column width="16%"></column>
<column width="16%"></column>
</columns>
<rows>
<row>
<vlayout>
<label value="Code" />
<textbox id="code" hflex="1" name="code"
value="@bind(vm.employer.code)" maxlength="25" />
</vlayout>
<vlayout>
<label value="First Name" />
<textbox id="firstname" hflex="1" name="firstName"
value="@bind(vm.employer.firstName)" maxlength="100" />
</vlayout>
<vlayout>
<label value="Last Name" />
<textbox id="lastname" hflex="1" name="lastname"
value="@bind(vm.employer.lastName)" maxlength="100" />
</vlayout>

<vlayout>
<label value="Department" />
<combobox model="@load(vm.allDepartments)" hflex="1"
value="@bind(vm.employer.departmentID) @converter('zkexamples.mvvm.DepartmentConverter')"
mold="rounded" autodrop="true" autocomplete="true">
<template name="model">
<comboitem
label="@load(each.departmentName)" value="@load(each.ID)" />
</template>
</combobox>
</vlayout>
</row>
</rows>
</grid>
<separator></separator>
<separator></separator>
<button label="Show Data" onClick="@command('saveThis')"></button>
</window>


As you can see, we have the converter in the value property as follows

value="@bind(vm.employer.departmentID) @converter('zkexamples.mvvm.DepartmentConverter')"

Here is the converter code

package zkexamples.mvvm;

import java.util.Iterator;
import java.util.List;

import org.zkoss.bind.BindContext;
import org.zkoss.bind.Converter;
import org.zkoss.zk.ui.Component;
import org.zkoss.zul.Combobox;

public class DepartmentConverter implements Converter {

@SuppressWarnings({ "unchecked" })
public Object coerceToUi(Object val, Component comp, BindContext ctx) {
Combobox box;
box = (Combobox) comp;
if (box.getSelectedItem() == null) {
String departmentName = null;
if (val != null) {
List<Department> locationList = (List<Department>) box
.getModel();
for (Iterator<Department> i = locationList.iterator(); i
.hasNext();) {
Department tmp = i.next();
if (tmp.getID() == (Long) val) {
departmentName = tmp.getDepartmentName();
}
}
}
return departmentName;
} else
return box.getSelectedItem().getLabel();
}

public Object coerceToBean(Object val, Component comp, BindContext ctx) {

Combobox box;
box = (Combobox) comp;

if (box.getSelectedItem() == null)
return val;
else
return box.getSelectedItem().getValue();

}
}


Here viewModel


package zkexamples.mvvm;

import java.util.List;

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.zk.ui.Component;
import org.zkoss.zul.Messagebox;

public class IndexVM {

private List<Department> allDepartments;
private Employer employer = new Employer();

public Employer getEmployer() {
return employer;
}

public void setEmployer(Employer employer) {
this.employer = employer;
}

public List<Department> getAllDepartments() {
return allDepartments;
}

public void setAllDepartments(List<Department> allDepartments) {
this.allDepartments = allDepartments;
}

@AfterCompose
public void initSetup(@ContextParam(ContextType.VIEW) Component view) {
allDepartments = DemoData.getAllDepartment();
}

@Command
public void saveThis() {

String data = "";
data = "Code :" + this.employer.getCode() + " , " + "First Name :"
+ this.employer.getFirstName() + " , "
+ this.employer.getLastName() + " , " + " Department : "
+ this.employer.getDepartmentID();
Messagebox.show(data);
}
}


Now we will get the departmentID.

image

Refer the following ZK Forum also
http://forum.zkoss.org/question/79517/combobox-selecteditem-mvvm-databind-problem

http://forum.zkoss.org/question/86919/zk-mvvm-combox-box-data-binding/


http://forum.zkoss.org/question/61956/set-combobox-selected-item-by-value

3 comments:

  1. what is DemoData in initSetup class ?
    Can you tell me about ZK Combo not have converter ?

    ReplyDelete
  2. Already set the selectedItem but it return null

    ReplyDelete