answered
2016-04-18 11:51:07 +0800
chillworld 5367 ● 4 ● 9
https://github.com/chillw...
I can give a work around for the getting each value of the subsubBean :
public SubBean() {
strings = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("One");
add("Two");
add("Three");
add("Four");
add("Five");
add("Six");
add("Seven");
add("Eight");
add("Nine");
add("Ten");
}
};
subSubBeans = new HashMap<String, SubSubBean>() {
@Override
public SubSubBean get(Object key) {
SubSubBean subSubBean = super.get(key);
if (subSubBean == null) {
subSubBean = new SubSubBean();
put((String)key, subSubBean);
}
return subSubBean;
}
};
}
So actually, I'm overriding the get of the HashMap and put your initializing code there.
Like this you could use the EL expressions in the zul :
<textbox value="@bind(bean.subSubBeans[each].value)" hflex="1" />
For the formStatus, I inspected a little deeper.
First of all, I added a command with the onChange and passed the form status to the viewmodel.
Like this I could inspect if the form status was updated correctly or not.
I know it looks strange but take in mind that if the binder don't get notified, your status is also never updated.
The result is that the dirty status is correct. => so the binder isn't notified.
So a temp workaround :
@Command
public void change(@BindingParam("proxy")Form form) {
System.out.println(form.getFormStatus().isDirty());
BindUtils.postNotifyChange(null, null, form.getFormStatus(), ".");
}
and in the zul :
<textbox value="@bind(bean.subSubBeans[each].value)" onChange="@command('change', proxy = fx)" hflex="1" />
I hope this can help you, I know it's pitty you have to send the fx to your viewmodel.
Update:
Looks like I was a little to fast.
The first problem is resolved, but only when you mark the method getSubSubBeans
@Immutable, otherwise you will never get in your get
method.
Now the second problem can't(yet) be fixed when the @Immutable is on the getter.
If you go back to the previous with getSubSubBean(each) and remove the @Immutable => the solution will work perfect, but the first issue is back.
I'll be looking for a whole solution, maybe the best to fill the map directly with the possible values.
Solution :
Oke, after a good night sleep, I had new idea's to try.
First of all, I need to add some kind of warning :
You proxy the map so the get method is also proxied.
By this, it means that the get method actually will never trigger the real get
method so putting the code in the Map
is a bad idea, and will give problems later on.
Besides that, initializing a bean when the form object is already created => this means that your form object is directly dirty.
In our solution, we don't see it by the problem of notifying the dirty status on deeper objects.
But remember, when a fix for this will come => save button is enabled when you load the page(at least when 1 SubSubBean needed to be created).
Let's go further with the solution :
So somewhere I was thinking the problem could be that the binder see's bean.getSubSubBean(each)
as always the same call, therefore calling it just once.
So I did go back to normal Map
and moved the getSubSubBean
method to the vm.
public SubSubBean getBean(SubBean bean, String key) {
System.out.println("getting bean");
SubSubBean subSubBean = bean.getSubSubBeans().get(key);
if (subSubBean == null) {
subSubBean = new SubSubBean();
bean.getSubSubBeans().put((String) key, subSubBean);
}
return subSubBean;
}
Oke, this works in combination with the dirty status fix.
Now I'm thinking that this code doesn't belong in the VM, so I need to move it.
As you can see, it's a kind of helper method we created.
So next thing I did is making a helper class.
public final class HelperClass {
public static Object getBean(SubBean bean, String key) {
SubSubBean subSubBean = bean.getSubSubBeans().get(key);
if (subSubBean == null) {
subSubBean = new SubSubBean();
bean.getSubSubBeans().put((String) key, subSubBean);
}
return subSubBean;
}
}
Now, the code is a little more separated.
How do we use this in code, well we have 2 options.
Prior ZK 8 we needed a taglib to call this method from a zul.
So start with creating a tld file (WEB-INF/tld):
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>helper</short-name>
<uri>/tld/helper</uri>
<function>
<name>getBean</name>
<function-class>zk.method.template.ref.form.binding.HelperClass</function-class>
<function-signature>
java.lang.Object getBean(zk.method.template.ref.form.binding.SubBean bean, java.lang.String key)
</function-signature>
<description>
Return the bean from a proxy object or create a new one.
</description>
</function>
</taglib>
and in the zul :
<?taglib uri="/WEB-INF/tld/helper.tld" prefix="help"?>
<textbox value="@bind(help:getBean(bean,each).value)" onChange="@command('change', proxy=fx)" hflex="1" />
Works like a charm.
With ZK8 we have now acces to static method's in the zul, so we don't need the TLD file but we can do this :
<?import class="zk.method.template.ref.form.binding.HelperClass" ?>
<textbox value="@bind(HelperClass.getBean(bean,each).value)" onChange="@command('change', proxy=fx)" hflex="1" />
Remember to set the import, that's really important.
So the both solutions works as you desired.
Hope this work around could fit in your design.
Greetz Chill.
Mathieu, you fill the map subsubbeans when you call the getter of the map. Does this need to be like that or can I call the entry of the map directly in the zul? (without the
chillworld ( 2016-04-18 06:51:55 +0800 )editgetSubSubBean(each)
)Yes, filling the map in getSubSubBean() is a requirement. In the real app, the map is partially filled but getSubSubBean() can never return null. It would be the case if I accessed directly to the map, and I don't want that logic (check null, instantiate and put in map if so) to be in the zul.
MathieuPiette ( 2016-04-18 09:43:37 +0800 )editThank you chillworld for your answer, this was the right thing to do, though I still don't get why the function was called only once. Form status sadly doesn't work either.
MathieuPiette ( 2016-04-19 09:14:15 +0800 )editfound a workaround.
chillworld ( 2016-04-19 10:11:36 +0800 )editThanks Chillworld and cor3000. Tried to rename the getSubSubBean() method or annotate it as @Transient, but it didn't change anything. The helper class trick doesn't work because the SubBean is a proxy and the method is not found, how did you manage to run it?
MathieuPiette ( 2016-04-20 09:58:06 +0800 )edit