-
FEATURED COMPONENTS
First time here? Check out the FAQ!
Hi,
I have a main windows with a list box to show a list of records. When a row is selected, another modal popup window for editing the selected record will be triggered by right click the context menu item. I have databinding in the main window and I expect all changes made in the modal popup window will be reflected back to the main window. Unfortunately, it is not the case. Can I use any databinding concepts to tackle this problem ?
Appreciate anyone could give me a hand and resolve the subject problem. Million Thanks. / Jonathan
Main Window
<?page title="School" contentType="text/html;charset=UTF-8"?> <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?> <zk> <window id="schoolctlview" title="School" border="normal" apply="model.SchoolViewCtl"> <listbox id="lbSchool" mold="paging" pageSize="10" selectedItem="@{selected}" context="GridMenu"> <auxhead sclass="category-center"> <auxheader colspan="1"> <image src="/img/funnel.png" /> <textbox id="filter21" /> </auxheader> <auxheader colspan="1"> <image src="/img/funnel.png" /> <textbox id="filter22" /> </auxheader> <auxheader colspan="1"> <image src="/img/funnel.png" /> <textbox id="filter23" /> </auxheader> <auxheader colspan="1"> <image src="/img/funnel.png" /> <textbox id="filter24" /> </auxheader> <auxheader colspan="1"> <image src="/img/funnel.png" /> <textbox id="filter25" /> </auxheader> </auxhead> <listhead> <listheader label="School Code" /> <listheader label="School Name" /> <listheader label="Tel" /> <listheader label="Fax" /> <listheader label="Email" /> </listhead> </listbox> <menupopup id="GridMenu"> <menuitem id="mitemView" label="View" /> </menupopup> </window> </zk>
<?page id="pageSchool"title="School" contentType="text/html;charset=UTF-8"?> <window id="wSchool" title="School" border="normal" mode="modal"> <zscript><![CDATA[ import model.School; School sch = (School)execution.getArg().get("data"); ]]></zscript> <label value="Name:" /> <textbox id="nameTb" value="@{sch.school_name}"/> <button label="OK" xmlns:w="http://www.zkoss.org/2005/zk/client" w:onClick="this.$f('wSchool').fire('onClose');"/> </window>
Control
package model; 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.SuspendNotAllowedException; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zkplus.databind.AnnotateDataBinder; import org.zkoss.zul.ListModel; import org.zkoss.zul.ListModelList; import org.zkoss.zul.Listbox; import org.zkoss.zul.Listcell; import org.zkoss.zul.Listitem; import org.zkoss.zul.ListitemRenderer; import org.zkoss.zul.Menuitem; import org.zkoss.zul.Window; public class SchoolViewCtl extends GenericForwardComposer { private Listbox lbSchool; private Menuitem mitemView; private AnnotateDataBinder binder; @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); List allSchools = new SchoolDAO().findAll(); ListModelList model = new ListModelList(allSchools); model.addSelection(allSchools.get(0)); lbSchool.setModel(model); lbSchool.setItemRenderer(new ListitemRenderer() { public void render(Listitem listItem, Object data) throws Exception { final School sch = (School) data; listItem.setValue(sch); new Listcell(sch.getSchool_code()).setParent(listItem); new Listcell(sch.getSchool_name()).setParent(listItem); new Listcell(sch.getTel()).setParent(listItem); new Listcell(sch.getFax()).setParent(listItem); new Listcell(sch.getEmail()).setParent(listItem); } }); } public void onClick$mitemView() { Window wSchool = new Window(); Map data = new HashMap(); data.put("data", (School)lbSchool.getSelectedItem().getValue()); wSchool = (Window)Executions.getCurrent().createComponents("school.zul", self, data); binder = new AnnotateDataBinder(wSchool); binder.loadAll(); } }
Hi Jonathan,
I 'm not sure if i understand correctly so let me recap. The school.zul is the modal dialog which is responsible to edit a school. As long as the user edits the selected school, you suppose that the execution in the SchoolViewCtl composer is paused and after closing the dialog the execution will continue to the following lines:
binder = new AnnotateDataBinder(wSchool); binder.loadAll();
Is that the case?
1. The only way to force execution pausing while working in a modal dialog is to declare the <disable-event-thread> parameter inside the zk.xml. But i would not recommend that! After all there is a reason it is disabled by default since zk 5.0
2. A proper and simple way would be to pass the SchoolViewCtl itself to the dialog as parameter inside the data Map. After finishing editing, the modal dialog should invoke a method in the SchoolViewCtl (lets say "updateListView()" ) to reflect the changes back to the list.
hope that helps
/costas
Hi Costas,
Million thanks for your reply. Your understanding to my question is exactly the same what I want to achieve. I am really interested in your point 2. Appreciate if you would share some lights by illustrating the handshake of data Map & updateListView(). I actually very green in developing using ZK and I found using ListitemRenderer from reference manual but not quite sure after I close the modal dialog how to refresh the new values captured.
Once again. Many thanks for your time and grateful to have your further guidence.
Jonathan
Jonathan,
You will get all you need if you follow Stephan's links... Actually these are more or less the same links i followed to learn zk (thx stephan!)
Anyway if you are more impatient you can read below a fully working example of how to edit a list using an edit dialog.
The main schools list page:
<?page title="Schools" contentType="text/html;charset=UTF-8"?> <zk> <window id="winSchools" title="Schools" border="normal" height="100%" width="100%" apply="schools.SchoolsListController"> <vlayout> <toolbar> <toolbarbutton id="tbtnEdit" label="Edit" image="/images/tb-edit.png" /> <toolbarbutton id="tbtnRemove" label="Remove" image="/images/tb-delete.png" /> </toolbar> <listbox id="lbSchools" model="@{ controller.schools }" selectedItem="@{ controller.selectedSchool }" vflex="true"> <listhead sizable="true"> <listheader label="Code" width="50px" /> <listheader label="Name" width="150px"/> <listheader label="Address" width="200px"/> </listhead> <listitem self="@{each='school'}" value="@{school}" > <listcell label="@{school.code}" /> <listcell label="@{school.name}" /> <listcell label="@{school.address}" /> </listitem> </listbox> </vlayout> </window> </zk>
The controller of the main schools page:
package schools; import java.util.ArrayList; 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.SuspendNotAllowedException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zkplus.databind.AnnotateDataBinder; import org.zkoss.zul.Messagebox; import org.zkoss.zul.api.Listbox; import org.zkoss.zul.api.Window; public class SchoolsListController extends GenericForwardComposer { private static final long serialVersionUID = 1L; private AnnotateDataBinder binder; private Window winSchools; private Listbox lbSchools; private List<School> schools; private School selectedSchool; @Override public void doAfterCompose(Component comp) throws Exception { try { super.doAfterCompose(comp); } catch (Exception e) { e.printStackTrace(); } /* * create the data binder and bind this composer as the base model bean */ binder = new AnnotateDataBinder(comp); binder.bindBean("controller", this); /* * populate data models */ createModels(); } public void onCreate(Event event) { /* * populate ui with data */ binder.loadAll(); } /* * Create some mock data models */ private void createModels() { schools = new ArrayList<School>(); schools.add(new School("1", "1st school", "address-1")); schools.add(new School("2", "2nd school", "address-2")); schools.add(new School("3", "3rd school", "address-3")); schools.add(new School("4", "4th school", "address-4")); schools.add(new School("5", "5th school", "address-5")); schools.add(new School("6", "6th school", "address-6")); schools.add(new School("7", "7th school", "address-7")); schools.add(new School("8", "8th school", "address-8")); schools.add(new School("9", "9th school", "address-9")); } /** * Event handler method triggered when the "edit" toolbar button is pressed */ public void onClick$tbtnEdit() { if (selectedSchool == null) { try { Messagebox.show("You have to select a school from the list first.", "Error", Messagebox.OK, Messagebox.ERROR); } catch (InterruptedException e) { e.printStackTrace(); } return; } Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("SELECTED_SCHOOL", selectedSchool); parameters.put("PARENT_WINDOW", winSchools); Window editDialog = (Window) Executions.createComponents("/schools/SchoolEditor.zul", winSchools, parameters); try { editDialog.doModal(); } catch (SuspendNotAllowedException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 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. */ public void onSchoolSaved() { int index = lbSchools.getSelectedIndex(); if (index == -1) return; binder.loadComponent( lbSchools.getItemAtIndexApi(index) ); } /** * Event handler method triggered when the "remove" toolbar button is pressed */ public void onClick$tbtnRemove() { if (selectedSchool == null) { try { Messagebox.show("You have to select a school from the list first.", "Error", Messagebox.OK, Messagebox.ERROR); } catch (InterruptedException e) { e.printStackTrace(); } return; } try { /* * This event listener is needed to catch the response given by the user in the confirmation dialog */ EventListener eventListener = new EventListener() { @Override public void onEvent(Event event) throws Exception { switch (((Integer) event.getData()).intValue()) { case Messagebox.OK: deleteSchool(); break; } } }; Messagebox.show("Are you sure you want to delete " + selectedSchool.getName() + "?", "Warning", Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION, eventListener); } catch (InterruptedException e) { e.printStackTrace(); } } /** * Delete the selected school */ private void deleteSchool() { schools.remove(selectedSchool); selectedSchool = null; binder.loadComponent(lbSchools); } public AnnotateDataBinder getBinder() { return binder; } public void setBinder(AnnotateDataBinder binder) { this.binder = binder; } public List<School> getSchools() { return schools; } public void setSchools(List<School> schools) { this.schools = schools; } public School getSelectedSchool() { return selectedSchool; } public void setSelectedSchool(School selectedSchool) { this.selectedSchool = selectedSchool; } }
The edit dialog page:
<?page title="Schools" contentType="text/html;charset=UTF-8"?> <zk> <window id="winEditor" title="School Editor" border="normal" height="350px" width="400px" closable="true" apply="schools.SchoolEditorController"> <vlayout> <toolbar> <toolbarbutton id="tbtnSave" label="Save" image="/images/tb-save.png" /> <toolbarbutton id="tbtnExit" label="Exit" image="/images/tb-exit.png" /> </toolbar> <grid> <columns> <column width="100px"/> <column/> </columns> <rows> <row> <label value="Code" /> <textbox value="@{ controller.selectedSchool.code, access='both', save-when='none' }"/> </row> <row> <label value="Name" /> <textbox value="@{ controller.selectedSchool.name, access='both', save-when='none' }"/> </row> <row> <label value="Address" /> <textbox value="@{ controller.selectedSchool.address, access='both', save-when='none' }"/> </row> </rows> </grid> </vlayout> </window> </zk>
And finally the school editor composer:
package schools; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zkplus.databind.AnnotateDataBinder; import org.zkoss.zul.api.Window; public class SchoolEditorController extends GenericForwardComposer { private static final long serialVersionUID = 1L; private AnnotateDataBinder binder; private School selectedSchool; private Window parentWindow; private Window winEditor; @Override public void doAfterCompose(Component comp) throws Exception { try { super.doAfterCompose(comp); } catch (Exception e) { e.printStackTrace(); } /* * create the data binder and bind this composer as the base model bean */ binder = new AnnotateDataBinder(comp); binder.bindBean("controller", this); /* * retrieve parameters passed here by the caller */ selectedSchool = (School)arg.get("SELECTED_SCHOOL"); parentWindow = (Window)arg.get("PARENT_WINDOW"); } public void onCreate(Event event) { /* * populate ui with data */ binder.loadAll(); } /** * Event handler for closing this dialog using the "exit" toolbar button */ public void onClick$tbtnExit() { winEditor.detach(); } /** * Event handler for saving the model values */ public void onClick$tbtnSave() { binder.saveAll(); winEditor.detach(); Events.sendEvent(new Event("onSchoolSaved", parentWindow)); } public AnnotateDataBinder getBinder() { return binder; } public void setBinder(AnnotateDataBinder binder) { this.binder = binder; } public School getSelectedSchool() { return selectedSchool; } public void setSelectedSchool(School selectedSchool) { this.selectedSchool = selectedSchool; } }
Hope that helps
/Costas
wondering a trivial question. based on the code Costas provided, i try to add a new button to create a new school. This i thouht should be very simple by setting selectedSchool to new School(). However, no matter what had been entered into Modal Window, when I click save button, it always return a empty instance of School back to SchoolsListController. Any code / concepts I missed here? Really appreciate further assistance and I had been sitting and researching internet for 2 days. Many Thanks. / Jonathan.
only Pseudo-Code:
void doNew() { School school = new School(); setSelectedSchool(school); } You must told the binder to save the UI components data to the bean properties. (because in costas sample zul file is standing ... save-when='none'...) void doSave(){ getBinder.saveAll(); myDataBase.saveMyBeanData(getSelectedSchool()); }
best
Stephan
PS: Try to debug the article module in ZKsample2.
Online-demo: http://www.zk-web.de/zksample2/
Hi,
I have a problem similar to jchiu one.
I have a window that opens a modal by clicking a button; the modal contains a form and I want that after I fill in all the form, the modal closes itself and results are shown in the window below. In reality nothing of that happens and I have null pointer exception. I already read all post about this topic and I also studied terrytornado examples.... but nothing!
I really hope that someone could help me...
attivita.zul
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" ?> <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> <vlayout> <label sclass="title" value="${labels.attivita}" /> <window id="attivitaWindow" sclass="cardSys" border="none" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('it.iks.smash.web.controller.AttivitaVM')" > <!-- bottone per aprire la modal window della ricerca --> <div id="button" apply="it.iks.smash.web.controller.AttivitaVM"> <button id="apriModal" label="Apri modal window" /> </div> ...
AttivitaVM.java controller for attivita.zul
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class) public class AttivitaVM extends SelectorComposer<Component>{ private static final long serialVersionUID = 1L; static Logger log = Logger.getLogger(AttivitaVM.class.getName()); @WireVariable(value = "attivitaService") private IAttivitaService attivitaService; private List<AttivitaAbstract> listAttivita; private AttivitaAbstract selectedAttivita; private AttivitaDettaglio attivitaDettaglio; @Wire private Textbox userTextBox; @Wire private Textbox cosdiaTextBox; @Wire private Listbox attivita; private AnnotateDataBinder binder; public Listbox getAttivita() { return attivita; } @Wire private Window attivitaWindow; private QueryManagerVM queryManagerController; public void setAttivita(Listbox attivita) { this.attivita = attivita; } @Listen("onClick = #apriModal") public void showModal(Event e) { log.info("@Listen(onClick=#apriModal)"); Window queryManager= new Window(); final HashMap<String, Object> map = new HashMap<String, Object>(); map.put("AttivitaVM", this); queryManager = (Window)Executions.getCurrent().createComponents("/WEB-INF/view/query-manager.zul", attivitaWindow, map); queryManager.doModal(); binder = new AnnotateDataBinder(queryManager); binder.loadAll(); } .....
query-manager.zul: the zul of the modal window
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" ?> <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> <window id="queryManagerModal" closable="true" position="center,center" apply="org.zkoss.bind.BindComposer" title="Query Manager" border="normal" viewModel="@id('vm') @init('it.iks.smash.web.controller.QueryManagerVM')" validationMessage="@id('vmsg')" form="@id('fx') @load(vm.queryManagerBean) @save(vm.queryManagerBean, before='eseguiQuery') " > <vlayout> .... <div id="button" apply="it.iks.smash.web.controller.QueryManagerVM" > <button id="eseguiQuery" label="Ricerca" onClick="@Command('eseguiQuery')"/> </div> </vlayout> </window>
QueryManagerVM.java: controller for the modal window query-manager.zul
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class) public class QueryManagerVM extends SelectorComposer<Component>{ private static final long serialVersionUID = 1L; static Logger log = Logger.getLogger(AttivitaVM.class.getName()); @Wire("#queryManagerModal") private Window queryManagerModal; @WireVariable(value = "attivitaService") private IAttivitaService attivitaService; private AttivitaVM controllerAttivita;//controller della pagina delle attività private QueryManager queryManagerBean=new QueryManager(); .... @Init public void init(@ExecutionArgParam("AttivitaVM") AttivitaVM controllerAttivita) { log.info("init:: inizializzazione"); setControllerAttivita(controllerAttivita); log.info("doAfterCompose(Component comp):: setControllerAttivita()"); // la lista attività ha il controller della modalwindow getControllerAttivita().setQueryManagerController(this); log.info("doAfterCompose(Component comp):: getControllerAttivita()"); } @Listen("onClick=#eseguiQuery") public void search(){ log.info("onClick$eseguiQuery()"); if(queryManagerBean.getTextboxUser().isEmpty()) log.info("textboxuser vuota"); log.info("queryManagerBean.getTextboxUser() "+queryManagerBean.getTextboxUser()); HashMap<String, Condizione> condizioni=new HashMap<String, Condizione>(); condizioni=(HashMap<String, Condizione>) searchCheckTipoAttivita(); condizioni.putAll(searchCampiRicerca()); condizioni.putAll(searchBonificoRicerca()); if(condizioni.isEmpty()) log.info("onClick$eseguiQuery:: condizioni empty"); else log.info("onClick$eseguiQuery:: condizioni"+condizioni.keySet().toString()); List<AttivitaAbstract> listAttivita=attivitaService.ricerca(condizioni); for(int i=0; i<listAttivita.size(); i++){ log.info("search():: listAttività["+i+"]: "+listAttivita.get(i)+", "); } //getControllerAttivita().setLista(listAttivita); queryManagerModal.detach(); } .....
Thank you to anybody that could help me
Hi tita
you can refer to
http://www.zkoss.org/forum/listComment/19829-ZK-MVVM-Modal-Window
Asked: 2011-10-18 10:03:22 +0800
Seen: 2,274 times
Last updated: Feb 06 '13