You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2021/07/17 15:29:53 UTC

[wicket] 01/01: WICKET-6902 allow prepend and append

This is an automated email from the ASF dual-hosted git repository.

svenmeier pushed a commit to branch WICKET-6902-js-after-components
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit 1f2064274c8c5385e66a71ff3445bdf84871561a
Author: Sven Meier <sv...@apache.org>
AuthorDate: Sat Jul 17 00:18:02 2021 +0200

    WICKET-6902 allow prepend and append
    
    up-to including #onAfterRespond()
---
 pom.xml                                            |  5 +++
 .../org/apache/wicket/ajax/AjaxRequestHandler.java | 12 +-----
 .../org/apache/wicket/ajax/AjaxRequestTarget.java  | 45 ++++++++++++++++------
 .../org/apache/wicket/page/PartialPageUpdate.java  | 22 ++++++++++-
 .../apache/wicket/ajax/AjaxRequestHandlerTest.java | 44 ++++++++++++++-------
 5 files changed, 90 insertions(+), 38 deletions(-)

diff --git a/pom.xml b/pom.xml
index eb5088a..241d0a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1153,6 +1153,11 @@
 								<className>org/apache/wicket/pageStore/IPageStore</className>
 								<method>void end(org.apache.wicket.pageStore.IPageContext)</method>
 							</difference>
+							<difference>
+								<differenceType>7012</differenceType>
+								<className>org/apache/wicket/ajax/AjaxRequestTarget$IListener</className>
+								<method>void onAfterRespond(java.util.Map, org.apache.wicket.ajax.AjaxRequestTarget)</method>
+							</difference>
 						</ignored>
 					</configuration>
 					<executions>
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
index 78699bf..fd54daa 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
@@ -145,19 +145,9 @@ public class AjaxRequestHandler extends AbstractPartialPageRequestHandler implem
 					final Map<String, Component> components = Collections
 						.unmodifiableMap(markupIdToComponent);
 
-					// create response that will be used by listeners to append javascript
-					final AjaxRequestTarget.IJavaScriptResponse jsresponse = new AjaxRequestTarget.IJavaScriptResponse()
-					{
-						@Override
-						public void addJavaScript(String script)
-						{
-							writeEvaluations(response, Collections.<CharSequence> singleton(script));
-						}
-					};
-
 					for (AjaxRequestTarget.IListener listener : listeners)
 					{
-						listener.onAfterRespond(components, jsresponse);
+						listener.onAfterRespond(components, AjaxRequestHandler.this);
 					}
 				}
 			}
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
index 6c874dc..2e1efb6 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
@@ -38,35 +38,54 @@ public interface AjaxRequestTarget extends IPartialPageRequestHandler, ILoggable
 	interface IListener
 	{
 		/**
-		 * Triggered before ajax request target begins its response cycle
+		 * Triggered before the target begins writing components.
 		 *
 		 * @param map
 		 *            modifiable map (markupId -&gt; component) of components already added to the target
 		 * @param target
 		 *            the target itself. Could be used to add components or to append/prepend
-		 *            javascript
+		 *            JavaScript
 		 *
 		 */
 		default void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target)
 		{}
 
 		/**
-		 * Triggered after ajax request target is done with its response cycle. At this point only
-		 * additional javascript can be output to the response using the provided
-		 * {@link AjaxRequestTarget.IJavaScriptResponse} object
+		 * Triggered after the target has written components. At this point only
+		 * additional JavaScript can be added to the response.
 		 *
-		 * NOTE: During this stage of processing any calls to target that manipulate the response
-		 * (adding components, javascript) will have no effect
+		 * NOTE: During this stage of processing any calls that manipulate components will result in
+		 * an exception.
 		 *
 		 * @param map
 		 *            read-only map:markupId-&gt;component of components already added to the target
 		 * @param response
-		 *            response object that can be used to output javascript
+		 *            response object that can be used to output JavaScript
+		 *            
+		 * @deprecated implement {@link #onAfterRespond(Map, AjaxRequestTarget)} instead
 		 */
+		@Deprecated
 		default void onAfterRespond(Map<String, Component> map, AjaxRequestTarget.IJavaScriptResponse response)
 		{}
 
 		/**
+		 * Triggered after the target has written components. At this point only
+		 * additional JavaScript can be added to the response.
+		 *
+		 * NOTE: During this stage of processing any calls that manipulate components will result in
+		 * an exception. After notification of all listeners no JavaScript can be added any longer.
+		 *
+		 * @param map
+		 *            read-only map:markupId-&gt;component of components already added to the target
+		 * @param target
+		 *            the target itself. Could be used to append/prepend JavaScript
+		 */
+		default void onAfterRespond(Map<String, Component> map, AjaxRequestTarget target)
+		{
+			onAfterRespond(map, script -> target.appendJavaScript(script));
+		}
+
+		/**
 		 * Triggered for every Ajax behavior. Can be used to configure common settings.
 		 * 
 		 * @param behavior
@@ -80,19 +99,23 @@ public interface AjaxRequestTarget extends IPartialPageRequestHandler, ILoggable
 	}
 
 	/**
-	 * An ajax javascript response that allows users to add javascript to be executed on the client
+	 * An ajax JavaScript response that allows users to add JavaScript to be executed on the client
 	 * side
 	 *
 	 * @author ivaynberg
+	 * 
+	 * @deprecated use {@link AjaxRequestTargetprependJavaScript(CharSequence)} and
+	 *             {@link AjaxRequestTarget#appendJavaScript(CharSequence)} instead
 	 */
+	@Deprecated
 	@FunctionalInterface
 	interface IJavaScriptResponse
 	{
 		/**
-		 * Adds more javascript to the ajax response that will be executed on the client side
+		 * Adds more JavaScript to the ajax response that will be executed on the client side
 		 *
 		 * @param script
-		 *            javascript
+		 *            JavaScript
 		 */
 		void addJavaScript(String script);
 	}
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
index 1712dc8..f95c959 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
@@ -104,6 +104,12 @@ public abstract class PartialPageUpdate
 	protected transient boolean componentsFrozen;
 
 	/**
+	 * A flag that indicates that javascripts cannot be added anymore.
+	 * See https://issues.apache.org/jira/browse/WICKET-6902
+	 */
+	protected transient boolean javascriptsFrozen;
+
+	/**
 	 * Buffer of response body. 
 	 */
 	protected final ResponseBuffer bodyBuffer;
@@ -161,12 +167,14 @@ public abstract class PartialPageUpdate
 			// process added components
 			writeComponents(response, encoding);
 
+			onAfterRespond(response);
+			
+			javascriptsFrozen = true;
+
 			// queue up prepend javascripts. unlike other steps these are executed out of order so that
 			// components can contribute them from during rendering.
 			writePriorityEvaluations(response, prependJavaScripts);
 
-			onAfterRespond(response);
-
 			// execute the dom ready javascripts as first javascripts
 			// after component replacement
 			List<CharSequence> evaluationScripts = new ArrayList<>();
@@ -493,6 +501,11 @@ public abstract class PartialPageUpdate
 	{
 		Args.notNull(javascript, "javascript");
 
+		if (javascriptsFrozen)
+		{
+			throw new IllegalStateException("A partial update of the page is being rendered, JavaScript can no longer be added");
+		}
+
 		appendJavaScripts.add(javascript);
 	}
 
@@ -505,6 +518,11 @@ public abstract class PartialPageUpdate
 	public final void prependJavaScript(CharSequence javascript)
 	{
 		Args.notNull(javascript, "javascript");
+		
+		if (javascriptsFrozen)
+		{
+			throw new IllegalStateException("A partial update of the page is being rendered, JavaScript can no longer be added");
+		}
 
 		prependJavaScripts.add(javascript);
 	}
diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxRequestHandlerTest.java b/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxRequestHandlerTest.java
index 66cc73d..7a83db4 100644
--- a/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxRequestHandlerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxRequestHandlerTest.java
@@ -21,14 +21,15 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
+
 import java.io.IOException;
 import java.lang.reflect.Constructor;
-import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.MockPageWithLink;
@@ -38,6 +39,9 @@ import org.apache.wicket.event.IEvent;
 import org.apache.wicket.markup.IMarkupResourceStreamProvider;
 import org.apache.wicket.markup.html.WebComponent;
 import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.util.encoding.UrlEncoder;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.StringResourceStream;
@@ -265,15 +269,18 @@ class AjaxRequestHandlerTest extends WicketTestCase
 	@Test
 	void globalAjaxRequestTargetListeners()
 	{
-		final ValidatingAjaxRequestTargetListener listener = new ValidatingAjaxRequestTargetListener();
-
+		ValidatingListener listener = new ValidatingListener();
+		
 		tester.getApplication().getAjaxRequestTargetListeners().add(listener);
+		tester.getApplication().getRequestCycleListeners().add(listener);
 
 		tester.startPage(TestEventPage.class);
 		tester.clickLink(MockPageWithLinkAndComponent.LINK_ID, true);
 
-		assertTrue(listener.onBeforeRespondExecuted);
-		assertTrue(listener.onAfterRespondExecuted);
+		tester.assertContains("BEFORE_RESPOND_PREPEND");
+		tester.assertContains("BEFORE_RESPOND_APPEND");
+		tester.assertContains("AFTER_RESPOND_PREPEND");
+		tester.assertContains("AFTER_RESPOND_APPEND");
 	}
 
 	/**
@@ -396,23 +403,32 @@ class AjaxRequestHandlerTest extends WicketTestCase
 		}
 	}
 
-	private static class ValidatingAjaxRequestTargetListener implements AjaxRequestTarget.IListener
+	private static class ValidatingListener implements AjaxRequestTarget.IListener, IRequestCycleListener
 	{
-		boolean onBeforeRespondExecuted = false;
-		boolean onAfterRespondExecuted = false;
-
 		@Override
 		public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target)
 		{
-			onBeforeRespondExecuted = true;
-
+			target.prependJavaScript("BEFORE_RESPOND_PREPEND");
+			target.appendJavaScript("BEFORE_RESPOND_APPEND");
 		}
 
 		@Override
-		public void onAfterRespond(Map<String, Component> map,
-			AjaxRequestTarget.IJavaScriptResponse response)
+		public void onAfterRespond(Map<String, Component> map, AjaxRequestTarget target)
 		{
-			onAfterRespondExecuted = true;
+			target.prependJavaScript("AFTER_RESPOND_PREPEND");
+			target.appendJavaScript("AFTER_RESPOND_APPEND");
+		}
+		
+		@Override
+		public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler) {
+			if (handler instanceof AjaxRequestHandler) {
+				try {
+					((AjaxRequestHandler) handler).appendJavaScript("FAIL");
+					
+					fail();
+				} catch (IllegalStateException javascriptFrozen) {
+				}
+			}
 		}
 	}