Search code examples
javascriptjavavaadin

Vaadin - Get a value from javascript via executeJs


Hi I want to call a javascript function and return the result back to java. In detail, I have a leaflet map component and I call getBounds() to get the view's bounding box - and I want to pass this bounding box back to the server side java.

I understand that this is an async call. I found two ways to get a result. The first one:

String js = map.clientComponentJsAccessor();
PendingJavaScriptResult executeJs = UI.getCurrent().getPage().executeJs("return " + js + ".getBounds()");
executeJs.then(json -> System.out.println("Json: " + json.toJson()));

This works well and the expected result is printed. Unfortunately, I don't get the result out of the lambda in the then function.

The second approach is:

String js = map.clientComponentJsAccessor();
PendingJavaScriptResult executeJs = UI.getCurrent().getPage().executeJs("return " + js + ".getBounds()");       
try {
    String json = executeJs.toCompletableFuture().get().toJson();
    System.out.println("Json:" + json);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

This looked pretty good at first, but during execution I get an IllegalStateException with this message: Cannot block on the value from the thread that has locked the session. This is because the request that delivers the value cannot be processed while this thread holds the session lock.

I understand what it means, but I don't know how to avoid it.

Can you help me out here to solve my issue with approach 1 or 2? Or is there a better solution using the vaadin api?


Solution

  • Getting the return value back to the server is always asynchronous. This is because the session lock is held while the framework runs your application logic that runs executeJs. The framework can't even send the JS to execute to the browser until after the lock has been released, i.e. after you have returned from the event handler or whatever else it is that triggers running the code block with your executeJs statement.

    What this means is that you just have to accept that your logic is split up into two separate parts. You can show a placeholder in your UI (e.g. a spinner) in the first chunk and then continue finalizing the UI updates in the then callback.