# How to add to list with ListModelList and DataBinder

dastultz
797 8

Hello, I have a Listbox backed by ListModelList and DataBinder. My ListitemRenderer binds the data as it renders. When I add an object to the model, the new row appears with the proper components (meaning the renderer is called, right?) but the components don't have any values (blank label, no label for a button). Adding a call to binder.loadAll() doesn't seem to help. I know the values in the binding bean are good because I can delete it from the list which depends on a value in the bean.

All I am doing is this to add the new object:

binder.loadAll(); // doesn't seem to help

I assume my renderer is doing what it needs to, but that could be what's missing.
Any ideas? Thanks.

/Daryl

delete retag edit

## 9 Replies

robertpic71
1275 1

Here some examples, loadAll() should work:

Examples

/Robert

henrichen
3869 2

Looks something wrong with your ListitemRenderer. It would be better if you can post a simplified testing code that would replicate your issue.

/henri

dastultz
797 8

Yes, "simple" is the problem, right? I'm all in Richlet Land so I'll try to post the main bits and hopefully the hole will be visible.

My button listener to add an item looks like this:

My List renderer looks like this:

public void render(Listitem listItem, Object data) throws Exception {
final String beanId = listItem.getId(); // don't require user (me) to make up an id
binder.bindBean(beanId, data);
// loop over "plug in" cell renderers
Iterator<BindingCellRenderer> iter = cellRenderers.iterator();
while (iter.hasNext()) {
BindingCellRenderer renderer = iter.next();
renderer.renderCell(data, listItem, beanId, getBinder());
}
}

My cell renderer looks like this:

public void renderCell(Object data, Listitem listItem, String beanId, DataBinder binder) {
Listcell cell = new Listcell();
listItem.appendChild(cell);
final Component comp = (Component) getComponentTemplate().clone(); // base actual component on template object
cell.appendChild(comp);
String attr = componentBindingAttributes.get(comp.getClass()); // map of common binding attribute
if (attr == null) attr = "value";
binder.addBinding(comp, attr, beanId + "." + getExpression()); // expression is name of property to bind to
}

Hope there is something useful there...

/Daryl

dastultz
797 8

Ok, here it is all in one place. I made some simplifications...

Listbox list = new Listbox();
list.setMultiple(true);
list.setCheckmark(true);
final DataBinder binder = new DataBinder();
list.setItemRenderer(new ListitemRenderer() {
public void render(Listitem listItem, Object data) throws Exception {
String beanId = listItem.getId();
binder.bindBean(beanId, data);
Listcell cell = new Listcell();
listItem.appendChild(cell);
final Component comp = new Label();
cell.appendChild(comp);
}
});

final ListModelList listModel = create list model...
list.setModel(listModel);
list.renderAll();
list.setPage(getPage());

final Textbox txt = new Textbox();
txt.setPage(getPage());

Button btnSave = new Button("Save");
public void onEvent(Event event) throws Exception {
create bean...
bean.setName(txt.getText());
txt.setText("");
}
});
btnSave.setPage(getPage());

henrichen
3869 2

dastultz,

Data Binder does not support "dynamically added Binding" yet. That is, when first loadXxx() or saveXxx() method is called, the binding is "fixed". All those bindings added later are not "visible" to the Data Binder. Here in your example, the binding of the Label's value is added later so it is not visible to the Data Binder and not work as your expect. This is really an implementation limitation. A feature regarding this is already fired but we have had no resource to do it yet.

http://sourceforge.net/tracker/index.php?func=detail&aid=1739790&group_id=152762&atid=785194

Don't you consider to use the collection data binding for your case?

/henri

dastultz
797 8

"A feature regarding this is already fired but we have had no resource to do it yet."

I looked at the tracker, not sure my case fits.. I don't know.

"Don't you consider to use the collection data binding for your case?"

I'm pretty new to this, so I don't know what you mean. Do mean BindingListModelList or CollectionItem? I'm happy to try some other approach. It seems like this would be a pretty common pattern, so there must be a solution. Do you have some example?

henrichen
3869 2

Have you seen this small talk?

http://www.zkoss.org/smalltalks/databind4/databind4.dsp

The idea is that you create a template for listitem and the Data Binder will use an implicit ListitemRenderer to render each Listitem for you based on the provided template. So you don't generate the Listitem and Listcell yourself one by one; rather you create a template Listitem so Data Binder knows how to produce similar ones for you.

The <a:bind ...> expression you saw in the small talk is the old way to denote an annotation. The new way is @{...} but the concept is the same.

<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<window>
<listbox model="@{persons}" selectedItem="@{selected}" rows="4">
<listitem self="@{each=person}"> <!-- The each=... tells Data Binder that this listitem is a template -->
<listcell label="@{person.firstName}"/> <!-- "person" would be the object taken from each model iteration -->
<listcell label="@{person.lastName}"/>
</listitem>
</listbox>
</window>


I knew you are using Richlet. However, you can create a component hierarchy by java code and provide proper bindings then the Data Binder will do the job for you.

dastultz
797 8

Thanks Henri, I'll give this a try. The obvious shortcoming of this approach is that you can't vary the rendered component, e.g., if property X = 0 render a label, if property X = 1 render a hyper link...

/Daryl

dastultz
797 8

Ok, I totally can't figure out how to convert your example to Richlet form, but that's ok, I've got something that works. My renderer keeps a reference to the binder. So when I add an item, I instantiate a new binder and then re-render the whole list:

myrenderer.clearBinder(); // instantiate new binder
listModel.add(newDataBean); // does this render anything?
listbox.renderAll();

I don't really mind doing this because I'd like to refresh the list from the database anyway so I pick up new items someone else might have made, plus the new one ends up in the right position in the list. My question is one of efficiency. My test case of 10 items or so works quite fast. I don't see the blank row show up as it did before, but I'm wondering if there is any hidden extra work being done. My listModel is a ListModelList, so the Listbox listens for changes and rerenders the new data (wrongly as we've seen), or does it? Anything I should worry about?

[hide preview]