How to pass a javascript callback function that calls back to server?

How can one provide a callback function to a javascript function call that will callback to the server side?

I’ve tried: (but it doesn’t work… can’t serialize the wanted callback to json… which I’m wanting a javascript function passed as the argument anyways)

Page page = UI.getCurrent().getPage();
        SerializableBiConsumer<JsonValue, JsonValue> callback=new SerializableBiConsumer<JsonValue, JsonValue>() {
			@Override
			public void accept(JsonValue error, JsonValue result) {
				//do stuff
			}
		};
		page.executeJavaScript("steem.api.getDynamicGlobalProperties($0);", callback);

ok, I’ve come up with the following “hack”. Is there a better way?

I do the following in the test project view:

ExecuteWithCallback withCallback = new ExecuteWithCallback(jsonValues -> {
			int ix = 0;
			for (JsonValue value : jsonValues) {
				TextArea textArea = new TextArea("JSON RECEIVED [" + (++ix) + "]
");
				if (value == null) {
					textArea.setValue("[NULL]
");
				} else {
					textArea.setValue(value.toJson());
					textArea.setWidth("98%");
				}
				SteemVaadinView.this.add(textArea);
			}
		});
		add(withCallback);
		withCallback.call();

Using the following to execute the global javascript and have its callback hit the server.

package com.muksihs.vaadin.steem;

import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.dom.Style;

import elemental.json.Json;
import elemental.json.JsonObject;
import elemental.json.JsonValue;

@SuppressWarnings("serial")
@Tag("script")
public class ExecuteWithCallback extends Component {
	private static int counter=0;
	private final int me;
	private final String id;
	private final ExecuteWithCallback.JsonvalueCallback callback;
	public ExecuteWithCallback(ExecuteWithCallback.JsonvalueCallback callback) {
		this.callback = callback;
		me = ++counter;
		id=this.getClass().getName()+"-"+(me);
		Style style = this.getElement().getStyle();
		style.set("display", "none");
		this.setId(id);
	}
	
	public static interface JsonvalueCallback {
		void onCallback(JsonValue... jsonValues);
	}
	
	public void call() {
		JsonObject query = Json.createObject();
		query.put("tag", "leatherdog-games");
		query.put("limit", 2);
		UI.getCurrent().getPage().executeJavaScript("steem.api.getDiscussionsByBlog($0,document.getElementById(\""+id+"\").$server.callback)", query);
	}
	@ClientCallable
	private void callback(JsonValue... jsonValues) {
		System.out.println("=== CALLBACK ... "+(jsonValues==null?"NULL":jsonValues.length));
		if (callback!=null) {
			callback.onCallback(jsonValues);
		}
		getElement().removeFromParent();
	}
}

I’ve worked out my hack to be something more general purpose in use:

See: https://github.com/muksihs/steem-vaadin/tree/7abe11d5b0d5f8745b423b04417c9d91667248fd

The utility routines are JsExecuteThen, JsWithArrayCallback, and JsWithCallback.

A couple of basic examples are in the file SteemVaadinView.java

Basically, a component is created for each execute to provide a callback hook, then the getPage javascript executor is called with the hooks provided as the callback/then/catch functions as arguments to the function call.

After the callback completes on the server side, the element for the component is removed.

Updated code: https://github.com/muksihs/steem-vaadin/tree/612a01ac284e3c90386043ff7dd25a60a1add318/src/main/java/com/muksihs/vaadin/javascript