0

How can I programmatically check the dirty state of all/certain ZK6 Bind forms in a page?

asked 2012-03-29 08:58:52 +0800

Matze2 gravatar image Matze2
773 7

updated 2013-05-21 08:43:47 +0800

benbai gravatar image benbai
2228 6
http://www.zkoss.org

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.

delete flag offensive retag edit

13 Answers

Sort by ยป oldest newest most voted
1

answered 2013-05-21 05:26:04 +0800

benbai gravatar image benbai
2228 6
http://www.zkoss.org

@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;
    }
}
link publish delete flag offensive edit
0

answered 2012-04-05 20:29:24 +0800

Matze2 gravatar image Matze2
773 7

updated 2012-04-05 20:31:36 +0800

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.

link publish delete flag offensive edit
0

answered 2012-04-05 12:45:22 +0800

benbai gravatar image benbai
2228 6
http://www.zkoss.org

updated 2012-04-06 01:54:58 +0800

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:

ZKFiddle-Link

TestBinder.java
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;
}
}


TestForm.java
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;
}
}


TestVM.java
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";
}
}


index.zul
<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

link publish delete flag offensive edit
0

answered 2012-04-02 11:29:57 +0800

Matze2 gravatar image Matze2
773 7

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.

link publish delete flag offensive edit
0

answered 2012-04-02 11:17:17 +0800

Matze2 gravatar image Matze2
773 7

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());
		...

link publish delete flag offensive edit
0

answered 2012-04-02 08:33:21 +0800

benbai gravatar image benbai
2228 6
http://www.zkoss.org

updated 2012-04-02 09:25:28 +0800

Hi,

The form is stored as an attribute of the root component of it by its id,
please refer to the sample:

ZKFiddle-Link

TestForm.java
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;
}
}


TestVM.java
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";
}
}


index.zul
<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

link publish delete flag offensive edit
0

answered 2013-05-20 14:19:19 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2623 3 8
http://emrpms.blogspot.in...

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 ?

link publish delete flag offensive edit
0

answered 2013-05-21 08:27:49 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2623 3 8
http://emrpms.blogspot.in...

updated 2013-05-21 08:45:54 +0800

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

form="@id('fx') @load(vm.selectedRecord) @save(vm.selectedRecord, before='saveThis')">

---

---

<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?).

link publish delete flag offensive edit

Comments

What is done in ShowWindow.showInsuranceLookup? If it changes the value of data bean directly, then the changed values will not cause the dirty status.

benbai ( 2013-05-21 08:45:32 +0800 )edit
0

answered 2013-05-21 08:47:22 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2623 3 8
http://emrpms.blogspot.in...

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?).

link publish delete flag offensive edit
0

answered 2013-05-21 14:23:41 +0800

benbai gravatar image benbai
2228 6
http://www.zkoss.org

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.

link publish delete flag offensive edit
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow

RSS

Stats

Asked: 2012-03-29 08:58:52 +0800

Seen: 534 times

Last updated: Jul 13 '13

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More