Communication
Here we will highlight a few methods for communication with Javascript
See also WLJS Functions
Direct call
Let us define our dummy function
.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
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)
.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
CreateFrontEndObject[MyFunction2["Hello World!"]]
it behaves like a symbol.
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
.js
core.MyFunction3 = async (args, env) => {
return [1,2,3,4,5,6];
}
and to read it synchronously
FrontFetch[MyFunction3[]]
or asynchronously
Then[FrontFetchAsync[MyFunction3[]], Function[data, Print[data]]]
Emitting events
We can call any WL function using Events system
.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
EventHandler["eventUid", Print]
Custom UI component
Using JS one can craft its own UI components. For example
.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
MyCustomComponent /: MakeBoxes[m_MyCustomComponent, StandardForm] := With[{
o = CreateFrontEndObject[m]
},
MakeBoxes[o, StandardForm]
]
In action
MyCustomComponent["Click me pls", "event1"]
EventHandler["event1", Print];
Integrating with standard inputs
It is much easier to work with EventObject
ClearAll[MyCustomComponent]
MyCustomInput[label_String] := With[{uid = CreateUUID[]},
EventObject[<|"Id"->uid, "View"->MyCustomComponent[label, uid]|>]
]
in action it behaves similar to InputRange and etc
EventHandler[MyCustomInput["Click me"], Print]
Using WLX
It is much easier to write markup in WLX compared to JS
.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
MyCustomComponent2["Event"->"test", "Label"->"Hello World!"] // WLXEmbed
EventHandler["test", Print];
or combining with a previous technic
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
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)
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;