You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by "Robson Miranda (JIRA)" <ji...@apache.org> on 2011/01/29 13:16:46 UTC

[jira] Updated: (TAP5-280) Fields injected by AjaxFormLoop sometimes have incorrect values after submits where validation fails.

     [ https://issues.apache.org/jira/browse/TAP5-280?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Robson Miranda updated TAP5-280:
--------------------------------

    Attachment: MyAjaxFormLoop.java

Solved the problem with server side validation. In this version, the formsupport's allocateControlName is overriden, allocating a control name appended to the client value of the current loop iteration.


> Fields injected by AjaxFormLoop sometimes have incorrect values after submits where validation fails.
> -----------------------------------------------------------------------------------------------------
>
>                 Key: TAP5-280
>                 URL: https://issues.apache.org/jira/browse/TAP5-280
>             Project: Tapestry 5
>          Issue Type: Bug
>          Components: tapestry-core
>    Affects Versions: 5.0.15
>            Reporter: Shawn Brownfield
>         Attachments: MyAjaxFormLoop.java
>
>
> 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) {
> 			outerList = new ArrayList<Outer>();
> 			Outer outer = new Outer(0);
> 			outerList.add(outer);
> 		}
> 	}
> 	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) {
> 				return outer.getId();
> 			}			
> 			public Outer toValue(Integer key) {
> 				Outer ret = getOuterById(key);
> 				if (ret == null) throw new RuntimeException("outerEncoder could not retreive item for key:" + key);
> 				return ret;
> 			}
> 		};
> 	public PrimaryKeyEncoder<Integer, Outer> getOuterEncoder() { return outerEncoder; }
> 	private final PrimaryKeyEncoder<EncoderKey, Inner> innerEncoder =
> 		new PrimaryKeyEncoder<EncoderKey, Inner>() {
> 			public void prepareForKeys(List arg0) {}
> 			public EncoderKey toKey(Inner inner) {
> 				Outer outer = inner.getOuter();
> 				return new EncoderKey(outer.getId(), outer.getList().indexOf(inner));
> 			}			
> 			public Inner toValue(EncoderKey key) {
> 				Inner ret = null;
> 				
> 				Outer outer = getOuterById(key.getGroupId());
> 				if (outer != null) {
> 					ret = outer.getList().get(key.getIndex());
> 				}
> 				
> 				if (ret == null) throw new RuntimeException("innerEncoder could not retreive item for key:" + key);
> 				return ret;
> 			}
> 	};
> 	public PrimaryKeyEncoder<EncoderKey, Inner> getInnerEncoder() { return innerEncoder; }
> 	////////////////////////////////
> 	// Helpers
> 	////////////////////////////////
> 	private Outer getOuterById(Integer outerId) {
> 		Outer outer = null;
> 		for (Outer o : outerList) {
> 			if (o.getId().equals(outerId)) {
> 				outer = o;
> 				break;
> 			}
> 		}
> 		
> 		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)){
> 				return super.equals(obj);
> 			}
> 			EncoderKey other = (EncoderKey)obj;
> 			return this.groupId.equals(other.groupId) && this.index.equals(other.index);
> 		}
> 		
> 		@Override
> 		public int hashCode(){
> 			return this.groupId * this.index * 31;
> 		}		
> 		
> 		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) { return; };

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.