In this example I demonstrate how we can respond to JS events within MATLAB. Here's a preview of what is possible using this approach:
Take a look at the code below, which was tested on R2018a. Please save all files in the same folder, and run it using jsEventDemo(demoNum)
, where demoNum
is 1...4
.
function varargout = jsEventDemo(demoNum)
%% Handle inputs and outputs
if ~nargin
demoNum = 4;
end
if ~nargout
varargout = {};
end
%% Create a simple figure:
hFig = uifigure('Position',[680,680,330,240],'Resize','off');
hTA = uitextarea(hFig, 'Value', 'Psst... Come here...!','Editable','off');
[hWin,idTA] = mlapptools.getWebElements(hTA);
% Open in browser (DEBUG):
% mlapptools.waitForFigureReady(hFig); mlapptools.unlockUIFig(hFig); pause(1);
% web(hWin.URL,'-browser')
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
switch demoNum
%% Demo #1: Respond to mouse events, inside JS, using "onSomething" bindings:
case 1
% Example from:
% https://dojotoolkit.org/documentation/tutorials/1.10/events/#dom-events
jsCommand = sprintf(fileread('jsDemo1.js'), idTA.ID_val);
%{
require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse"],
function(on, dom, domStyle, mouse) {
var myDiv = dom.byId("%s");
on(myDiv, mouse.enter, function(evt){
domStyle.set(myDiv, "backgroundColor", "red");
});
on(myDiv, mouse.leave, function(evt){
domStyle.set(myDiv, "backgroundColor", "");
});
});
%}
hWin.executeJS(jsCommand);
%% Demo #2: Respond to mouse click events, inside JS, using pub/sub:
case 2
% Example from:
% https://dojotoolkit.org/documentation/tutorials/1.10/events/#publish-subscribe
hTA.Value = "Click here and see what happens";
jsCommand = sprintf(fileread('jsDemo2.js'), idTA.ID_val);
%{
require(["dojo/on", "dojo/topic", "dojo/dom"],
function(on, topic, dom) {
var myDiv = dom.byId("%s");
on(myDiv, "click", function() {
topic.publish("alertUser", "Your click was converted into an alert!");
});
topic.subscribe("alertUser", function(text){
alert(text);
});
});
%}
hWin.executeJS(jsCommand);
%% Demo #3: Trigger MATLAB callbacks programmatically from JS by "pressing" a fake button:
case 3
hB = uibutton(hFig, 'Visible', 'off', 'Position', [0 0 0 0], ...
'ButtonPushedFcn', @fakeButtonCallback);
[~,idB] = mlapptools.getWebElements(hB);
jsCommand = sprintf(fileread('jsDemo3.js'), idTA.ID_val, idB.ID_val);
%{
require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse"],
function(on, dom, domStyle, mouse) {
var myDiv = dom.byId("%s");
var fakeButton = dom.byId("%s");
on(myDiv, mouse.enter, function(evt){
fakeButton.click();
});
});
%}
hWin.executeJS(jsCommand);
%% Demo 4: Trigger MATLAB callbacks and include a "payload" (i.e. eventData) JSON:
case 4
hB = uibutton(hFig, 'Visible', 'off', 'Position', [0 0 0 0],...
'ButtonPushedFcn', @(varargin)smartFakeCallback(varargin{:}, hWin));
[~,idB] = mlapptools.getWebElements(hB);
jsCommand = sprintf(fileread('jsDemo4.js'), idTA.ID_val, idB.ID_val);
%{
var payload = [];
require(["dojo/on", "dojo/dom", "dojo/topic", "dojo/mouse"],
function(on, dom, topic, mouse) {
var myDiv = dom.byId("%s");
var fakeButton = dom.byId("%s");
topic.subscribe("sendToMATLAB", function(data){
payload = data;
fakeButton.click();
});
on(myDiv, mouse.enter, function(evt){
data = {action: "enter",
coord: [evt.clientX, evt.clientY]};
topic.publish("sendToMATLAB", data);
});
on(myDiv, mouse.leave, function(evt){
data = {action: "leave",
coord: [evt.clientX, evt.clientY]};
topic.publish("sendToMATLAB", data);
});
});
%}
hWin.executeJS(jsCommand);
end % switch
% keyboard; %DEBUG
end
function fakeButtonCallback(obj, eventData) %#ok<INUSD>
disp('Callback activated!');
pause(2);
clc;
end
function smartFakeCallback(obj, eventData, hWin)
% Retrieve and decode payload JSON:
payload = jsondecode(hWin.executeJS('payload'));
% Print payload summary to the command window:
disp("Responding to the fake " + eventData.EventName + ...
" event with the payload: " + jsonencode(payload) + ".");
% Update the TextArea
switch payload.action
case "enter"
act_txt = "entered";
case "leave"
act_txt = "left";
end
coord = payload.coord;
str = ["Mouse " + act_txt + " from: "; "(" + coord(1) + "," + coord(2) + ")"];
obj.Parent.Children(2).Value = str;
end
Several thoughts:
- The attached
.js
files will not work by themselves, rather, they requiresprintf
to replace the%s
with valid widget IDs. Of course, these could be made into proper JS functions. - I tried getting it to work with a
<textarea>
control, so that we would get the payload right in the callback'seventData
object in MATLAB, but couldn't get it to fire programmatically (solutions like this didn't work). This is why I had to store the payload as JSON, and retrieve it withjsondecode(hWin.executeJS('payload'))
.
When a property, like
backgroundColor
injsDemo1
, is changed in JS, the visual effect is immediate. Apparently Matlab caches the property value, as 'hTA.BackgroundColor' is still[1 1 1]
. Is there any way to force Matlab to update the cached property value? If so, this may be a way to transfer data from JS to Matlab.