-
FEATURED COMPONENTS
First time here? Check out the FAQ!
I have a page divided into multiple areas, each controlled by a ZK6 binding form. There is a "final" submit button which does the real storage of the data. In that handler I would llike to check the dirty state of all forms and raise a warning message ("Page contains modifications that won't be saved"). It is possible to show the dirty state in the page as described in this small talk. But how can I do this programmatically? I looked in the ZK6 binder implementation and there seems no easy way to do that.
@Senthil,
It depends what the 'lookup' means, if you change the component's value directly, you can then fire an onChange event as needed. If you change the value of backing bean, then the new value will be considered as 'clean' one.
e.g.,
zul
<zk>
<window apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('test.TestVM')">
<div form="@id('fx') @load(vm.data) @save(vm.data, before='doSave')">
<intbox readonly="true" id="ibxOne" value="@bind(fx.cost)" />
<intbox readonly="true" value="@bind(fx.amount)" />
<label value="dirty" visible="@load(fxStatus.dirty)" />
<div />
<button label="set value by command"
onClick="@command('changeValue')" />
will really change the value of backing bean, and the new value will be considered as 'clean'
<div />
<button label="set value directly">
<attribute name="onClick"><![CDATA[
ibxOne.setValue(10);
Events.postEvent("onChange", ibxOne, null);
]]></attribute>
</button>
will change the value of component, just need to fire an onChange event to trigger form binding mechanism
</div>
</window>
</zk>
TestVM.java
package test;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zul.ListModelList;
/**
* tested with ZK 6.5.1.1
*
* @author benbai
*
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class TestVM {
ListModelList _model;
TestData _data;
public TestData getData () {
if (_data == null) {
_data = new TestData(3, 5);
}
return _data;
}
@Command
public void doSave () {
}
@Command
@NotifyChange ("data")
public void changeValue () {
_data.setAmount(10);
}
}
TestData.java
package test;
public class TestData {
private int _cost;
private int _amount;
public TestData (int cost, int amount) {
_cost = cost;
_amount = amount;
}
public void setCost (int cost) {
_cost = cost;
}
public int getCost () {
return _cost;
}
public void setAmount (int amount) {
_amount = amount;
}
public int getAmount () {
return _amount;
}
public int getTotalPrice () {
return _cost * _amount;
}
}
I was not aware of the possibility to create custom binders. I think I have more use cases, e.g. to clear validateMessages on demand, or explicitly triggering validators (like the good old "dataBinder.saveAll()" in ZK Bind1).
Thanks! Will have a look into it.
Hi,
It can be slightly improved by use custom binder, use the id on the page and store the form in a map.
As the updated sample:
package j2cptkn6$v4;
import java.util.HashMap;import java.util.Map;
import org.zkoss.bind.AnnotateBinder;
import org.zkoss.bind.Form;
import org.zkoss.zk.ui.Component;public class TestBinder extends AnnotateBinder {
/**
*
*/
private static final long serialVersionUID = -6535823320662957857L;
private static final Map<Object, Form> _allForms = new HashMap<Object, Form>();
public Form getForm(Component comp,String id){
Form form = super.getForm(comp, id);
if (form != null) {
_allForms.put(id, form);
}
return form;
}
public static Form getFormByFormId (String id) {
if (_allForms.containsKey(id))
return _allForms.get(id);
return null;
}
}
package j2cptkn6$v4;
public class TestForm {
private String _id;
public TestForm () {
}
public TestForm (String id) {
setId(id);
}
public String getId () {
return _id;
}
public void setId (String id) {
_id = id;
}
}
package j2cptkn6$v4;import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
public class TestVM {
private TestForm _form = new TestForm ("id");
private String _dirtyMessage = "it is not dirty";
public TestForm getForm () {
return _form;
}
public void setForm (TestForm form) {
_form = form;
}
public String getDirtyMessage() {
return _dirtyMessage;
}
@Command @NotifyChange("dirtyMessage")
public void checkDirty () {
if (TestBinder.getFormByFormId("fx").isDirty())
_dirtyMessage = "from checkDirty: it is dirty";
else
_dirtyMessage = "from checkDirty: it is not dirty";
}
}
<zk>
<window apply="org.zkoss.bind.BindComposer" binder="@init('j2cptkn6$v4.TestBinder')"
viewModel="@id('vm') @init('j2cptkn6$v4.TestVM')">
<groupbox id="gbx" form="@id('fx') @load(vm.form) @save(vm.form, before='saveOrder')"
visible="@bind(not empty vm.form)" hflex="true" mold="3d">
<grid hflex="true" >
<columns>
<column width="120px"/>
<column/>
</columns>
<rows>
<row>
id: <textbox value="@bind(fx.id)" />
</row>
<row>
dirty? <textbox value="@load(vm.dirtyMessage)" width="300px" />
</row>
</rows>
</grid>
<button label="check dirty" onClick="@command('checkDirty')" />
</groupbox>
</window>
</zk>
Regards,
Ben
One way to solve this more cleanly would be a possibility to publish the local form/formStatus variables to the global context, i.e. to make them accessible via Binder or BindContext interface - and also as Data Binding expression.
Then you can also put your "dirty form indicators" outside of the form's container component. This is also not possible now.
Thanks, Ben.
In my current solution (which is quite similar to yours) I just wire the form's root component in my view model. This has the advantage that the form is still created by ZK and not in my view model.
This is not perfect for MVVM philosophy (which is why I was asking), but satisfies the needs of course.
Here is the relevant code:
@Wire private Hbox formContainer; ... @Command public void submit() { String formId = (String) formContainer.getAttribute("$FORM_ID$"); Form form = (Form) formContainer.getAttribute(formId); System.out.println("Form is dirty: " + form.isDirty()); ...
Hi,
The form is stored as an attribute of the root component of it by its id,
please refer to the sample:
package j2cptkn6$v2;
import java.util.*;import org.zkoss.lang.Objects;
public class TestForm {
private String _id;
private String _name;
public TestForm () {
}
public TestForm (String id) {
setId(id);
}
public String getId () {
return _id;
}
public void setId (String id) {
_id = id;
}
}
package j2cptkn6$v2;
import java.util.*;import org.zkoss.bind.BindContext;
import org.zkoss.bind.Form;
import org.zkoss.bind.ValidationContext;
import org.zkoss.bind.Validator;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.zul.*;
import org.zkoss.zk.ui.Component;
public class TestVM {
private TestForm _form = new TestForm ("id");
private String _dirtyMessage = "it is not dirty";
public TestForm getForm () {
return _form;
}
public void setForm (TestForm form) {
_form = form;
}
public String getDirtyMessage() {
return _dirtyMessage;
}
@Command @NotifyChange("dirtyMessage")
public void checkDirty (@ContextParam(ContextType.VIEW) Component ctx) {
if (((Form)ctx.getFellow("gbx").getAttribute("fx")).isDirty())
_dirtyMessage = "from checkDirty: it is dirty";
else
_dirtyMessage = "from checkDirty: it is not dirty";
}
}
<zk>
<window apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('j2cptkn6$v2.TestVM')">
<groupbox id="gbx" form="@id('fx') @load(vm.form) @save(vm.form, before='saveOrder')"
visible="@bind(not empty vm.form)" hflex="true" mold="3d">
<grid hflex="true" >
<columns>
<column width="120px"/>
<column/>
</columns>
<rows>
<row>
id: <textbox value="@bind(fx.id)" />
</row>
<row>
dirty? <textbox value="@load(vm.dirtyMessage)" width="300px" />
</row>
</rows>
</grid>
<button label="check dirty" onClick="@command('checkDirty')" />
</groupbox>
</window>
</zk>
you may also try maintain the dirty status by yourself with normal mvvm as the sample in the related thread:
http://www.zkoss.org/forum/listComment/18621-ZK-6-binding-to-css-properties
Regards,
Ben
Hi Benbai
I Need help here. I know that when user type some thing in the textbox and move focus, then the it will be dirty(we can show the image or change the text box forecolor). But in my scenario, textbox is readonly, and the value is filled using some lookup, now i want to make programmatically make dirty state of the form. How we can do this ?
Hi Benbai. Thank you. But in my case it is one to many relationship. Here is the code, but form status not changing to dirty.
Zul File
<vlayout> <label value="Insurance"/> <hbox width="80%"> <textbox id="insurance" readonly="true" value="@bind(fx.insurance)" hflex="1"/> <toolbarbutton id="searchInsurance" sclass="gridToolbarButton" image="/images/search.png" onclick="@command('showInsuranceLookUp')"/> </hbox> </vlayout>
VM
@Command
public void showInsuranceLookUp() {
ShowWindow.showInsuranceLookup();
}
// note this will be called from InsuranceLookup.java
@GlobalCommand
@NotifyChange({ "insuranceAddress", "." })
public void onSelectInsuranceLookup(
@BindingParam("selectedRecord") Insurance insurance,
@BindingParam("insuranceAddress") String insuranceAddress) {
this.currentInsurance = insurance;
this.insuranceAddress = insuranceAddress;
this.selectedRecord.setInsurance(currentInsurance);
}
I saw your code. On clicking of the first button, it is clean status. But on on clicking of second button, because of postevent, it becomes dirty state.
In my current project, scripting in the zul is disabled. So i have to trigger to the onchange event in the VM ? (But we are violating MVVM Concept?).
I saw your code. On clicking of the first button, it is clean status. But on on clicking of second button, because of postevent, it becomes dirty state.
In my current project, scripting in the zul is disabled. So i have to trigger to the onchange event in the VM ? (But we are violating MVVM Concept?).
In my opinion, postevent is not the reason that it becomes dirty state, "change the value of component instead of backing bean" is.
Assume there are two object, form and backing bean
when you change value of backing bean directly, the new value is set to backing bean then update to form, the value of form is the same as backing bean, so it will not becomes dirty state.
when you change value of component, the new value is set to component and form but not backing bean, the value of form is different with backing bean, so it will becomes dirty state.
the postevent is just for trigger mvvm mechanism to update the dirty state.
you can confirm this by remove postevent and readonly attribute, refresh page then click two buttons to change value, then change values back to old value with keybord and then blur to trigger onChange.
Asked: 2012-03-29 08:58:52 +0800
Seen: 534 times
Last updated: Jul 13 '13