Okay, fourth post. I think we have learnt enough theory about uihtml. It’s about time we make the jump and start creating our own, right? I sure think so, so this post will get more hand-on and less blabla-ish.

How will our components look like? Well, we know for sure that they need to be related to an uihtml element that can render an <iframe> inside the figure and load our components HTML code. Components also definitely need to be able to share data with the view, both sending and receiving (that was the topic of last post, if you haven’t read it yet!).

Since all of our components will be doing the same core functions, it kind of makes sense that we create an abstract Component class that our individual ones can inherit from. Let’s design that class today, and use it to derive our first fully-fledged component.

The Component class

Let’s design our abstract class step by step. The first thing we need to do is to give it a name, and since it is going to be parent class for all our components… I think Component suits it perfectly.

MATLAB
classdef (Abstract) Component
end

Great, step 0 completed! Now we have to think about what this class needs to do. In my mind, our Component class needs to do three essential things:

  1. Create the uihtml element
  2. Send our dirty properties to the view
  3. Receive data from the view

Sending and receiving data is going to be the hot topic. Last post we discussed the mechanism, and how there was a problem if we used the uihtml element alone that could make us lose information before a graphics update. I promised a solution though: something that could listen to graphic update events and act on them. The answer to our problems is called ComponentContainer, and it is a class that Matlab provides.

The base ComponentContainer class

The matlab.ui.componentcontainer.ComponentContainer is, in essence, a plain <div> element that we can put inside a figure. It’s also a container, which means that we can nest other components inside of it (buttons, axes, uihtml, etc). Unlike other containers, like uipanels, ComponentContainers are abstract and allow us to derive subclasses from them. Abstraction equals reusability, so this is a nice feature.

If you have never used a ComponentContainer, there is am awesome contribution in the FEX called the Widgets Toolbox that I highly recommend. It provides reusable components (or widgets) made out of other standard components. For example, it provides a ProgressBar class by wrapping a couple of uiimage and uilabel components.

But we are specially interested in ComponentContainers for one particular reason. They define an abstract update method which, according to the documentation:
This method executes during the next drawnow (graphics update) execution after the user changes one or more property values on the UI component.
You heard it well: Matlab calls the update function on every graphics update. That means that, when we define that method, we can effectively listen to graphic updates. This is exactly what we were looking for in our previous post! To use the update method, our abstract Component class will need to inherit from the ComponentContainer one.

MATLAB
classdef (Abstract) Component < matlab.ui.componentcontainer.ComponentContainer
end
Component inherits from ComponentContainer

The setup

The first thing that we’ll be implementing is what it must happen when we initialize our component. Obviously, we need to create a uihtml element, and it needs to load the components HTML file. Since every component will have an associated HTML file, we will add an abstract and constant property (we’ll call it HTMLSource) that every subclass will need to re-define.

MATLAB
classdef (Abstract) Component < matlab.ui.componentcontainer.ComponentContainer
    
    properties (Abstract, Constant)
        HTMLSource string % to be defined by subclasses
    end
end

Usually we would create the uihtml element in the constructor of our class. However, the ComponentContainer superclass is somehow special and provides a setup method to define the children of the container. The setup method will be executed during the call to the superclass constructor.

In the setup, we will therefore need to create the uihtml element and parent it to the Component class (we are placing the uihtml <iframe> inside the ComponentContainer <div>) and set the HTML source property to the one we defined earlier. Since the HTML document should fill the containers <div>, we need to set uihtml width and height to that of the container. We will also store a reference of the uihtml object because later we’ll need it to send and receive data.

MATLAB
classdef Component < matlab.ui.componentcontainer.ComponentContainer

    properties (Constant, Abstract)
        HTMLSource % to be defined by subclasses
    end
    
    properties (Access = private)
        HTMLComponent % reference to the uihtml
    end
    
    methods (Access = public)
        function this = Component(varargin)
            % Call superclass constructor
            this@matlab.ui.componentcontainer.ComponentContainer(varargin{:});
        end
    end
    
    methods (Access = protected)
        function setup(this)
            this.HTMLComponent = uihtml("Parent", this, ... nest in container
              "Position", [0, 0, this.Position(3:4)], ... fill area of container
              "HTMLSource", this.HTMLSource);
        end
    end
end

Sending data

Okay, we arrive to a very interesting part. We need to see how we use the update function to send the data to Javascript.

If you remember last post, the main problem we had was accidentally overwriting the uihtml Data property before a graphics update happened (and that occurred when the view sent data to Matlab). To avoid that, we can now keep the Data property empty, and only fill it up when the graphics update happens… voilà, we need to set the Data inside the update.

And what data do we send to the view? My preferred strategy (although you could think of something different) is to send only the properties that have changed. I like to call them dirty properties. What we will do is the following:

  • When we change a property in Matlab and we want to notify Javascript of the change, we will mark it as dirty. The Component will provide a protected method, markAsDirty, that does this.
  • We will store dirty properties in a struct. The struct is very convenient for our purpose because we can dynamically add fields: if a field does not exist, it is created; and if a field already exists, only the value is updated.
  • On the update, we just copy our dirty properties struct into the uihtml Data. That will send the dirty properties to the view. And of course, after sending the data we can simply reinitialize the dirty properties struct.
MATLAB
classdef Component < matlab.ui.componentcontainer.ComponentContainer

    properties (Constant, Abstract)
        HTMLSource % to be defined by subclasses
    end
    
    properties (Access = private)
        DirtyProperties = struct();
        HTMLComponent % reference to the uihtml
    end
    
    methods (Access = protected)
        function markAsDirty(this, propertyName)
            this.DirtyProperties.(propertyName) = this.(propertyName);
        end
        function setup(this)
            this.HTMLComponent = uihtml("Parent", this, ... nest in container
              "Position", [0, 0, this.Position(3:4)], ... fill area of container
              "HTMLSource", this.HTMLSource);
        end
        function update(this)
            this.HTMLComponent.Data = this.DirtyProperties; % send to JS
            this.DirtyProperties = struct(); % reinitialize
        end
    end
end

Receiving data

Last thing we need to do is to handle the data transferred from the JS view into Matlab. Here we also need a strategy, because theoretically the view can send anything. My preferred strategy is to use event subscriptions. They work in this way:

  • When the view sends some data, it sends it as a JS object with two properties: the identifier (name) and the data (remember that JS objects become structs when passed through the jsondecode function)
  • In our Matlab controller, the Components would provide a subscribe method. This method accepts an event name and an associated function handle. An internal struct (I call it Subscriptions) stores the map between events and callbacks.
  • When the view sends a given event (with a particular name), the Data property of the uihtml will be modified. We can listen to those changes by specifying a function in the DataChanged property of the uihtml. When executed, that method will check if it the component is subscribed to the event name. If it is, it will execute the callback with two inputs: the component itself and the data that came with the event.
MATLAB
classdef Component < matlab.ui.componentcontainer.ComponentContainer

    properties (Constant, Abstract)
        HTMLSource % to be defined by subclasses
    end
    
    properties (Access = private)
        DirtyProperties = struct();
        Subscriptions = struct();
        HTMLComponent % reference to the uihtml
    end
    
    methods (Access = protected)
        function subscribe(this, eventName, callback)
            this.Subscriptions.(eventName) = callback;
        end
        function markAsDirty(this, propertyName)
            this.DirtyProperties.(propertyName) = this.(propertyName);
        end
        function setup(this)
            this.HTMLComponent = uihtml("Parent", this, ... nest in container
              "Position", [0, 0, this.Position(3:4)], ... fill area of container
              "HTMLSource", this.HTMLSource, ...
              "DataChangedFcn", @(~,evt) this.handleJSEvent(evt.Data));
        end
        function update(this)
            this.HTMLComponent.Data = this.DirtyProperties; % send to JS
            this.DirtyProperties = struct(); % reinitialize
        end
    end
    methods (Access = private)
        function handleJSEvent(this, evt)
            if isfield(thisSubscriptions, evt.Name)
                % Evaluate the callback
                feval(this.Subscriptions.(evt.name), this, evt.Data);
            end
        end
    end
end

And there it is, our abstract Component class. Let’s use it to create our first true component!

A button component

I know, I know. Buttons again no, please. And I agree. But they are the simplest thing we can do, and this post is already becoming long. I promise you that in the next posts we will start doing fancy components. But today, let’s stick with a basic button. And we are actually going to make it super simple to demonstrate how to use our Component class.

MATLAB
classdef Button < Component
    properties (Constant)
        HTMLSource = "buttonComponent.html";
    end
end

What do we need the button to do? We just need it to do two things: control its style and react to a click event. Styling the button will be sending data from Matlab, while reacting to events will imply receiving the data from JS.

Styling the button

In Matlab, our Button class will have a publicStyle property, which will be a struct. When we change it, it will be marked as dirty and sent to the view on the next graphic update.

MATLAB
classdef Button < Component

    properties (Constant)
        HTMLSource = "buttonComponent.html";
    end
    
    properties (Access = public)
        Style
    end
    
    methods 
        function set.Style(this, newStyle)
            this.Style = newStyle;
            this.markAsDirty("Style");
        end
    end
end

The Style must contain valid CSS attributes (in camelCase), because when the view receives the style, it will be copied into the style property of the <button> element. We can copy directly the style though (the style property does not admit plain objects), we need to copy the style properties using something like the Object.assign JS method.

HTML (buttonComponent.html)
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        function setup(uihtml) {
            let button = document.getElementById("myButton");
            
            uihtml.addEventListener("DataChanged", function () {
                if ("Style" in uihtml.Data) {
                    Object.assign(button.style,uihtml.Data.Style);
                }
            });
        }
    </script>
</head>

<body>
    <button id="myButton">Click me</button>  
</body>
</html>

Reacting to events

When the view detects that the button has been clicked or the mouse has entered, it will need to notify the controller by writing the Data property. Remember that, due to our strategy, the event that is sent to Matlab must contain 2 fields: the name and the data. For the names, I think “click” and “mouseEnter” sound reasonable. For a “click” event, no data needs to be sent (we’ll leave it empty); but for the “mouseEnter”, I want to send the coordinates of the mouse when it enters the button.

HTML (buttonComponent.html)
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        function setup(uihtml) {
            let button = document.getElementById("myButton");
            
            uihtml.addEventListener("DataChanged", function () {
                if ("Style" in uihtml.Data) {
                    Object.assign(button.style,uihtml.Data.Style);
                }
            });
            
            button.onclick = function() {
                uihtml.Data = {name: "click", data: {}};
            };
            button.onmouseenter = function(evt) {
                uihtml.Data = {name: "mouseEnter", data: {x: evt.clientX, y:evt.clientY}};
            };
        }
    </script>
</head>

<body>
    <button id="myButton">Click me</button>  
</body>
</html>

In Matlab, we only need to subscribe to the “click” and “mouseEnter” events with whatever callback we want. It makes sense if we define 2 public properties, ClickFcn and MouseEnterFcn, that the user can override (just like a standard button) and that are executed when the corresponding event is received.

MATLAB
classdef Button < Component

    properties (Constant)
        HTMLSource = "buttonComponent.html";
    end
    
    properties (Access = public)
        Style
        ClickFcn = @(~, ~) doNothing();
        MouseEnterFcn = @(~, ~) doNothing();
    end
    
    methods 
        function this = Button(varargin)
            this@Component(varargin{:});
            this.subscribe("click", @(~,data) feval(this.ClickFcn, this, data);
            this.subscribe("mouseEnter", @(~,data) feval(this.MouseEnterFcn, this, data);
        end
        function set.Style(this, newStyle)
            this.Style = newStyle;
            this.markAsDirty("Style");
        end
    end
end

function doNothing()
end

Use the class

Try creating your first button now! In the command window, instantiate a uifigure and a Button, and play with it. For example, we can make it a red circular button that, when clicked, displays something on the command windowL

MATLAB (command window)
f = uifigure();
b = Button("Parent", f);
b.Style.position = "absolute";
b.Style.height = "100%";
b.Style.width = "100%";
b.Style.backgroundColor = "red";
b.Style.borderRadius = "50%";
b.ClickFcn = @(~,~) disp("I've been clicked!");

Exercise: I have a difficult exercise for you. Can you make it so that the button changes its background color only when the mouse is over it? When the mouse is no longer over it, it should go back to its original color. Hint: you’ll need to extend the HTML to also notify of “mouseleave” events, and to provide a MouseLeaveFcn property. You will also need to add drawnow in your callbacks so that the style update is forced!

Conclusion

Our Component class is now fully functional, and we can start deriving real components out of it, just like our button. And it doesn’t suffer from the issue that we mentioned during the last post, which is actually amazing. So… is this it? Can we start creating more components, each one of them more advanced? Yes, but I wont.

There is something that annoys me a bit about the Component class, and it is that a Component needs to be associated with an HTML file. At first glance it looks fine, but when I think about the component framework I think of bigger components that reuse other ones. For example, I want to design a search bar component, and it will have a button. I want to reuse our Button class inside the search bar, and be able to style the Button directly from Matlab. Maybe the SearchBar component could have a children property that is a Button? This wouldn’t work right now, because creating a Button would add another <iframe> to the figure.

What do we do? We need to change our Component framework, and we will do it in the next post. After that, we will move on to designing real, fully fledged components.

One last note: You can find the Component and the Button classes in the following Github repo. You’ll notice that the classes are defined inside +weblab/+internal/+deprecated folders, so to call the button you’ll need to call it as:

b = weblab.internal.deprecated.Button(uifigure);

The classes are more documented, so be sure to check them out. They are wrapped inside the +deprecated package because, as I mentioned before, I don’t plan Component to be our reference class in the future.

Categorized in: