So far we have seen that when we create an uihtml component, we are actually embedding our own website inside an <iframe> of the parent figure. Components will live both in Matlab and HTML/JS, and in each of these worlds we will program different functionalities.

  • The uihtml component in Matlab will be the interface to the HTML. We (and our users and code) will interact with it to modify its appearance or to react to its events. From now on, I’ll be referring this as the Matlab-side of the component or the Controller.
  • The View will be the part of the component that we implement in HTML, JS and CSS. Here we will define the web elements that form our component, and we will also need to decide what parts of it can be modified by the controller.

Controller and view will need to be connected: the controller will need to command changes to the view, and the view will need to notify the controller that an event has happened. The goal of todays post is to focus on how this data transfer works.

The transfer mechanism

Sharing data between the controller (in Matlab) and the view (in Javascript) is simple.

In Matlab, data is sent/received using the same uihtml object that we initialize when we create the component.

  • To send data, we will only need to set the Data property to whatever information we want to set to the view.
  • Whenever the component receives data from the view, a DataChanged event will be triggered. We can listen to those events by attaching a function to the DataChangedFcn property of the uihtml element, and then getting the received data from the same Data property.
MATLAB
% Controller
u = uihtml('HTMLSource', 'source.html');
u.Data = ...; % Send data 
u.DataChangedFcn = @(~,~) ...; % Receive data

To connect our view, we need to define a setup function in Javascript. This function will be called when the HTML document finishes loading, and its first input argument will be the node we will use to send & receive data.

  • To send data, we again need to set the Data property of the node, like we did in Matlab.
  • To receive data, we need to attach a listener that is triggered on DataChanged events.
JavaScript (in source.html)
// View
function setup(uihtmlNode) {
    uihtmlNode.Data = ...; // Send data
    uihtmlNode.addEventListener("DataChanged", function() {...}); // Receive data
}

How is data sent?

Data shared between the controller and the view is first parsed into a string, then transferred to the other side, and lastly re-parsed into a built-in type.

When we send data from the controller, Matlab calls the jsonencode function on the Data property. This converts the data into a string with a format that can be later interpreted by Javascript. When the view receives the value, the built-in JSON.parse method is called, converting the string into a base JS type.

Similarly, when the view sends its data, the JSON.stringify method is called first, converting the JS data into a string. The controller will receive the string and transform it to a Matlab base-type by calling jsondecode.

There are a couple of things we need to keep in mind from these conversions:

  • There is a 1 to 1 relationship between Matlab an Javascript base classes. A logical value in Matlab is mapped into a JS Boolean; a struct maps to an Object, with its struct fields mapping into Object properties recursively; arrays of a given type also map into a JS Arrays of the mapped type; … and so on. The conversions also work from JS to Matlab.
  • jsonencode only has a defined behaviour for Matlabs base classes. If we want to set Data to a custom class, it will need to have a public jsonencode method that takes precedence over the built-in function. But remember, the encoded string will need to be re-parsed in Javascript, so it will become a JS base type.
A simple example

This was a lot of boring theory. Let’s see how it would look like with a (very simple) example. We will be creating a button whose background color and inner text can be changed from Matlab. When the button is clicked, Matlab will also print a message in the command window.

To spice things a bit, I’ll first define a Style class that will hold our background color and text properties. Notice that I created a jsonencode method that in essence converts my object to a struct to then parse it into a string.

MATLAB (Style.m)
classdef Style
  properties
    Color (1,1) string
    Text (1,1) string
  end
  
  methods 
    function this = Style(color, text)
      % Style constructor
      this.Color = color;
      this.Text = text;
    end
    
    function out = jsonencode(this, varargin)
      s = struct("color", this.Color, "text", this.Text);
      out = jsonencode(s, varargin{:});
    end
  end
end

Defining the component in Matlab is straightforward: we create a uihtml object, set its source to our HTML file (button.html), and add a listener to its DataChangedFcn property that displays whatever is received from the view.

MATLAB (main.m)
f = uifigure();
c = uihtml(f, 'HTMLSource', 'button.html');

% Add a listener that displays the Data property when it changes
c.DataChangedFcn = @(~,~) disp(c.Data);

In Javascript, things are more interesting. We need to first define the setup function with the uihtml as its input parameter. When called, the function will get a reference of the <button> (that we define in the <body>), and create 2 event listeners:

  1. The first one will modify the color and text of the button whenever a new style value is received from Matlab.
  2. The second one send a message to Matlab whenever the <button> is clicked.
HTML (button.html)
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        function setup(uihtml) {
            let button = document.getElementById("myButton");
            
            // Listen to when we receive a "style" message
            // Change the buttons background color and inner text
            uihtml.addEventListener("DataChanged", () => {
                button.style.backgroundColor = uihtml.Data.color;
                button.innerHTML = uihtml.Data.text;
            });
            
            // When the button is clicked, send a message to Matlab
            button.addEventListener("click", () => {
                uihtml.Data = "Somebody clicked me";
            });
        }
  </script>
</head>

<body>
    <button id="myButton" style="width:100%;">Click me!</button>
</body>
</html>

Try it out yourself! Run main.m and see the button appearing on the figure. Click it and inspect the command window. In the command window also, try setting the data property of the uihtml to a valid style:

MATLAB
c.Data = Style("red", "I'm a red button");
% c.Data = struct("color", "red", "text", "I'm a red button")

Mini-exercise: Can you, from the command window, change the background-color of the <button> to a nice linear gradient? (Hint: check at the possible values that a CSS color can take).

One mini-trick

You have probably noticed that in our example, most of our Javascript code lies inside the  setup function. You can imagine the reason: it is the place where we gain access to the uihtml object, and therefore from where we can send and receive data.

However, there are some scenarios in which I don’t know how to send the data when the setup function is called. For example, dynamically added elements do not exist when setup runs. That means that, theoretically, we cannot link their events to the controller (like we did with the “click” event in the example). At least that is what Mathworks documentation states: “The uihtml JavaScript object is accessible only from within the setup function”.

But there is a way… there is always a way. And it is simple. We just need to understand that, in Javascript, a variable becomes global if it’s defined outside of a function. Then, the only thing that we need to do is to copy the uihtml object into a variable that we have defined before the setup function.

HTML
<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript">
            let uihtml; // Define it outside the function (undefined)
            function setup(orginalUihtml) {
                uihtml = orginalUihtml; // Copy it!
            }
        </script>
    </head>
    
    <body>
        <button id="myButton"></button>
        <script type="text/javascript">
            let b = document.getElementById("myButton");
            b.onclick(()=> {uihtml.Data = "Clicked"}); // Send to Matlab
        </script>
    </body>
</html>
    

Isn’t this way cleaner? The only thing we musn’t forget is that the uihtml object only exists after the document loads. Don’t try to send stuff during initialization or you’ll start seeing errors.

One mini problem

So far we have talked about how the data is transferred between view and controller, and about what kind of data can be transferred. We are missing the when is the data sent. Sending data from the view to the controller happens whenever the Data property of the JS uihtml object changes. Straightforward and simple, no issues there. Sending data from Matlab, on the other hand, is less trivial.

To investigate the mechanism, let’s make a change to our example. Let’s add an element to the HTML that displays how many times the DataChanged event has been triggered:

HTML (button.html)
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        function setup(uihtml) {
            let button = document.getElementById("myButton");
            let counter = document.getElementById("counter");
            let count = 0;
            
            // Listen to when we receive a "style" message
            // Change the buttons background color and inner text
            uihtml.addEventListener("DataChanged", () => {
                button.style.backgroundColor = uihtml.Data.color;
                button.innerHTML = uihtml.Data.text;
                count += 1;
                counter.innerHTML = count;
            });
            
            // When the button is clicked, send a message to Matlab
            button.addEventListener("click", () => {
                uihtml.Data = "Somebody clicked me";
            });
        }
    </script>
</head>

<body>
    <button id="myButton" style="width:100%;">Click me!</button>
    <div id="counter">0</div>
</body>
</html>
        

Rerun the code in Matlab to create the component, and once the figure is loaded go to the command window and specify a new style, just like we did before.

MATLAB
c.Data = Style("red", "I'm a red button");

If everything is ok, you should see a 1 in our counter below the button, as expected.
Now, instead of writing one line in the command window, write 2 consecutive styles. Since we are setting the Data property twice, we could expect the counter to go to 3. But does it?

MATLAB
c.Data = Style("red", "I'm a red button");
c.Data = Style("green", "I'm a green button");

You may have guessed it: the answer is no. The counter only reaches 2, and the button becomes green. What has happened to the red style? Did it change so fast that we didn’t see it and JS didn’t have time to update the count? Nope, not really. The red style was never sent to the view.

You see, Matlab does not send data to the view every time something changes. Encoding and sending data to JS can take some time, and if it happened too often our program would run very slow. Instead, Matlab waits for the perfect time: the moment when the main thread is empty, and nothing else is happening. There are 3 scenarios that indicate that the time for an update has come:

  1. The code has finished executing, so there is actually nothing else in the thread to be done.
  2. The execution stops temporarily (for example, when the pause or input commands are invoked).
  3. We force Matlab to pause and update the graphics with the drawnow command.

I’m sure you have already experienced this when plotting something inside a loop. To see the evolution of the line, you had to force the update on every iteration by calling drawnow.

Behind the scenes, when we change a graphical property (in our case the Data), Matlab marks it as dirty. When the update time comes, Matlab collects all the dirty properties and sends them to the view. This is pretty neat, mainly because dirty properties are only encoded and sent once per update.

How can this be an issue to us? Well, our problem is that the Data property (the one getting marked as dirty) encapsulates the rest of the sub-properties that we want to control from our view. And we don’t want to send every sub-property on every update, only the dirty ones.

c.Data = struct("color", "red");
c.Data = struct("text", "Hello, I'm red"); 
% Only the 2nd line is sent. We can't 
% send sub-properties one by one
c.Data = struct("color", "red", ...
                "text", "Hello, I'm red"); 
% We send everything. Scales bad if 
% there are many sub-properties

Is there a way we can mark our sub-properties as dirty and only update those? Indeed. Instead of assigning the data to a new struct, we can just add/modify its fields.

MATLAB
c.Data = struct(); % Initialize the data with an empty struct.
c.Data.color = "red";
c.Data.text = "Hello, I'm green"; 
c.Data.color = "green";

This works. In the end, we are writing in the struct only the properties that have changed; and if they change more than once, their last value is the one that matters. But we need to reassign the empty struct after an update. Otherwise, the color and the text would appear forever in the struct, implying that they would be forever dirty.

How can we know when the update has happened, and thus when we can re-do the struct? We can use the fact that when the update happens, the DataChanged event in Javascript is triggered. So we can just ask JS to report back that the struct should be cleared.

JavaScript
uihtml.addEventListener("DataChanged", () => {
  ...               // Do our stuff
  uihtml.Data = {}; // send back an empty object / jsondecode('{}') == struct()
});                 

This seems to work fine, hurray! We can now go to sleep and dream tightly. But then…

Remember that the Data property is the only channel in which we send and receive the information. Our method accumulates the dirty properties inside Data, and releases them during an update. If before the update something in the view decides to send back some data… then the Data in Matlab gets overwritten, and we lose track of everything marked as dirty.

This issue arises when your app has a function that takes a some time to execute. While executing, Matlabs thread is blocked and no graphics update occur until it finishes. If in the meantime your user decided to interact with the view (something that would send data to the controller) the Data would be changed.

However, this is something that we can avoid by introducing drawnow lines here and there, specially before long functions begin executing in the background. Still, calling drawnow will make that our updates are not grouped, and will make our app slower.

But don’t worry, there is yet another solution that will make the uihtml update system work 100% of the time. We are looking for something that lets us “listen” to when a graphic update is happening, so that we can write the Data property only once at that exact time.

Since this post has become too long I think I will leave the solution for the next one. Cliffhanger, I know. But I like leaving some challenges open for you as homework. Try searching Matlabs documentation and figuring it out yourself first!

By the way, the next post is going to be the first one in which we start creating our component framework, so stay tuned. I’ll also be creating a Github repo to share everything that I do here. See you on the next one!

Categorized in: