Docs

Calling JavaScript from the Server

Call JavaScript functions from the server.

The Element API contains methods for executing JavaScript in the browser from the server side.

callJsFunction Method

The Element.callJsFunction() method allows you to run a client-side component function from the server side. The method accepts two parameters: the name of the function to call; and the arguments to pass to the function.

The arguments passed to the function must be a type supported by the communication mechanism. The supported types are String, Boolean, Integer, Double, JsonNode, Element, Component, and JsFunction (see Passing JavaScript Functions).

Example 1. Calling the clearSelection() JavaScript function on the root element from the server side
Source code
Java
public void clearSelection() {
    getElement().callJsFunction("clearSelection");
}
Example 2. Calling the expand(otherComponentElement) JavaScript function on the root element from the server side
Source code
Java
public void setExpanded(Component otherComponent) {
    getElement().callJsFunction("expand",
            otherComponent.getElement());
}
Example 3. Passing a JSON object to a JavaScript function
Source code
Java
public void configure(String label, int count) {
    ObjectNode config = JacksonUtils.createObjectNode();
    config.put("label", label);
    config.put("count", count);
    config.put("enabled", true);
    getElement().callJsFunction("configure", config);
}

executeJs Method

You can also use the generic Element.executeJs() method to run JavaScript asynchronously from the server side. This method can be used in addition to the Element.callJsFunction() method when calling any JavaScript.

The executeJs() method accepts two parameters: the JavaScript expression to invoke; and the parameters to pass to the expression. The given parameters are available as variables named $0, $1, and so on.

The arguments passed to the expression must be a type supported by the communication mechanism. The supported types are String, Integer, Double, Boolean, JsonNode, Element, Component, and JsFunction (see Passing JavaScript Functions).

Example 4. Calling MyModule.complete(true) on the client side
Source code
Java
public void complete() {
    getElement().executeJs("MyModule.complete($0)", true);
}
Example 5. Passing a JSON array to a JavaScript expression
Source code
Java
public void setItems(List<String> items) {
    getElement().executeJs("this.items = $0", items);
}
Warning
Avoid Script Injection Vulnerabilities
Always pass arguments using the $0, $1, …​ notation to avoid script injection vulnerabilities. Never concatenate or interpolate strings to build JavaScript code to be executed.

If you need to run JavaScript without having access to an element, use the UI.getCurrent().getPage().executeJs() method.

Return Values

The return value from the JavaScript function called using callJsFunction(), or the value from a return statement in an executeJs() expression can be accessed by adding a listener to the PendingJavaScriptResult instance returned from either method.

Example 6. Checking for support of Constructable Stylesheets in the browser
Source code
Java
public void checkConstructableStylesheets() {
    getElement().executeJs(
            "return 'adoptedStyleSheets' in document")
            .then(Boolean.class, supported -> {
                if (supported) {
                    System.out.println(
                            "Feature is supported");
                } else {
                    System.out.println(
                            "Feature isn't supported");
                }
            });
}

If the return value is a JavaScript Promise, the client only sends the return value to the server when the Promise is resolved.

Deserializing Generic Types with TypeReference

The then() method accepts a Class parameter for simple types, but this doesn’t work for generic types like List<Person> due to Java type erasure. For these cases, use the Jackson TypeReference overloads.

Example 7. Deserializing a JavaScript array into a List<Person>
Source code
Java
getElement().executeJs("return this.getItems()")
        .then(new TypeReference<List<Person>>() {},
                items -> {
                    // items is List<Person>
                    items.forEach(person ->
                            System.out.println(person.getName()));
                });

An error handler can be provided as a second callback. The handler receives the error message from the failed JavaScript execution as a String:

Source code
Java
getElement().executeJs("return this.getItems()")
        .then(new TypeReference<List<Person>>() {},
                items -> processItems(items),
                errorMessage -> handleError(errorMessage));

You can also use toCompletableFuture(TypeReference) to get the result as a CompletableFuture:

Source code
Java
CompletableFuture<Map<String, Person>> future = getElement()
        .executeJs("return this.getPersonMap()")
        .toCompletableFuture(
                new TypeReference<Map<String, Person>>() {});

Passing JavaScript Functions [version-badge-upcoming]

A JsFunction lets you build a reusable JavaScript function on the server and pass it as a parameter to executeJs() or callJsFunction(). The function arrives on the client as a real callable function with its captured values pre-bound, so you don’t need to concatenate JavaScript fragments to embed server-side values.

The first argument to JsFunction.of() is a JavaScript function body. The remaining arguments are captured values, referenced inside the body as $0, $1, … using the same naming convention as executeJs() parameters.

Example 8. Defining a function and invoking it
Source code
Java
JsFunction greet = JsFunction.of(
        "return $0 + ' ' + $1;", "Hello", "World");
getElement().executeJs(
        "this.textContent = $0();", greet);

Captures may be any value supported as an executeJs() parameter. An attached Element arrives as the corresponding DOM node; a detached Element arrives as null.

Example 9. Capturing an element
Source code
Java
Div target = new Div();
add(target);

JsFunction mutate = JsFunction.of(
        "$0.textContent = 'updated';",
        target.getElement());
getElement().executeJs("$0();", mutate);

Declaring Runtime Arguments

Use withArguments() to declare named parameters that the function accepts at call time. The body references them by name, and the JavaScript that invokes the function passes them positionally:

Example 10. A function with runtime arguments
Source code
Java
JsFunction format = JsFunction
        .of("return prefix + ':' + suffix;")
        .withArguments("prefix", "suffix");
getElement().executeJs(
        "this.textContent = $0('alpha', 'beta');", format);

Controlling this

In JavaScript, the value of this inside a function depends on how the function is invoked, not where it is defined. The body of a JsFunction follows this convention – this is not bound to the host element automatically.

When the function is invoked as $0(…​) from inside an executeJs() expression, the body’s this is the global object (or undefined in strict mode), not the element on which executeJs() was called. To set this explicitly, invoke the function with Function.prototype.call(): its first argument becomes this inside the body.

Example 11. Calling the function with a specific this
Source code
Java
JsFunction setOwnText = JsFunction
        .of("this.textContent = msg;")
        .withArguments("msg");
getElement().executeJs(
        "$0.call(this, 'host element text');", setOwnText);

If the body always needs to reference the same element regardless of how the function is called, pass it as a capture instead of relying on this.

Client-Side Lifecycle With addJsInitializer [version-badge-upcoming]

The Element.addJsInitializer() method registers a JavaScript expression that runs each time a client-side DOM node is created for the element, and whose returned cleanup callback runs when that DOM node is discarded or the returned Registration is removed.

Use this when you need to install something on the client-side DOM – an event listener, a third-party widget, an observer – and reliably tear it down. A one-shot executeJs() call doesn’t cover two cases: a real re-attach gives the element a brand-new DOM node that no longer has your listener, and cleanup from a server-side detach listener cannot be delivered because the element is leaving the tree.

The expression syntax is the same as executeJs(): this is the host element on the client, and parameters are referenced as $0, $1, …. If the expression returns a function, that function is invoked at teardown.

Example 12. Installing a listener with cleanup
Source code
Java
Registration registration = getElement().addJsInitializer("""
        const handler = (event) => console.log('clicked', event.target);
        this.addEventListener('click', handler);
        return () => this.removeEventListener('click', handler);
        """);

Remove the registration on the server when the listener is no longer needed; the cleanup callback then runs on the client.

Re-Attach Semantics

The initializer is re-run after a real re-attach – when the element is removed from the DOM in one round trip and re-added in a later one, and so the browser receives a fresh DOM node. It is not re-run when the element is detached and re-attached on the server inside a single round trip, because the client never discarded its DOM in that case.

Cleanup Constraints

The return value of the expression is read synchronously. A function is treated as the cleanup callback; returning nothing (no return, or undefined) means there’s no cleanup. Returning any other value – including a Promise resolving to a function – is logged as an error on the client. If you need asynchronous setup, do the async work inside the body and store the teardown in a synchronously returned function.

AB7EDF45-DB22-4560-AF27-FF1DC6944482

Updated