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; + } + }