Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Abandoned
-
5.0.15
-
None
Description
Using the below .tml, .java. and .js files:
1. Click Add Group.
2. Add values "a" and "1" in the upper group, "c" and "3" in the lower group.
3. Click Save.
4. Add a row in the upper group and enter "b" and "b".
5. Click Save.
==> Values "c" and "3" are copied into the last row of the upper group (validation has failed).
The only values ever lost/copied over are values of rows that were inserted via the addRowLink. Upon failure of validation, it looks as though the ValidationTracker is trying to look up the values for the new rows using the control id from the render phase, but the submission had "mangled"/"uniquified" control ids (with :?????? added) for all the inputs, so that is how these values are stored in the ValidationTracker.
AjaxFormLoopTest.tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head/>
<body>
<h1>Nested AjaxFormLoop Test</h1>
<t:form t:id="formLoopTestForm" t:clientValidation="false">
<div t:type="AjaxFormLoop"
t:id="outerFormLoop"
t:source="outerList"
t:value="tempOuter"
t:encoder="outerEncoder"
>
<table style="background-color: #ece9d8; margin: 2px 0;">
<tbody>
<tr>
<th>String</th>
<th>Number</th>
</tr>
<tr t:type="AjaxFormLoop"
t:id="innerFormLoop"
t:source="tempOuter.list"
t:value="tempInner"
t:context="tempOuter.id"
t:encoder="innerEncoder"
>
<td><t:textField t:value="tempInner.string"/></td>
<td><t:textField t:value="tempInner.number"/></td>
<t:parameter name="addRow">
<td colspan="2">
<t:addRowLink>Add Row</t:addRowLink>
</td>
</t:parameter>
</tr>
</tbody>
</table>
<t:parameter name="addRow">
<t:addRowLink>Add Group</t:addRowLink>
</t:parameter>
</div>
<br/>
<t:submit t:id="save" value="Save"/>
<t:actionLink t:id="clear">Clear</t:actionLink>
</t:form>
</body>
</html>
AjaxFormLoopTest.java:
@IncludeJavaScriptLibrary("context:javascript/script.js")
public class AjaxFormLoopTest {
@Persist
@Property
private List<Outer> outerList;
@Property
private Outer tempOuter;
@Property
private Inner tempInner;
void beginRender() {
if (outerList == null)
}
Object onAddRowFromOuterFormLoop()
{ int nextId = outerList.size() + 1; Outer outer = new Outer(nextId); outerList.add(outer); return outer; }Object onAddRowFromInnerFormLoop(Integer outerId)
{ Outer outer = getOuterById(outerId); List<Inner> list = outer.getList(); Inner inner = new Inner(); list.add(inner); inner.setOuter(outer); return inner; }void onActionFromClear()
{ outerList = null; } ////////////////////////////////
// Encoders
////////////////////////////////
private final PrimaryKeyEncoder<Integer, Outer> outerEncoder =
new PrimaryKeyEncoder<Integer, Outer>() {
public void prepareForKeys(List arg0) {}
public Integer toKey(Outer outer)
public Outer toValue(Integer key)
};
public PrimaryKeyEncoder<Integer, Outer> getOuterEncoder()
private final PrimaryKeyEncoder<EncoderKey, Inner> innerEncoder =
new PrimaryKeyEncoder<EncoderKey, Inner>() {
public void prepareForKeys(List arg0) {}
public EncoderKey toKey(Inner inner)
public Inner toValue(EncoderKey key) {
Inner ret = null;
Outer outer = getOuterById(key.getGroupId());
if (outer != null)
if (ret == null) throw new RuntimeException("innerEncoder could not retreive item for key:" + key);
return ret;
}
};
public PrimaryKeyEncoder<EncoderKey, Inner> getInnerEncoder()
////////////////////////////////
// Helpers
////////////////////////////////
private Outer getOuterById(Integer outerId) {
Outer outer = null;
for (Outer o : outerList) {
if (o.getId().equals(outerId))
}
return outer;
}
// Referenced classes
public static class Outer {
private Integer id;
private List<Inner> list;
public Outer(Integer id)
{ this.id = id; list = new ArrayList<Inner>(); Inner inner = new Inner(); inner.setOuter(this); list.add(inner); }public Integer getId()
{ return id; }public void setId(Integer id)
{ this.id = id; }public List<Inner> getList()
{ return list; }public void setList(List<Inner> list)
{ this.list = list; }}
public static class Inner {
private String string;
private Integer number;
private Outer outer;
public String getString()
{ return string; }public void setString(String string)
{ this.string = string; }public Integer getNumber()
{ return number; }public void setNumber(Integer number)
{ this.number = number; }public Outer getOuter()
{ return outer; }public void setOuter(Outer outer)
{ this.outer = outer; }}
public static class EncoderKey implements Serializable{
private static final long serialVersionUID = 1L;
private final Integer groupId;
private final Integer index;
public final static String SEPARATOR = "_";
public EncoderKey(Integer q, Integer i)
{ this.groupId = q; this.index = i; }public Integer getGroupId()
{ return groupId; }public Integer getIndex()
{ return index; } @Override
public boolean equals(Object obj){
if (!(obj instanceof EncoderKey))
EncoderKey other = (EncoderKey)obj;
return this.groupId.equals(other.groupId) && this.index.equals(other.index);
}
@Override
public int hashCode()
public int compareTo(EncoderKey other)
{ int test = this.groupId.compareTo(other.groupId); return test != 0 ? test : this.index.compareTo(other.index); }
public String toString()
{ return "Group: " + groupId + " Index: " + index; }public String encode()
{ return this.getGroupId()+EncoderKey.SEPARATOR+this.getIndex(); } }
}
script.js:
/** Override to prevent client-side validation. */
Tapestry.Initializer.validate = function (field, specs)
;
Attachments
Attachments
Issue Links
- incorporates
-
TAP5-1896 AjaxFormLoop corrupts list when redisplayed after Remove then server-side failure
- Closed