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!
Aside from being a SPA framework, there are many differences. The ones affecting the way the plugin is written are:
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.
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 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.
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.
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.
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.
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!
We are beyond proud to announce we have become B Corp certified and joined the growing list of Australian companies who are demonstrating their commitment to sustainability. We consider B Corp certification to be a guide for us to validate that we are doing the right thing and that symbol of trust that our stakeholders should expect.
With so many options, how can you ensure you're making an informed decision and truly comparing apples to apples? Our selection criteria checklist is here to guide you. By asking the right questions and focusing on what truly matters, you can streamline the process and set your project up for success from the start.
One of the biggest fears technology buyers face is overpaying for a solution from a software development company. It’s a valid concern—nobody wants to invest significant budget only to feel they didn’t get what they paid for.
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.