Hi All!
I am struggling since days with a Tapestry Bug(?) and maybe one of you
have an idea whats wrong or what my mistake may be...
Scenario: I use a loop to display multiple rows in a table. Each row
allows inline editing if the user presses a link. Due to link pressing
the particular row's zone is swapped to show edit fields instead of pure
text. Everything works beside submit. Submit always changes the last
element in the loop not the one it's iterating over. I dubugged it down
to Tapestry's PropBinding and noticed there is always the last element
used as "root" but maybe I am fooled by the proxies.
I can provide a WAR if you like, but maybe the sources are enough
because I tried to strip down to bare minumum.
tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
<t:form>
<table>
<t:loop source="XValues" value="xValue" encoder="XValueEncoder"
formState="iteration">
<t:submitNotifier>
<tr t:type="zone" update="show" t:id="row"
id="row_${xValue.pk}">
<t:if test="!XValueChanged">
<t:delegate to="block:readOnly"/>
</t:if>
<t:if test="XValueChanged">
<t:delegate to="block:editable"/>
</t:if>
</tr>
<t:block t:id="readOnly">
<td>${xValue.pk}</td>
<td>${xValue.s}</td>
<td><a t:type="eventlink" t:id="modifyXValue"
context="xValue.pk" zone="row_${xValue.pk}">edit</a></td>
</t:block>
<t:block t:id="editable">
<td>${xValue.pk}</td>
<td>
<t:textfield t:id="xs" value="xValue.s"/>
</td>
<td><a t:type="eventlink" t:id="revertXValue"
context="xValue.pk" zone="row_${xValue.pk}">revert</a></td>
</t:block>
</t:submitNotifier>
</t:loop>
</table>
<input t:type="submit" name="save" value="save"/>
</t:form>
</html>
Basically the loop displays 3 rows and uses a delegate to decide if it
has to show edit fields or pure text. Each row has a unique zone id
based on the pk.
Each eventlink just transports the pk. Regardless of formState
("iterable" or "values") still the wrong element is updated.
Java:
package de.threeoldcoders.grayhair.pages;
import de.threeoldcoders.grayhair.services.XValue;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.annotations.Id;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
import javax.inject.Inject;
import java.util.*;
public class Index
{
private static final List<XValue> _allXValues = Arrays.asList(new
XValue(1, "1"), new XValue(2, "2"), new XValue(3, "3"));
private static final List<XValue> _changes = new ArrayList<XValue>();
@Inject private Request _request;
@Inject private AjaxResponseRenderer _arr;
@Inject @Id("readOnly") private Block _blockReadOnly;
@Inject @Id("editable") private Block _blockEdit;
@Property XValue _xValue;
void onAfterSubmit()
{
final XValue orig =
getXValueEncoder().toValue(Long.toString(_xValue.getPk())); // trick to
retrieve original instance
if (! orig.getS().equals(_xValue.getS())) {
orig.setS(_xValue.getS());
}
_changes.remove(orig);
}
Object onModifyXValue(final long pk)
{
// just retrieve original instance and mark it as changed
final XValue orig = getXValueEncoder().toValue(Long.toString(pk));
_changes.add(orig);
if (_request.isXHR()) {
_xValue = orig;
_arr.addRender("row_" + _xValue.getPk(), _blockEdit);
}
return null;
}
Object onRevertXValue(final long pk)
{
// just retrieve original instance and mark it as changed
final XValue orig = getXValueEncoder().toValue(Long.toString(pk));
_changes.remove(orig);
if (_request.isXHR()) {
_xValue = orig;
_arr.addRender("row_" + _xValue.getPk(), _blockReadOnly);
}
return null;
}
public List<XValue> getXValues()
{
return _allXValues;
}
public boolean isXValueChanged()
{
return _changes.contains(_xValue);
}
public ValueEncoder<XValue> getXValueEncoder()
{
return new ValueEncoder<XValue>()
{
@Override public String toClient(final XValue value)
{
return value.getPk().toString();
}
@Override public XValue toValue(final String clientValue)
{
return getXValues().get(Integer.parseInt(clientValue)-1);
}
};
}
}
As I wanted it as small as possible this sample makes no use of
services, modules and all, thus my datamodel is simply hardcoded as
static members. I know this is not working in the real world. Each event
handler returns the block according to the internal state so it toggles
to edit mode or readonly mode, depending on the link, which is working.
When I now press "edit" in the second row and change "2" to "B" for
example and press submit, the "B" appears in row "3" because this is the
last element in the list. It will never change something else regardless
how many rows are in edit mode. The submit notifier correctly iterates
all three rows, but it looks like the "property set" call comes ways to
late on a wrong instance. I even tried "defer" true / false without any
difference.
Thanks in advance
Jens
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org