APEX spinner download indicator using Vanilla JavaScript (async/await)

Search for a command to run...

No comments yet. Be the first to comment.
Building on the previous topic of leveraging Liquibase properties to modify APEX installations, we will delve into a companion technique. This one enables users to define as many property files as they wish in the artifact to set it when deploying. T...

Prereqs Latest SQLcl Existing SQLcl Project with an APEX app Note On average, I prefer to keep the same Schema name, APEX Application ID, Workspace Name, and Workspace ID across environments and have separate databases. I understand that this isn...

Situation Recently I was given a seed data file that was generated from SQLcl with a bunch of INSERT statements, and it worked fine in SQL Developer when ran but would fail in SQLcl with the following: SQL> @DEMO_DATA_TABLE.sql Error starting at lin...

Quick Tip for handling the merging of JSON in Oracle Database!

There are plenty of good resources explaining how to create custom download links in APEX so I will skip over that part and list a couple here:
- https://oracle-base.com/articles/misc/apex-tips-file-download-from-a-button-or-link
- https://joelkallman.blogspot.com/2014/03/yet-another-post-how-to-link-to.html
These examples go into good detail on how to generate a custom link to download a file. But what about how to indicate that a file is downloading if it is taking a long time in the backend?
Some custom download processes can take quite sometime depending on what the process is doing in the backend (generation/business logic). It can be quite confusing to the end user that a process is occurring and for them to even know that they are waiting on downloading a file at all if this is the case.
If it takes a long time to run the process that ultimately downloads the file - then just setting a spinner on page submit or via apex.server.process will not necessarily achieve the desired outcome. Usually the apex engine is killed when you dedicate a page/process to download the file, and subsequently you can not easily signal that the download process is done and to take action upon that to kill a spinner if you had one going.
There are a few plugins out there that you can use in combination with each other as well. Lets just assume that you have a link already set up to download your file and that it kicks off some custom process to do so. Here is a quick Vanilla JavaScript approach to achieve a spinner while the download process is occurring and to remove it when it is done.

Page Properties -> Function and Global Variable Declaration
const download = async (url, filename) => {
// add spinner
var lSpinner$ = apex.util.showSpinner();
const data = await fetch(url);
const blob = await data.blob();
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
// set link download url/filename
link.setAttribute("href", objectUrl);
link.setAttribute("download", filename);
link.style.display = "none";
// add link and download file
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(blob);
link.remove();
// remove spinner
lSpinner$.remove();
};
This is utilizing async and await to make us wait for the promise to return a result. Majority of the waiting done is on the fetch here in my case as my process running takes over 5 seconds before it even sends a file back to the client. This allows us to confidently set a spinner before we wait for the result, and then remove the spinner after all of our code is done running.
2. Declare a Dynamic Action on button click to download the file

Dynamic Action -> Code
My download link runs a process that takes over 5 seconds to generate my file. I am using page items to hold the url and filename, so I used this:
download(apex.items.P1_DOWNLOAD_URL.value, apex.items.P1_FILENAME.value)

That is it. This concept can be used also to fire other events after a custom file generation process is done as well and not just to get a spinner. Hope this helps any of you that have long running download processes so that the end user is aware that something is occurring. Cheers!