-
FEATURED COMPONENTS
First time here? Check out the FAQ!
Hi folks from ZK Bind,
sometimes I have a large amount of listmodel objects. Only a subset of these are displayed on the screen. Until runtime decides which are to be reported.
The data for the lists I receive discontinuous.
For this reason, I did the listmodels not bound by name. The binding is done using a method from view model:
listbox model="@bind(vm.getLM(each))"
Unfortunately, all properties are bound internally with the name "getLM".
After receiving the data for a list so I can send only one notification for all lists.
org.zkoss.bind.BindUtils.postNotifyChange(null, null, this, "getLM");
Is it possible to extend the functionality of the ZK library so that objects can be bound directly?. Then i can call
org.zkoss.bind.BindUtils.postNotifyChange(null, null, this, lm);
Kai
Hmm, the preview shows me index-based acces as <i> or not at all. I hope you understand the code anyway
<zk> <zscript><![CDATA[ int[] involved= { 3, 7, 1, 9 }; // Simulate the involved listmodel objects class VM { private ListModelList[] models= new ListModelList[10]; public VM() { for (int i= 0; i< models.length; ++i) models<i >= new ListModelList(); } // Give the correct item from the list public ListModelList getLM(int index) { return models; } // Simulate asynchron data receive. Any data, which come from somewhere public void simulateData(int index) { int n_data= (int) (java.lang.Math.random()* 5+ 1); ListModelList lm= getLM(index); lm.clear(); for (int i= 0; i< n_data; ++i) { lm.add("Data "+ String.valueOf(i)+ " for ListModel "+ String.valueOf(index)); } org.zkoss.bind.BindUtils.postNotifyChange(null, null, this, "getLM"); } // Return a list containing the indices of the involved listmodel objects private java.util.List indexes_= null; public java.util.List getIndexes() { if (null== indexes_) { indexes_= new java.util.ArrayList(); for (int i= 0; i< involved.length; ++i) indexes_.add(new Integer(involved<i >)); } return indexes_; } } ]]></zscript> <window id="win" title="Window" border="normal" width="600px" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('VM')" > <grid> <rows children= "@load(vm.indexes)"> <template name="children"> <row > <label value="${each}"/> <listbox model="@bind(vm.getLM(each))"> <!-- Access ListModel via function getLM() --> <template name="model"> <listitem> <listcell label="@load(each)"/> </listitem> </template> </listbox> <button label= "Simulate data receive for Model ${each}" attributes.i="@load(each)"> <attribute name="onClick">vm.simulateData(((Integer)self.getAttribute("i")).intValue());</attribute> </button> </row> </template> </rows> </grid> </window> </zk>
Maybe use EventsQueue.lookup() in every ListModel is more direct and clear. Because, in my personal opinion, the fourth parameter of BindsUtil() is property name of bean. If it can pass any variable freely, the routine of NotifyChange will become complex and lose control.
btw, you can use
<button label="..." onClick="@command('simulateData', 3)" />
Hello MonthyPan, thanks for your answer.
Your hint to EventQueue.lookup () I do not understand yet. On the basis of which event I should call the method and for what purpose?
onCommand I normally use. At this point it was not because the Java code is in the zul file. BeanShell does, however, an error at Annontation.
FYI getLM() has the following implementation. The single quotes you have to think away. As I wrote, my source code is not displayed correctly. ???
public ListModelList getLM(int index) { return models'['index']'; }
If I NotifyChange @ ("lM") add, how then the correct index is passed to this method?
Kai
In every ListModel, use EVentQueue.lookup().subscribe() to catch event in EventQueue.
In the simulateData(), you use EventQueue.lookup().publish() to send an event.
Thus, when simulateData() is called, every ListModel will get the event to do something as your requirement.
NotifyChange("foo") will trigger @bind(vm.foo) or @load(vm.foo) in zul, they will call getFoo() in view model.
The code I provide is a pseudo code, maybe not fit your situation at all. (sorry)
Hi MonthyPan, I've changed the example again and hope that the problem will be shown better. Because Listbox processed INTERVAL_ADDED, all
items are updated automatically. However, if I children binding use, is not so. Only if I call postNotifyChange(). For the call but I need a unique name for the
bean property. All models, however, are bound with "getLM". This gives notification to all vlayout elements, although only one model has been updated.
I think that EventQueue does not help me. I think that UI elements with children binding should process LastDataEvents.
Kai
<zk> <zscript><![CDATA[ int[] involved= { 3, 7, 1, 9 }; // Simulate the involved listmodel objects String eq_name= "eq0"; class VM { private ListModelList[] models= new ListModelList[10]; boolean has_subscribed= false; public VM() { for (int i= 0; i< models.length; ++i) models<i >= new ListModelList(); } public ListModelList getLM(int index) { return models'['index']'; } // Simulate asynchron data receive. Any data, which come from somewhere public void simulateData(int index) { if (!has_subscribed) { has_subscribed= true; // Subscribe for each model is probably not necessary if (EventQueues.exists(eq_name)) EventQueues.lookup(eq_name).subscribe(new org.zkoss.zk.ui.event.EventListener() { public void onEvent(Event e) { if (e instanceof PropertyChangeEvent) { PropertyChangeEvent pe= (PropertyChangeEvent) e; System.out.print("\nProcess event: "+ e+ " base/property "+ pe.getBase()+ " / "+ pe.getProperty()); } else System.out.print("\nProcess event: "+ e+ " for ListModel no."+ ((Integer) e.getData())); } }); } int n_data= (int) (java.lang.Math.random()* 5+ 1); ListModelList lm= getLM(index); lm.clear(); for (int i= 0; i< n_data; ++i) { lm.add("Data "+ String.valueOf(i)+ " for ListModel "+ String.valueOf(index)); } String eqn= EventQueues.exists(eq_name) ? eq_name : null; BindUtils.postNotifyChange(eqn, null, this, "getLM"); EventQueues.lookup(eq_name).publish(new Event("onData", null, new Integer(index))); } // Return a list containing the indices of the involved listmodel objects private java.util.List indexes_= null; public java.util.List getIndexes() { if (null== indexes_) { indexes_= new java.util.ArrayList(); for (int i= 0; i< involved.length; ++i) indexes_.add(new Integer(involved'['i']')); } return indexes_; } } ]]></zscript> <window id="win" title="Window" border="normal" width="600px" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('VM')" binder="@init(queueName='eq0')" > <grid> <rows children= "@load(vm.indexes)"> <template name="children"> <row > <label value="${each}"/> <vlayout children="@load(vm.getLM(each))"> <!-- Access ListModel via function getLM() --> <template name="children"> <label value="@load(each)"/> </template> </vlayout> </row> </template> </rows> </grid> <separator spacing="100px"/> <grid> <rows children= "@load(vm.indexes)"> <template name="children"> <row > <label value="${each}"/> <listbox model="@bind(vm.getLM(each))"> <!-- Access ListModel via function getLM() --> <template name="model"> <listitem label="@load(each)"/> </template> </listbox> <button label= "Simulate data receive for Model ${each}" attributes.i="@load(each)"> <attribute name="onClick">vm.simulateData(((Integer)self.getAttribute("i")).intValue());</attribute> </button> </row> </template> </rows> </grid> </window> </zk>
Ok, here we are.......
I rewrite your code in pure MVVM without EventQueues (that's my fault, sorry), I think it's cleaner and more readable.
Hope it's helpful.
Hello MonthyPan,
your example works well. Thank you for that. I have now modified your example again (but only a little :-) ).
NotifyChange ('*') we need not, if only list boxes would contain. Moreover, it do not like, because it tells you that all properties have changed.
That is not true. Only one change at a time. Therefore there is no update in the vlayout elements of the second grid, if I NotifyChange ('*') comment out.
How can I send a message about the change only of a list model. Or even better would be to process ListData events for all UI components with children binding such as listbox for model binding.
Oh, and I have a function to access the lists by index
public ListModelList< String > getModel(final int index) { return models.get(index); }
... and in the zul file I would rather prefer write
<listbox model="@load(vm.model(each))">
instead of write
<listbox model="@load(vm.models.get(each))">
However, the function getModel(int) is not found, during parsing the expressions?
Kai
package foo; import java.util.ArrayList; import java.util.List; import org.zkoss.bind.annotation.BindingParam; import org.zkoss.bind.annotation.Command; import org.zkoss.bind.annotation.NotifyChange; import org.zkoss.zul.ListModelList; public class VM { private static int[] involved = { 3, 7, 1, 9 }; private java.util.List< ListModelList< String > > models= new java.util.ArrayList< ListModelList< String > >(); public TestVM() { for (int i= 0; i< 10; ++i) { models.add(new ListModelList< String >()); mockModel(models.get(i), i); } } private void mockModel(final ListModelList< String > lm, final int index) { int n_data = (int) (java.lang.Math.random() * 5 + 1); for (int i= 0; i< n_data; ++i) { lm.add("Data " + i + " for ListModel " + index); } } public java.util.List< ListModelList< String > > getModels() { return models; } public ListModelList< String > getModel(final int index) { return models.get(index); } @Command // @NotifyChange("*") public void simulateData(@BindingParam("index")int index) { ListModelList< String > lm = getModel(index); lm.clear(); mockModel(lm, index); } private List< Integer > indexes_ = null; public List<Integer> getIndexes() { if (null == indexes_) { indexes_ = new ArrayList<Integer>(); for (int i= 0; i< involved.length; ++i) indexes_.add(new Integer(involved<i >)); } return indexes_; } }
<zk> <window id="win" title="Window" border="normal" width="600px" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('foo.VM')" > <grid> <rows children="@load(vm.indexes)"> <template name="children"> <row> <label value="@load(each)" ></label> <listbox model="@load(vm.models.get(each))"> <template name="model"> <listitem label="@load(each)" ></listitem> </template> </listbox> <button label="Simulate for Model ${each}" onClick="@command('simulateData', index=each)" ></button> </row> </template> </rows> </grid> <separator spacing="100px"></separator> <grid> <rows children= "@load(vm.indexes)"> <template name="children"> <row > <label value="@load(each)"></label> <vlayout children="@load(vm.models.get(each))"> <template name="children"> <label value="@load(each)"></label> </template> </vlayout> </row> </template> </rows> </grid> </window> </zk>
Asked: 2012-06-15 12:49:04 +0800
Seen: 409 times
Last updated: Jun 22 '12