Background information

In this tutorial we will show how the Modelit Embedded Webserver for Matlab can be used to build a Single-Page Application (SPA). The goal is to build a simple web app which can later be adapted to be used with your own Matlab code and front-end interfaces.

A SPA typically consists of two elements:

  • A front-end.
    In this example the front-end is written in plain HTML, CSS and JavaScript.
    The front-end of a SPA is loaded once. And JavaScript is used to make the web app interactive. It is used to process user interaction, to send and retrieve data to and from the back-end and to update the user interface.
  • A back-end.
    Written in Matlab by using the Modelit Embedded Webserver for Matlab.
    In this example the back-end runs in Matlab. Enabling debugging and experimenting, but it can also be compiled to run as a standalone application or as a Docker container.

At the end of this tutorial you will have a web app as embedded below: 

Typically a front-end is served from a static file, for example "index.html" and user actions cause Back-end functions to be invoked as shown in the figure below. In the present tutorial a single URL will serve both the front-end and back-end of the server. The initial call to the URL will return the front-end while subsequent calls will return JSON messages as needed by this front-end.  To accomplish this we will first write a file "index.html" with the code for the front end, and then create a file "TemperatureConversionServer.m"  that serves this file and responds to JSON input.

In the present setup front-end and back-end will be served from a single URL. In this way the calls to the back-end will not suffer from CORS (see solve-cors-issues) restrictions and the entire app will be hosted on the same location.

Schema of the Temperature Conversion SPA

Steps

The next steps create the Temperature Conversion SPA:

Prerequisites:

This tutorial assumes a working version of Matlab is installed on your computer.

Step 1: Prepare project folder

  1. Select a name for the folder you will be working from
  2. Make this the Matlab startup folder of your next Matlab session
  3. Download the zip file with the free version of the Modelit Embedded Webserver for Matlab
  4. Extract this zip file to the project folder
  5. Launch Matlab

Step 2: Specify the HTML code for the front-end

First we will implement the front-end. This is an HTML page with two input fields. One for the temperature in degrees Celcius and one for the temperature in degrees Fahrenheit. Some simple inline CSS is used for component styling and the layout is done with the flexbox layout (see css flexbox). Note that any front-end framework such as React, Vue or Angular can be used in combination with the Embedded Server toolbox.

In the script section of the .html file some JavaScript code is defined to make the web app interactive and to communicate with the Matlab back-end. The important methods are:

  • getElementById to retrieve the two input controls by using their ID attribute
  • addlistener method of the input elements to react to changes in the input controls
  • XMLHttpRequest to communicate with the Matlab back-end
  • JSON.stringify and JSON.parse to convert JavaScript objects to JSON and vice versa. These are the Matlab jsonencode and jsondecode equivalents 
  • the onreadystatechange property is used to process the response asynchronously, the callback assigned to this property is triggered when the response is received from the server.

When the input is changed, i.e. the control has lost focus. Then the corresponding callback is called. In this callback the value of the input control is read and depending on the control (Celcius or Fahrenheit) a JSON message is sent to the back-end. This message is sent as a HTTP POST. POST has advantages that the message is not visible in the url and that there are no restrictions on the length. See get vs post.

Copy the code below in a file "index.html" in the project folder. You can already preview the result by opening the index.html file in a browser. However it will not respond to user actions because we did not yet the back-end.

<!DOCTYPE html>
<html>
  <body>
    <h1>Celcius &#x2194; Fahrenheit</h1>

    <div style="display: flex">
      <div>
        <label style="display: block" for="celcius">Degrees Celcius:</label>
        <input
          style="width: 240px"
          placeholder="Temperature in Celcius"
          id="celcius"
          name="celcius"
        />
      </div>
      <div style="align-self: center; padding: 0px 20px 0px 20px">=</div>
      <div>
        <label style="display: block" for="fahrenheit"
          >Degrees Fahrenheit:</label
        >
        <input
          style="width: 240px"
          placeholder="Temperature in Fahrenheit"
          id="fahrenheit"
          name="fahrenheit"
        />
      </div>
    </div>

    <script>
      const celcius = document.getElementById("celcius");
      const fahrenheit = document.getElementById("fahrenheit");

      function callback(result) {
        celcius.value = result.celcius;
        fahrenheit.value = result.fahrenheit;
      }

      celcius.addEventListener("change", (e) => {
        const xhr = new XMLHttpRequest();

        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4) {
            callback(JSON.parse(xhr.response));
          }
        };

        // open the request with the verb and the url
        xhr.open("POST", window.location.href);
        // send the request
        xhr.send(JSON.stringify({ celcius: Number(e.target.value) }));
      });

      fahrenheit.addEventListener("change", (e) => {
        const xhr = new XMLHttpRequest();

        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4) {
            callback(JSON.parse(xhr.response));
          }
        };

        // open the request with the verb and the url
        xhr.open("POST", window.location.href);
        // send the request
        xhr.send(JSON.stringify({ fahrenheit: Number(e.target.value) }));
      });
    </script>
  </body>
</html>

 

Step 3: Create TemperatureConversionServer.m

Open the Matlab editor and copy the code below into the editor.

function server = TemperatureConversionServer
% TemperatureConversionServer- The server listens to port 8082 on localhost for Get and Post requests. % When receiving an HTTP get request the front-end HTML file is returned. When receiving an HTTP Post request the
% TemperatureConversionServerCallback uses the JSON message in the request's body to calculate the conversion and returns a JSON
% message with the calculated temperatures in Celcius and Fahrenheit. % % CALL: %   TemperatureConversionServer % % INPUT: % no input required % % OUTPUT: % server: <modelit.web.server.Server> % A server listening to the port 8082 for HTTP GET and HTTP POST requests.
port = 8082;
% Create the server and the callback.
server = modelit.web.server.Server(port, @TemperatureConversionCallback); % Start listening server.start;

%___________________________________________________________
function TemperatureConversionCallback(event)
% TemperatureConversionCallback - Callback called by the modelit.webserver.Server object for converting
% Celcius to Fahrenheit and
vice versa.
%
% CALL:
% TemperatureConversionCallback(event)
%
% INPUT:
% event: <modelit.webserver.HttpExchange>
% with the request data and methods to generate a response.
%
% OUTPUT:
% No output, a response with a JSON object is return to the client.
%

% Copyright 2023 Modelit, www.modelit.nl

switch lower(event.getRequestMethod)
case 'get'
   % Serve the HTML file with the front-end HTML, CSS and JavaScript code.
fname = fullfile(fileparts(mfilename('fullpath')), 'index.html');
if exist(fname, 'file')
response = readBytesFromFile(fname);
event.send(200, response); % Send the file with status 200: OK
return
else
event.send(404); % Status 404: Not found
return
end
case 'post'
% Received a POST message from the front-end
response = convert(event);
% Set the MIME type and allow Cross Origin Resource Sharing
event.addResponseHeader('Content-Type', 'application/json',...
'Access-Control-Allow-Origin', '*');
% Send the response back to the client with status 200 OK.
event.send(200, response);
return;
otherwise
event.send(405); % Send status 405: Method not allowed .
return;
end

%__________________________________________________________________________
function text = convert(event)
% convert- Do the conversion.
%
% CALL:
% text = convert(event)
%
% INPUT:
% event: <modelit.web.server.HttpExchange>
%
% OUTPUT:
% text: <string>
% JSON string with data for the front-end.

% Make a simple JSON object with Matlab function jsondecode.
input = jsondecode(char(event.getRequestBody)');

% Initialize the output (Matlab) structure.
S = struct('celcius',NaN,'fahrenheit',NaN);
try
if isfield(input, 'celcius')
% Celcius given, calculate degrees Fahrenheit.
S = struct('celcius', input.celcius, 'fahrenheit', input.celcius*1.8 + 32);
elseif isfield(input, 'fahrenheit')
% Fahrenheit given, calculate degrees Celcius.
S = struct('celcius',(input.fahrenheit - 32)/1.8,'fahrenheit', input.fahrenheit);
end
catch me
 % Use the default values. The front-end will take care of error handling by not displaying any values.
end

text = jsonencode(S);

Save TemperatureConversionServer.m to disk. Running this function creates a webservice that listens to port 8082 and will invoke TemperatureConversionCallback for each request. Depending on the value of requestmethod the callback will do the following:

  • When requestmethod "GET" (the URL is invoked from a browser), the callback will simply read index.html and return its content. 
  • When requestmethod is "POST"  (the URL is called in response a user action), the callback will process the JSON input., using the version "convert".

A note on the function "convert"

When we know the format of the messages that are sent and received by the front-end we can implement the back-end. The back-end processes the conversion requests from the webpage. These requests are sent to the server when an change event is triggered in one of the two input controls. The requests are JSON messages of the form:

{ celcius: <number> }

when the Input with the temperature in degrees Celcius was changed, or

{ fahrenheit: <number> }

when the Input with the temperature in degrees Fahrenheit was changed.

These JSON messages are received by the server and sent to the TemperatureConversionCallback. In this callback the data can be retrieved from the event object that has the method getRequestBody. This method returns the binary data of the HTTP POST message. Remember that the body consists of binary data and has to be converted to characters, so we can use jsondecode. N.B. JSON is the preferred communication format for structured data between Matlab and the front-end.

{ celcius: <number> 
  fahrenheit: <number> 
}

This Matlab structure is converted to a JSON message by using the Matlab jsondecode function. Depending on the fields the complete conversion is done. The resulting answer is a Matlab struct. After this the result is converted to JSON and send back to the client, by using a HTTP statuscode see statuscodes

Step 4: Start the server

From within Matlab run start the server by executing the command: TemperatureConversionServer in the console

>> server = TemperatureConversionServer
server = Server with properties: running: 1 port: 8082

Note that the server will run until we stop it with from the command line, or close the Matlab session:

>> server.stop

Invoking TemperatureConversionServer before the previous server on port 8082 is stopped will result in an error.

Step 5: Open the web page

Open a browser and type localhost:8082 in the address bar. Now the web app appears and you can convert the temperature from degrees Celcius to Fahrenheit and vice versa.