You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2015/01/05 21:42:47 UTC

tapestry-5 git commit: TAP5-2391: Properly track Fields in ValidationTracker, even during Ajax requests

Repository: tapestry-5
Updated Branches:
  refs/heads/master fa691e262 -> 280940fb0


TAP5-2391: Properly track Fields in ValidationTracker, even during Ajax requests


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/280940fb
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/280940fb
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/280940fb

Branch: refs/heads/master
Commit: 280940fb098a4766effe297c20812042723529a8
Parents: fa691e2
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Mon Jan 5 12:42:39 2015 -0800
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Mon Jan 5 12:42:39 2015 -0800

----------------------------------------------------------------------
 .../main/java/org/apache/tapestry5/Field.java   |  5 +--
 .../main/java/org/apache/tapestry5/Field2.java  | 38 ++++++++++++++++++++
 .../apache/tapestry5/ValidationTrackerImpl.java | 26 +++++++++-----
 .../tapestry5/corelib/base/AbstractField.java   | 16 ++++++++-
 .../integration/app1/AjaxGroovyTests.groovy     | 12 +++++++
 5 files changed, 86 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/280940fb/tapestry-core/src/main/java/org/apache/tapestry5/Field.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/Field.java b/tapestry-core/src/main/java/org/apache/tapestry5/Field.java
index eabc532..c22b28d 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/Field.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/Field.java
@@ -1,5 +1,3 @@
-// Copyright 2013 The Apache Software Foundation
-//
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -22,6 +20,9 @@ package org.apache.tapestry5;
  * component's {@link #getControlName()} will only be accurate after it has rendered.  In some cases, when generating
  * JavaScript for example, it is necessary to {@linkplain org.apache.tapestry5.services.Heartbeat#defer(Runnable) wait
  * until the end of the current Heartbeat} to ensure that all components have had their chance to render.
+ * <p/>
+ * Most Fields also implement {@link org.apache.tapestry5.Field2}, which was introduced to bridge a gap related to
+ * re-rendering a form as part of an Ajax request.
  */
 public interface Field extends ClientElement
 {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/280940fb/tapestry-core/src/main/java/org/apache/tapestry5/Field2.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/Field2.java b/tapestry-core/src/main/java/org/apache/tapestry5/Field2.java
new file mode 100644
index 0000000..3322325
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/Field2.java
@@ -0,0 +1,38 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5;
+
+/**
+ * Due to how control names and client ids are allocated inside during an Ajax request, it is difficult to
+ * to connect input data and field validation errors to the fields, since the control name and client id are different
+ * during the processing of the submitted form data and during the subsequent render. Starting in 5.4, the
+ * key used to identify a field inside the {@link org.apache.tapestry5.ValidationTracker is this new validation id,
+ * which is assigned on first read.
+ * <p/>
+ * If a field inplements {@link org.apache.tapestry5.Field} but not Field2, then the control name is used as the
+ * validation id (which will work correctly during non-Ajax requests).
+ * <p/>
+ * This assumes a "flat" field structure, where a given component renders only once (not multiple times, inside
+ * a {@link org.apache.tapestry5.corelib.components.Loop}.
+ *
+ * @since 5.4
+ */
+public interface Field2 extends Field
+{
+    /**
+     * Returns a request-scoped unique validation id for the field. This returns the same value regardless of how
+     * many times the field is rendered, which means that the behavior will be incorrect when the
+     * field component is placed inside a loop.
+     */
+    String getValidationId();
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/280940fb/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
index 33666a4..c77e154 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
@@ -1,5 +1,3 @@
-// Copyright 2006-2013 The Apache Software Foundation
-//
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -35,15 +33,15 @@ public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedOb
     {
         private static final long serialVersionUID = -3653306147088451811L;
 
-        private final String fieldName;
+        private final String validationId;
 
         private String input;
 
         private String errorMessage;
 
-        FieldTracker(String fieldName)
+        FieldTracker(String validationId)
         {
-            this.fieldName = fieldName;
+            this.validationId = validationId;
         }
     }
 
@@ -52,6 +50,7 @@ public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedOb
     private List<FieldTracker> fieldTrackers;
 
     // Rebuilt on-demand
+    // Keyed on validationId
 
     private transient Map<String, FieldTracker> fieldToTracker;
 
@@ -66,12 +65,23 @@ public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedOb
         fieldToTracker = CollectionFactory.newMap();
 
         for (FieldTracker ft : fieldTrackers)
-            fieldToTracker.put(ft.fieldName, ft);
+            fieldToTracker.put(ft.validationId, ft);
+    }
+
+    private String getKey(Field field)
+    {
+        if (field instanceof Field2)
+        {
+            Field2 field2 = (Field2) field;
+            return field2.getValidationId();
+        }
+
+        return field.getControlName();
     }
 
     private FieldTracker get(Field field)
     {
-        String key = field.getControlName();
+        String key = getKey(field);
 
         refreshFieldToTracker();
 
@@ -90,7 +100,7 @@ public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedOb
 
         refreshFieldToTracker();
 
-        String key = fieldTracker.fieldName;
+        String key = fieldTracker.validationId;
 
         if (!fieldToTracker.containsKey(key))
         {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/280940fb/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
index eec94df..851de73 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java
@@ -30,6 +30,7 @@ import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 
 import java.io.Serializable;
+import java.util.UUID;
 
 /**
  * Provides initialization of the clientId and elementName properties. In addition, adds the {@link RenderInformals},
@@ -38,7 +39,7 @@ import java.io.Serializable;
  * @tapestrydoc
  */
 @SupportsInformalParameters
-public abstract class AbstractField implements Field
+public abstract class AbstractField implements Field2
 {
     /**
      * The user presentable label for the field. If not provided, a reasonable label is generated from the component's
@@ -362,4 +363,17 @@ public abstract class AbstractField implements Field
 
         beanValidationContext.setCurrentProperty(null);
     }
+
+    private String validationId;
+
+    @Override
+    public String getValidationId()
+    {
+        if (validationId == null)
+        {
+            validationId = UUID.randomUUID().toString();
+        }
+
+        return validationId;
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/280940fb/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AjaxGroovyTests.groovy
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AjaxGroovyTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AjaxGroovyTests.groovy
index 025db6b..b4531da 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AjaxGroovyTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AjaxGroovyTests.groovy
@@ -45,4 +45,16 @@ class AjaxGroovyTests extends App1TestCase {
         assertText "css=#target > p", "You submitted the form."
     }
 
+    @Test
+    void ajax_form_validation() {
+        openLinks "Ajax Validation"
+
+        click SUBMIT
+
+        waitForAjaxRequestsToComplete()
+
+        assertText "css=.form-group.has-error .help-block", "Server-side validation error."
+
+    }
+
 }