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 a cell
So-called FrontEndExecutable gives a DOM element our function because of their StandardForm is defined like that. This is the easiest way
.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 or use ViewBox directly i.e.
MyFunction2 /: MakeBoxes[m_MyFunction2, StandardForm] := (
ViewBox[m, m]
)
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[{},
ViewBox[m, m]
]
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"-> HTMLView[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 see HTMLView page.
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;