Kiandra Insights

Deep Dive Into Building OutSystems React Plugins

Stuart Harris Kiandra Software Development Team Lead and OutSystems Champion
by
Stuart Harris
Software Development Team Lead
|
January 28, 2021
Stuart Harris
Software Development Team Lead
January 28, 2021
Illustration of a power plug connecting into a socket

Welcome to Part III of our blog series on OutSystems development and how to create plugins. You can read Part I here and part II here.

Over the course of this series you will have seen the various options that OutSystems provides to build plugins. There are a heap of benefits to the using Reactive Web plugin, not least speed and performance and a better UX and in this instalment I’ll walk you through the process of converting a WebForms OutSystems forge plugin to a Reactive Web plugin. Let's get started!

What is different in React?

Aside from being a SPA framework, there are many differences. The ones affecting the way the plugin is written are:

  • Pages and blocks do not have a preparation running synchronously when the page loads, they have events which run asynchronously.
  • Client actions are available which run in the browser, giving more compile time checking to JavaScript code.
  • Client actions may contain JavaScript blocks which can also execute JavaScript from other components, such as another library.
  • Client actions may be passed to JavaScript functions and can then be called as a callback, passing another JavaScript function as another callback using the Object data type.
  • GetSessionId() does not return the session ID. This meant I could not restrict uploads to a single session for a user.
  • In client-side JavaScript blocks, when assigning a string to a Binary Data variable, the string is assumed to be in base64 format, and is converted to binary data. Very helpful!

What does the plugin do?

The ReactFilePondUpload plugin essentially it integrates the awesome FilePond javascript library written by Rik Schennink. It uploads files asynchronously. So multiple files will upload at the same time. There are several options including image preview and editing.

It works by uploading a file to temporary database storage and returns a unique token identifying the file. Then the consumer component passes the tokens to a server action which retrieves the uploaded files by specifying the tokens.There are two web blocks. The UploadBlock requires the user to click submit to trigger file processing. The AutoUploadBlock has a callback that fires when all files are uploaded, so the user does not have to do anything more than dropping the files on the widget.

RFPU_UserInteraction-Stuart-Harris-(1).png


The WebForms Plugin

First, let’s look at the FilePondUpload plugin. In this plugin, the tokens are shared with the consumer via a hidden input box. When the user submits the form, the value of the input box is also submitted. So the consumer can then use the variable associated with the input box to retrieve uploaded files.

As you might expect, the JavaScript library requires a server-side endpoint to accept the uploaded files. The WebForms plugin was able to use a web page as the target of the standard HTTP upload method via a slightly misused Preparation.

The React Plugin

The React Plugin has the same web blocks and configuration, but no placeholder for an input control to retrieve the tokens. As we have client actions, we can interact with the FilePond JavasSript library by calling functions directly, or at least via the FilePondUploadJS integration script.

Rather than tell what would undoubtedly be a gripping tale of the curious things I tried, how I failed many times, only to triumph in the end; maybe I should spare you with just the short version. So this is how it ended up.

Initialise the plugin

The configuration is a FilePondUploadConfig structure supplied as a parameter to the UploadBlock web block. To pass the structure to the integration script, convert it to a JSON string with the JSONSerialize widget. Pass the output of JSONSerialize into a JavaScript block as Text. Then convert it to a JavaScript Object with JSON.parse(). This might seem a bit weird, but if we just passed the object directly we would be working with the internals of an OutSystems structure object. I avoided relying on internals to avoid breaking changes.

Var configObject = JSON.parse($parameters.UploadConfigJson);
var containerId = $parameters.UploadContainerId;
var isAuto = $parameters.IsAutoUpload;
var uploadCallback = $actions.UploadFile;
var uploadedCallback = $actions.NotifyUploaded;
var rejectCallback = $actions.RejectFile;
FilePondUpload.configure(containerId, configObject, isAuto, uploadCallback, uploadedCallback, rejectCallback);

The FilePondUpload JavaScript object is the integration layer between OutSystems widgets and the FilePond javascript library. The configure function initialises the FilePond control and sets up all the events and callbacks.

Upload files

When a file is ready for upload, the FilePond library uses the process configuration option. This can be a string specifying an endpoint that can accept uploaded files. However, this does not work for React Web Apps, as pages do not have Preparations, and APIs do not return simple text responses. Thanks to the flexibility offered by FilePond, for React I was able to use a custom javascript function to perform the upload.

The process function has to read the file using a standard FileReader object. The file is retrieved in base64 format which is passed to an OutSystems client action. I have removed error handling in the below code to simply it.

var fileReader = new FileReader();
// Set up callback when the file is loaded
fileReader.onload = function(frEvt) {
 var base64 = fileReader.result;
 // Remove the base64 prefix
 base64 = base64.replace(/^data:[^;]+;base64,/,'');

 var successCallback = function(token) {
   load(token);
 };

 // Callback to actually upload the file
 sendFileCallback(file.name, file.type, base64, successCallback, errorCallback);
};
// Now read the file!
fileReader.readAsDataURL(file);

Once we have the file, the UploadFile client action is called. We can call this because a reference to this function was passed into the FilePondUpload configure function.

UploadFile calls SendFile which is a server action. The SendFile action accepts a BinaryData object. Fortunately, assigning a base64 encoded string to a BinaryData object converts the string into binary.  Side note – a big thanks to Miguel Vincente’s forge plugin to show me the way!

SendFile stores the uploaded file and generates a unique token. Hooray, we’re half-way there!

We now just have to get the token back to the FilePond library. The call to SendFile is synchronous, so UploadFile has been waiting for a response. SendFile returns the token. UploadFile was given a success callback, which accepts a token and will in turn call load(token) telling FilePond that the file has been safely uploaded.

Unfortunately, as the call to SendFile is synchronous, we have no way of reporting progress as the file is uploaded. So the control will just show that the file is uploading, but it will not show what percentage has been uploaded.

You can see a full summary of this upload sequence in the below image.

RFPU_Upload_Sequence-Diagram-Stuart-Harris-(1).png


How to retrieve the file

A significant benefit Reactive Web Apps gives us is the ability to integrate directly with JavaScript code without having to resort to indirect practices such as using JSON in hidden input controls as an interface.

Files are retrieved by calling FilePondUpload.getTokens(), which is nicely wrapped behind a client action called FilePondUpload_GetTokens(). The consumer then calls a server function to perform its processing and calls the FilePondUpload_GetUploadedFiles() server action to retrieve the files.

Reset the widget

The final step is to reset the widget. The consumer must call the FilePondUpload_Reset() client action which calls the FilePondUpload.reset() function to clear the uploaded files from the widget. Congratulations - we're all done!

React makes it easier! As you can seethere were a few hoops to jump through, but you can see the interface to the plugin is much simpler for the developer using the plugin. Being able to work within OutSystems in client side code offers a cleaner and safer way to integrate with javascript controls. Hopefully, as React Web Apps mature in the OutSystems platform, there will also be a way to monitor progress of a call to the server.

I hope you have found this helpful in building your own plugins or at least understanding a bit more about React Web Apps. Happy coding!

Share article
LinkedIn.com

More insights

An abstract illustration of a young woman testing software on her laptop

Performance testing is a commitment to excellence

Farzin Karthik
20/11/2023

At Kiandra, we recognise and acknowledge the pivotal role of performance testing in achieving this fine balance. In this blog, we will unravel what performance testing truly means at Kiandra and why it's a cornerstone of our development philosophy.

Read more
OutSystems Premier Partner badge sits on a dark blue background

Kiandra becomes first Premier OutSystems partner in the ANZ region

Meghan Lodwick
10/11/2023

Kiandra are proud to announce that it has attained the status of Premier OutSystems Partner – the most important partnership status from the world’s leading enterprise low-code platform.

Read more
Wreath surrounds the text "OutSystems Partner of the Year 2022"

OutSystems Top Partner ANZ for 2022

Meghan Lodwick
18/9/2023

Kiandra has received the OutSystems Partner of the Year Award for the entire Australia New Zealand region. The custom software solutions provider was recognised at the ‘Top Partner of Australia and New Zealand’.

Read more

Let’s discuss your next project

Whether you’re curious about custom software or have a specific problem to solve – we’re here to answer your questions. Fill in the following form, and we’ll be in touch soon.

Email

Would you like to receive an occasional email showcasing the latest insights, articles and news from our team of software experts?

Thanks for reaching out! One of our software experts will be in
touch soon to help you with your enquiry
Oops! Something went wrong while submitting the form.

This website uses cookies to improve your experience. By browsing our website you consent to the use of cookies as detailed in our Privacy Policy