From 5a168a6d7a04eba959fbe7d10993b0eff14ace98 Mon Sep 17 00:00:00 2001
From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com>
Date: Thu, 13 Nov 2025 15:39:11 -0300
Subject: [PATCH] feat: add executeJs helper
---
.../ElementalPendingJavaScriptResult.java | 90 +++++++++++++++++++
.../vaadin/jsonmigration/JsonMigration.java | 18 ++++
.../jsonmigration/JsonMigrationHelper.java | 5 +-
.../jsonmigration/JsonMigrationHelper25.java | 30 +++++++
.../LegacyJsonMigrationHelper.java | 17 ++++
5 files changed, 159 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalPendingJavaScriptResult.java
diff --git a/src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalPendingJavaScriptResult.java b/src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalPendingJavaScriptResult.java
new file mode 100644
index 0000000..e2c80c6
--- /dev/null
+++ b/src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalPendingJavaScriptResult.java
@@ -0,0 +1,90 @@
+/*-
+ * #%L
+ * Json Migration Helper
+ * %%
+ * Copyright (C) 2025 Flowing Code
+ * %%
+ * 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.
+ * #L%
+ */
+package com.flowingcode.vaadin.jsonmigration;
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * 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.
+ */
+
+import com.vaadin.flow.component.page.PendingJavaScriptResult;
+import com.vaadin.flow.function.SerializableConsumer;
+import elemental.json.JsonValue;
+import java.io.Serializable;
+
+/**
+ * A pending result from a JavaScript snippet sent to the browser for evaluation. This interface
+ * utilizes Elemental JSON in order to abstract away breaking changes introduced in Vaadin version
+ * 25.
+ *
+ * @see PendingJavaScriptResult
+ * @author Vaadin Ltd
+ */
+public interface ElementalPendingJavaScriptResult extends Serializable {
+
+ /**
+ * Adds an untyped handler that will be run for a successful execution and a
+ * handler that will be run for a failed execution. One of the handlers will
+ * be invoked asynchronously when the result of the execution is sent back
+ * to the server. It is not possible to synchronously wait for the result of
+ * the execution while holding the session lock since the request handling
+ * thread that makes the result available will also need to lock the
+ * session.
+ *
+ * Handlers can only be added before the execution has been sent to the
+ * browser.
+ *
+ * @param resultHandler
+ * a handler for the JSON representation of the value from a
+ * successful execution, not null
+ * @param errorHandler
+ * a handler for an error message in case the execution failed,
+ * or null to ignore errors
+ */
+ void then(SerializableConsumer resultHandler,
+ SerializableConsumer errorHandler);
+
+ /**
+ * Adds an untyped handler that will be run for a successful execution. The
+ * handler will be invoked asynchronously if the execution was successful.
+ * In case of a failure, no handler will be run.
+ *
+ * A handler can only be added before the execution has been sent to the
+ * browser.
+ *
+ * @param resultHandler
+ * a handler for the JSON representation of the return value from
+ * a successful execution, not null
+ */
+ default void then(SerializableConsumer resultHandler) {
+ then(resultHandler, null);
+ }
+
+}
diff --git a/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java b/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java
index 1f66778..ef23c12 100644
--- a/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java
+++ b/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java
@@ -19,11 +19,13 @@
*/
package com.flowingcode.vaadin.jsonmigration;
+import com.vaadin.flow.component.page.PendingJavaScriptResult;
import com.vaadin.flow.dom.DomEvent;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.server.Version;
import elemental.json.JsonObject;
import elemental.json.JsonValue;
+import java.io.Serializable;
import java.lang.reflect.Method;
import lombok.SneakyThrows;
@@ -114,6 +116,22 @@ public static void setPropertyJson(Element element, String name, JsonValue json)
invoke(Element_setPropertyJson, element, name, json);
}
+ /**
+ * Asynchronously runs the given JavaScript expression in the browser in the context of this
+ * element.
+ *
+ * @param element the {@code Element} on which to run the JavaScript expression
+ * @param expression the JavaScript expression to invoke
+ * @param parameters parameters to pass to the expression
+ * @return a pending result that can be used to get a value returned from the expression
+ * @see Element#executeJs(String, Serializable...)
+ */
+ public static ElementalPendingJavaScriptResult executeJs(Element element, String expression,
+ Serializable... parameters) {
+ PendingJavaScriptResult result = element.executeJs(expression, parameters);
+ return helper.convertPendingJavaScriptResult(result);
+ }
+
/**
* Gets additional data related to the event.
*
diff --git a/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java b/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java
index c51f778..9bd05fe 100644
--- a/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java
+++ b/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java
@@ -19,6 +19,7 @@
*/
package com.flowingcode.vaadin.jsonmigration;
+import com.vaadin.flow.component.page.PendingJavaScriptResult;
import elemental.json.JsonValue;
import java.lang.reflect.Method;
@@ -29,5 +30,7 @@ interface JsonMigrationHelper {
JsonValue convertToJsonValue(Object object);
Object invoke(Method method, Object instance, Object... args);
-
+
+ ElementalPendingJavaScriptResult convertPendingJavaScriptResult(PendingJavaScriptResult result);
+
}
diff --git a/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java b/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java
index 8ea995c..3079ee2 100644
--- a/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java
+++ b/src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java
@@ -21,10 +21,13 @@
import java.lang.reflect.Method;
import java.util.Arrays;
+import com.vaadin.flow.component.page.PendingJavaScriptResult;
+import com.vaadin.flow.function.SerializableConsumer;
import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonObject;
import elemental.json.JsonValue;
+import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import tools.jackson.databind.JsonNode;
@@ -135,4 +138,31 @@ private static BaseJsonNode convertToJsonNode(JsonValue jsonValue) {
}
}
+
+ @Override
+ public ElementalPendingJavaScriptResult convertPendingJavaScriptResult(
+ PendingJavaScriptResult result) {
+ return new PendingJavaScriptResultImpl(result);
+ }
+
+ @SuppressWarnings("serial")
+ @AllArgsConstructor
+ private static final class PendingJavaScriptResultImpl
+ implements ElementalPendingJavaScriptResult {
+ private final PendingJavaScriptResult delegate;
+
+ @SuppressWarnings("rawtypes")
+ private static SerializableConsumer wrap(SerializableConsumer resultHandler) {
+ return (SerializableConsumer) node -> resultHandler.accept(convertToJsonValue(node));
+ };
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void then(SerializableConsumer resultHandler,
+ SerializableConsumer errorHandler) {
+ delegate.then(wrap(resultHandler), errorHandler);
+ }
+
+ }
+
}
diff --git a/src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java b/src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java
index 425f213..c4b331e 100644
--- a/src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java
+++ b/src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java
@@ -19,10 +19,13 @@
*/
package com.flowingcode.vaadin.jsonmigration;
+import com.vaadin.flow.component.page.PendingJavaScriptResult;
import java.lang.reflect.Method;
import elemental.json.JsonValue;
+import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
+import lombok.experimental.Delegate;
@NoArgsConstructor
class LegacyJsonMigrationHelper implements JsonMigrationHelper {
@@ -43,4 +46,18 @@ public Object invoke(Method method, Object instance, Object... args) {
return method.invoke(instance, args);
}
+ @Override
+ public ElementalPendingJavaScriptResult convertPendingJavaScriptResult(
+ PendingJavaScriptResult result) {
+ return new PendingJavaScriptResultImpl(result);
+ }
+
+ @SuppressWarnings("serial")
+ @AllArgsConstructor
+ private static final class PendingJavaScriptResultImpl
+ implements ElementalPendingJavaScriptResult, PendingJavaScriptResult {
+ @Delegate
+ private final PendingJavaScriptResult delegate;
+ }
+
}