Skip to main content

Communication

Here we will highlight a few methods for communication with Javascript

note

Direct call

Let us define our dummy function

cell 1
.js
core.MyFunction = async (args, env) => {
const data = await interpretate(args[0], env);
alert(data);
}

then the fastest and most direct way of calling it is using FrontSubmit

cell 2
FrontSubmit[MyFunction["Hello World!"]]

or chaining it with some other WLJS function

FrontSubmit[MyFunction[ReadClipboard[]]]

Execution in the container

So-called containers gives a DOM element, local memory for our function (see more WLJS Functions)

cell 1
.js

core.MyFunction2 = async (args, env) => {
const data = await interpretate(args[0], env);
env.element.innerHTML = `<span style="color:red">${data}</span>`;
}

Now we can call it from our cell

cell 2
CreateFrontEndObject[MyFunction2["Hello World!"]]

it behaves like a symbol.

tip

You don't have to always use CreateFrontEndObject. It is possible to force Wolfram Kernel to apply it automatically on output using MakeBoxes

This is basically how Graphics and others are implemented.

Data fetching

A back communication is also possible

cell 1
.js

core.MyFunction3 = async (args, env) => {
return [1,2,3,4,5,6];
}

and to read it synchronously

cell 2
FrontFetch[MyFunction3[]]

or asynchronously

cell 2
Then[FrontFetchAsync[MyFunction3[]], Function[data, Print[data]]]

Emitting events

We can call any WL function using Events system

cell 1
.js

const button = document.createElement('button');
button.innerText = "Press me";

button.addEventListener('click', () => {
server.kernel.emitt('eventUid', 'True');
});

return button;

Now we can capture it using

cell 2
EventHandler["eventUid", Print]

Custom UI component

Using JS one can craft its own UI components. For example

cell 1
.js

core.MyCustomComponent = async (args, env) => {
const label = await interpretate(args[0], env);
const ev = await interpretate(args[1], env);

const button = document.createElement('button');
button.innerText = label;
button.style.backgroundColor = "lightblue";

button.addEventListener('click', () => {
server.kernel.emitt(ev, 'True');
});

env.element.appendChild(button);
}

Now we can make boxes for it

cell 2
MyCustomComponent /: MakeBoxes[m_MyCustomComponent, StandardForm] := With[{
o = CreateFrontEndObject[m]
},
MakeBoxes[o, StandardForm]
]

In action

cell 3
MyCustomComponent["Click me pls", "event1"]
EventHandler["event1", Print];

Integrating with standard inputs

It is much easier to work with EventObject

cell 3
ClearAll[MyCustomComponent]

MyCustomInput[label_String] := With[{uid = CreateUUID[]},
EventObject[<|"Id"->uid, "View"->MyCustomComponent[label, uid]|>]
]

in action it behaves similar to InputRange and etc

cell 4
EventHandler[MyCustomInput["Click me"], Print]

Using WLX

It is much easier to write markup in WLX compared to JS

cell 1
.wlx

MyCustomComponent2[OptionsPattern[]] := With[{
Event = OptionValue["Event"],
Label = OptionValue["Label"],
Uid = CreateUUID[]
},
<div>
<button id="{Uid}" style="background: lightblue" class="p-1">
<Label/>
</button>
<script type="module">
document.getElementById('<Uid/>').addEventListener('click', () => {

server.kernel.emitt('<Event/>', 'True');
})
</script>
</div>
]

Options[MyCustomComponent2] = {"Event" -> "", "Label" -> "Click me"};

In action

cell 2
MyCustomComponent2["Event"->"test", "Label"->"Hello World!"] // WLXEmbed
EventHandler["test", Print];

or combining with a previous technic

cell 2
MyCustomInput2[label_String] := With[{uid = CreateUUID[]},
EventObject[<|"Id"->uid, "View"-> WLXEmbed[MyCustomComponent2["Label"->label, "Event"->uid]]|>]
]

You can use it as a normal input element

cell 3
EventHandler[MyCustomInput2["Click me"], Print]

Side effects

To call another function after an elements was drawn, use an options of WLXEmbed. This is used for dynamic updates in TextView and HTMLView implementations.

Emit event from WLJS

One can fire an event also using frontend's function EventFire, which is effectively acts like server.kernel.emitt being called from the WLJS Interpreter (i.e. using FrontSubmit or other and wrapped in Offload)

danger

Not implemented!

EventHandler["internalEvent", Print];
FrontSubmit[EventFire["internalEvent", ReadClipboard[]] // Offload]

It will send an expression to be executed on the frontend, that reads a clipboard and fires back an event with a fetched data.

Request evaluation

It is also possible from Javascript to request an evaluation of any symbol using server object

.js

const doc = document.createElement('span');

const run = async () => {
const data = await server.kernel.ask('RandomReal[{-1,1}, 3]');
const result = await interpretate(data, {});
doc.innerText = result.join(', ');
}

run();

return doc;