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 2014/06/03 18:21:21 UTC
[4/4] git commit: Revised solution for TAP5-2331
Revised solution for TAP5-2331
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/193d022b
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/193d022b
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/193d022b
Branch: refs/heads/master
Commit: 193d022be074d90fec562a9915d72af9b85e9ad4
Parents: 24d47d1
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Tue Jun 3 09:05:48 2014 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Tue Jun 3 09:21:33 2014 -0700
----------------------------------------------------------------------
.../tapestry5/corelib/base/AbstractField.java | 132 +++++++++----------
.../corelib/pages/PropertyEditBlocks.java | 18 ++-
.../services/PreSelectedFormNamesService.java | 6 +-
.../src/test/app1/MultiZoneUpdateInsideForm.tml | 3 +-
.../integration/app1/AlertsTests.groovy | 26 ++--
.../app1/pages/MultiZoneUpdateInsideForm.java | 2 -
.../pages/PerFormValidationMessageDemo.java | 2 -
7 files changed, 93 insertions(+), 96 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/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 dc400d2..beb4d1d 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
@@ -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
@@ -14,24 +12,8 @@
package org.apache.tapestry5.corelib.base;
-import java.io.Serializable;
-
-import org.apache.tapestry5.BindingConstants;
-import org.apache.tapestry5.ComponentAction;
-import org.apache.tapestry5.ComponentResources;
-import org.apache.tapestry5.Field;
-import org.apache.tapestry5.FieldValidationSupport;
-import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.ValidationDecorator;
-import org.apache.tapestry5.ValidationTracker;
-import org.apache.tapestry5.Validator;
-import org.apache.tapestry5.annotations.AfterRender;
-import org.apache.tapestry5.annotations.BeginRender;
-import org.apache.tapestry5.annotations.Environmental;
-import org.apache.tapestry5.annotations.Mixin;
-import org.apache.tapestry5.annotations.Parameter;
-import org.apache.tapestry5.annotations.SetupRender;
-import org.apache.tapestry5.annotations.SupportsInformalParameters;
+import org.apache.tapestry5.*;
+import org.apache.tapestry5.annotations.*;
import org.apache.tapestry5.corelib.mixins.DiscardBody;
import org.apache.tapestry5.corelib.mixins.RenderInformals;
import org.apache.tapestry5.internal.BeanValidationContext;
@@ -39,12 +21,16 @@ import org.apache.tapestry5.internal.InternalComponentResources;
import org.apache.tapestry5.internal.services.PreSelectedFormNamesService;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.internal.util.TapestryException;
import org.apache.tapestry5.services.ComponentDefaultProvider;
import org.apache.tapestry5.services.Environment;
import org.apache.tapestry5.services.FormSupport;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
+import java.io.Serializable;
+
/**
* Provides initialization of the clientId and elementName properties. In addition, adds the {@link RenderInformals},
* and {@link DiscardBody} mixins.
@@ -80,7 +66,7 @@ public abstract class AbstractField implements Field
@Inject
protected Environment environment;
-
+
@Inject
@Symbol(SymbolConstants.FORM_FIELD_CSS_CLASS)
protected String cssClass;
@@ -130,37 +116,32 @@ public abstract class AbstractField implements Field
private static final ProcessSubmission PROCESS_SUBMISSION_ACTION = new ProcessSubmission();
/**
- * The id used to generate a page-unique client-side identifier for the component.
- * If this parameter is not bound and this component is rendered multiple times in a request
- * and <code>forceClientAllocation</code> is false (the default),
- * a suffix will be appended to the to id to ensure uniqueness. Either way,
- * its value may be accessed via the {@link #getClientId() clientId property}.
- * When this parameter is bound, Tapestry considers the user (developer) is taking care of
- * providing unique client-side identifiers. Special care should be taken when the
- * field is inside a Zone.
- * <br>
- * <strong>Default value: the component's t:id</strong>.
- * <br>
- * <em>This parameter will be ignored if it receives any of these values:</em>
- * <ul>
- * <li>reset</li>
- * <li>submit</li>
- * <li>id</li>
- * <li>method</li>
- * <li>action</li>
- * <li>onsubmit</li>
- * <li>cancel</li>
- * </ul>
+ * Used to explicitly set the client-side id of the element for this component. Normally this is not
+ * bound (or null) and {@link org.apache.tapestry5.services.javascript.JavaScriptSupport#allocateClientId(org.apache.tapestry5.ComponentResources)}
+ * is used to generate a unique client-id based on the component's id. In some cases, when creating client-side
+ * behaviors, it is useful to explicitly set a unique id for an element using this parameter.
+ * <p/>
+ * Certain values, such as "submit", "method", "reset", etc., will cause client-side conflicts and are not allowed; using such will
+ * cause a runtime exception.
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL)
- protected String clientId;
-
+ private String clientId;
+
/**
- * When true, it forces the clientId to be passed through the id allocator to avoid repeated ids
- * even when the clientId parameter is bound.
+ * A rarely used option that indicates that the actual client id should start with the clientId parameter (if non-null)
+ * but should still pass that Id through {@link org.apache.tapestry5.services.javascript.JavaScriptSupport#allocateClientId(String)}
+ * to generate the final id.
+ * <p/>
+ * An example of this are the components used inside a {@link org.apache.tapestry5.corelib.components.BeanEditor} which
+ * will specify a clientId (based on the property name) but still require that it be unique.
+ * <p/>
+ * Defaults to false.
+ *
+ * @since 5.4
*/
- @Parameter("false")
- private boolean forceClientIdAllocation;
+ @Parameter
+ private boolean ensureClientIdUnique;
+
private String assignedClientId;
@@ -186,7 +167,7 @@ public abstract class AbstractField implements Field
@Inject
protected FieldValidationSupport fieldValidationSupport;
-
+
@Inject
private PreSelectedFormNamesService preSelectedFormNamesService;
@@ -195,11 +176,6 @@ public abstract class AbstractField implements Field
return defaultProvider.defaultLabel(resources);
}
- final String defaultClientId()
- {
- return resources.getId();
- }
-
public final String getLabel()
{
return label;
@@ -208,11 +184,6 @@ public abstract class AbstractField implements Field
@SetupRender
final void setup()
{
- // By default, use the component id as the (base) client id. If the clientid
- // parameter is bound, then that is the value to use.
-
- String id = clientId;
-
// Often, these controlName and clientId will end up as the same value. There are many
// exceptions, including a form that renders inside a loop, or a form inside a component
// that is used multiple times.
@@ -220,16 +191,39 @@ public abstract class AbstractField implements Field
if (formSupport == null)
throw new RuntimeException(String.format("Component %s must be enclosed by a Form component.",
resources.getCompleteId()));
-
- final boolean avoidAllocation = resources.isBound("clientId") && !forceClientIdAllocation && !preSelectedFormNamesService.isPreselected(clientId);
- assignedClientId = avoidAllocation ? clientId : javaScriptSupport.allocateClientId(id);
-
- String controlName = formSupport.allocateControlName(id);
+
+ assignedClientId = allocateClientId();
+
+ String controlName = formSupport.allocateControlName(assignedClientId);
formSupport.storeAndExecute(this, new Setup(controlName));
formSupport.store(this, PROCESS_SUBMISSION_ACTION);
}
+ private String allocateClientId()
+ {
+ if (clientId == null)
+ {
+ return javaScriptSupport.allocateClientId(resources);
+ }
+
+ if (preSelectedFormNamesService.isPreselected(clientId))
+ {
+ throw new TapestryException(String.format(
+ "The value '%s' for parameter clientId is not allowed as it causes a naming conflict in the client-side DOM. " +
+ "Select a name not in the list: %s.",
+ clientId,
+ InternalUtils.joinSorted(preSelectedFormNamesService.getNames())), this, null);
+ }
+
+ if (ensureClientIdUnique)
+ {
+ return javaScriptSupport.allocateClientId(clientId);
+ }
+
+ return clientId;
+ }
+
public final String getClientId()
{
return assignedClientId;
@@ -328,7 +322,10 @@ public abstract class AbstractField implements Field
protected void putPropertyNameIntoBeanValidationContext(String parameterName)
{
- if (beanValidationDisabled) { return; }
+ if (beanValidationDisabled)
+ {
+ return;
+ }
String propertyName = ((InternalComponentResources) resources).getPropertyName(parameterName);
@@ -346,7 +343,10 @@ public abstract class AbstractField implements Field
protected void removePropertyNameFromBeanValidationContext()
{
- if (beanValidationDisabled) { return; }
+ if (beanValidationDisabled)
+ {
+ return;
+ }
BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
index 0b92f4b..19326cc 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PropertyEditBlocks.java
@@ -1,5 +1,3 @@
-// Copyright 2007, 2008, 2011 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
@@ -45,52 +43,52 @@ public class PropertyEditBlocks
parameters = {"value=context.propertyValue", "label=prop:context.label",
"translate=prop:textFieldTranslator", "validate=prop:textFieldValidator",
"clientId=prop:context.propertyId", "annotationProvider=context",
- "forceClientIdAllocation=true"})
+ "ensureClientIdUnique=true"})
private TextField textField;
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label",
"translate=prop:numberFieldTranslator", "validate=prop:numberFieldValidator",
"clientId=prop:context.propertyId", "annotationProvider=context",
- "forceClientIdAllocation=true"})
+ "ensureClientIdUnique=true"})
private TextField numberField;
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label", "encoder=valueEncoderForProperty",
"model=selectModelForProperty", "validate=prop:selectValidator",
- "clientId=prop:context.propertyId", "forceClientIdAllocation=true"})
+ "clientId=prop:context.propertyId", "ensureClientIdUnique=true"})
private Select select;
@SuppressWarnings("unused")
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label",
- "clientId=prop:context.propertyId", "forceClientIdAllocation=true"})
+ "clientId=prop:context.propertyId", "ensureClientIdUnique=true"})
private Checkbox checkboxField;
@SuppressWarnings("unused")
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label", "clientId=prop:context.propertyid",
- "validate=prop:dateFieldValidator", "forceClientIdAllocation=true"})
+ "validate=prop:dateFieldValidator", "ensureClientIdUnique=true"})
private DateField dateField;
@SuppressWarnings("unused")
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label", "clientId=prop:context.propertyid",
- "validate=prop:calendarFieldValidator", "forceClientIdAllocation=true"})
+ "validate=prop:calendarFieldValidator", "ensureClientIdUnique=true"})
private DateField calendarField;
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label",
"translate=prop:passwordFieldTranslator", "validate=prop:passwordFieldValidator",
- "clientId=prop:context.propertyId", "annotationProvider=context", "forceClientIdAllocation=true"})
+ "clientId=prop:context.propertyId", "annotationProvider=context", "ensureClientIdUnique=true"})
private PasswordField passwordField;
@Component(
parameters = {"value=context.propertyValue", "label=prop:context.label",
"translate=prop:textAreaTranslator",
"validate=prop:textAreaValidator", "clientId=prop:context.propertyId",
- "annotationProvider=context", "forceClientIdAllocation=true"})
+ "annotationProvider=context", "ensureClientIdUnique=true"})
private TextArea textArea;
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PreSelectedFormNamesService.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PreSelectedFormNamesService.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PreSelectedFormNamesService.java
index fc00ba6..379c53b 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PreSelectedFormNamesService.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PreSelectedFormNamesService.java
@@ -1,5 +1,3 @@
-// Copyright 2014 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
@@ -18,9 +16,9 @@ import java.util.Set;
import org.apache.tapestry5.internal.InternalSymbols;
/**
- * Service provinding methods related to names that shouldn't be used as HTML elements client ids.
+ * Service providing methods related to names that shouldn't be used as HTML elements client ids.
*
- * @see InternalSymbols.PRE_SELECTED_FORM_NAMES
+ * @see InternalSymbols#PRE_SELECTED_FORM_NAMES
*/
public interface PreSelectedFormNamesService
{
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/tapestry-core/src/test/app1/MultiZoneUpdateInsideForm.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/MultiZoneUpdateInsideForm.tml b/tapestry-core/src/test/app1/MultiZoneUpdateInsideForm.tml
index ca3180e..5334b34 100644
--- a/tapestry-core/src/test/app1/MultiZoneUpdateInsideForm.tml
+++ b/tapestry-core/src/test/app1/MultiZoneUpdateInsideForm.tml
@@ -8,7 +8,8 @@
<t:zone t:id="select1ValueZone" visible="false">Show</t:zone>
<t:zone t:id="select2ValueZone">
<t:label for="selectValue2"/>
- <select t:type="Select" t:id="selectValue2" t:validate="required"/>
+ <!-- Only necessary to "lock down" the clientId to facilliate the test. -->
+ <select t:type="Select" t:id="selectValue2" clientId="selectValue2" t:validate="required"/>
</t:zone>
<br/>
<input type="submit" value="Upate Form"/>
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AlertsTests.groovy
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AlertsTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AlertsTests.groovy
index 4cf2aa9..081445b 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AlertsTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/AlertsTests.groovy
@@ -1,5 +1,3 @@
-// Copyright 2011-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
@@ -99,9 +97,14 @@ class AlertsTests extends App1TestCase {
void ajax_update_and_remove() {
openLinks "Alerts Demo", "Reset Alerts Storage"
- select "css=#ajax select[name=\"severity\"]", "Error"
- select "css=#ajax select[name=\"duration\"]", "Until Dismissed"
- type "css=#ajax input[name=\"message\"]", "ajax error until"
+ def severitySelector = "css=#ajax select[name='severity_0']"
+ def durationSelector = "css=#ajax select[name='duration_0']"
+ def messageSelector = "css=#ajax input[name='message_0']"
+ def markupSelector = "css=#ajax input[name='markup_0']"
+
+ select severitySelector, "Error"
+ select durationSelector, "Until Dismissed"
+ type messageSelector, "ajax error until"
click "//input[@value='Ajax Update']"
@@ -116,14 +119,15 @@ class AlertsTests extends App1TestCase {
// Check that the alert container is now empty
assertText "css=$CONTAINER", ""
-
+
// Now with markup
openLinks "Alerts Demo", "Reset Alerts Storage"
-
- select "css=#ajax select[name=\"severity\"]", "Warn"
- select "css=#ajax select[name=\"duration\"]", "Single"
- check "css=#ajax input[name='markup']"
- type "css=#ajax input[name='message']", "<a><span>Markup!</span></a>"
+
+ select severitySelector, "Warn"
+ select durationSelector, "Single"
+
+ check markupSelector
+ type messageSelector, "<a><span>Markup!</span></a>"
click "//input[@value='Ajax Update']"
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateInsideForm.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateInsideForm.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateInsideForm.java
index ceccda0..155e755 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateInsideForm.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateInsideForm.java
@@ -1,5 +1,3 @@
-// Copyright 2010 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
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/193d022b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PerFormValidationMessageDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PerFormValidationMessageDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PerFormValidationMessageDemo.java
index 633142b..03399f9 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PerFormValidationMessageDemo.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PerFormValidationMessageDemo.java
@@ -1,5 +1,3 @@
-// Copyright 2008 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