Examples
This ListEdit control is typically used in conjuction with the ListEditMap class. To understand how the ListEdit control works you need to understand
the how forms are rewound. But before we get into that lets see an example of the
ListEdit control in action.
This example is from the vlib sample application and allows the user to edit a list of publishers.
HTML:
The following code sets up list of TextField components for editing the publisher names. A checkbox is used to mark publishers to be deleted from the database. It's pretty similar to a Foreach component at this stage. <tr jwcid="listEdit"> <td class="control"><input jwcid="inputName@TextField" value="ognl:publisher.name" size="40" maxlength="40"/> </td> <td class="checkbox"><input type="checkbox" jwcid="delete@Checkbox" selected="ognl:listEditMap.deleted"/> </td> </tr>
Page file:
The page file sets up some page properties for the ListEditMap and current publisher. The listEdit component is where it gets interesting. The source binding is the set of all keys in the list edit map. Basically the list of all the publishers identifers. The value is the current key. It would actually be possible to use the publisher object as the value however because each value object is serialized this could get pretty inefficient. Instead what happens is a listener is setup. This listener sets up the actual publisher object from the key when the form is rewound. <property-specification name="listEditMap" type="org.apache.tapestry.form.ListEditMap"/> <property-specification name="publisher" type="org.apache.tapestry.vlib.ejb.Publisher"/> <component id="listEdit" type="ListEdit"> <binding name="source" expression="listEditMap.keys"/> <binding name="value" expression="listEditMap.key"/> <static-binding name="element" value="tr"/> <binding name="listener" expression="listeners.synchronizePublisher"/> </component>
Code:
In the code part of this example consists of the abstract properties we set up earlier and three important methods. The synchronizePublisher() method is used to set the Publisher object from the keys we are storing in the listEditMap property. The secret to understanding how it works lies in understanding the rewind cycle. During rewind the listener method is called multiple times - once for each row. The rewind process calls listEditMap.setKey(). This effects the current value returned by listEditMap.getValue(). Once the current publisher is setup calls from the TextField component control the setting of the necessary publisher object properties.
The next two methods are easier to understand. The readPublishers() call populates the listEditMap property with the set of all publishers to be edited. It is called from pageBeginRender(). The processForm() method handles the form submit. All updated publishers are in map.getValues() and deleted publishers are returned by map.getDeletedKeys().
public abstract ListEditMap getListEditMap();
public abstract void setListEditMap(ListEditMap listEditMap);
public abstract Publisher getPublisher();
public abstract void setPublisher(Publisher publisher);
public void synchronizePublisher(IRequestCycle cycle)
{
ListEditMap map = getListEditMap();
Publisher publisher = (Publisher) map.getValue();
if (publisher == null)
{
setError(getMessage("out-of-date"));
throw new PageRedirectException(this);
}
setPublisher(publisher);
}
private void readPublishers() { ...
ListEditMap map = new ListEditMap();
int count = Tapestry.size(publishers);
for (i = 0; i < count; i++)
map.add(publishers[i].getId(), publishers[i]);
setListEditMap(map);
} public void processForm(IRequestCycle cycle)
{
...
ListEditMap map = getListEditMap();
List updateList = map.getValues();
List deletedIds = map.getDeletedKeys();
// update and delete publishers
}
|