Refresh Oracle APEX Template Components which contain JavaScript code

With around 20 years on the job, Matt is one of the most experienced software developers at Pretius. He likes meeting new people, traveling to conferences, and working on different projects.
He’s also a big sports fan (regularly watches Leeds United, Formula 1, and boxing), and not just as a spectator – he often starts his days on a mountain bike, to tune his mind.
Welcome Oracle APEX Template Component developers... this blog post is going to get complicated real quickly.
Scenario
The Scenario is: I have a a Template Component with this Partial
{if MY_VAR/}<div id='luf'>#MY_VAR#</div>{endif/}
This Template Component has a loaded JavaScript file
My seconds.js looks like this
var tc_selector = '.luf';
function initTemplateComponent() {
var secs = apex.date.secondsPastMidnight();
\((tc_selector).append(\)("<div>").text('JS: ' + secs));
}
initTemplateComponent();
Basically it grabs the number of seconds since midnight.
The region has a Static ID of ellandRoad
When it runs, I get:
Some numbers from the DB
Seconds since midnight from JavaScript
It looks like this:
The Update button you're seeing just refreshes the Template Component Region
When I click the Update button I see this this:
Whats happened is that the region has been refreshed, but the JavaScript does not run.
Solution
Rewrite the Template Component JavaScript
The solution is to rewrite the seconds.js to look like this
var tc_selector = '.luf';
function getComponentElement(parentSelector) {
var \(parent = parentSelector ? \)(parentSelector) : null;
return \(parent ? \)parent.find(tc_selector) : $(tc_selector);
}
apex.jQuery(window).on('theme42ready', function() {
$(tc_selector).each(function (index) {
var region = apex.region.findClosest(this);
if (region) {
$(region.element[0]).on("apexafterrefresh", function (event) {
initTemplateComponent( event.target )
});
}
});
});
function initTemplateComponent(parentSelector) {
var secs = apex.date.secondsPastMidnight();
var $sel = getComponentElement(parentSelector);
\(sel.append(\)("<div>").text('JS: ' + secs));
}
initTemplateComponent();
Points
I put a bit of thought into refreshing individual regions, i.e not all regions may wish to be blanket updated.
I tried to keep
initTemplateComponentintact and resemble the original. Although now it has a parameter & callsgetComponentElement. This is just a small rewrite to fetch the region affected by the refreshOn UT ready, a new event is added waiting for the a refresh event. This also calls
initTemplateComponentso be aware that this line may need changing to whatever your function called
Next section, we have to look after the refresh
Method 1: Execute JavaScript Refresh
For this method need to change the Refresh Dynamic Action into a Execute JavaScript one
const regionID = 'ellandRoad';
apex.region(regionID).refresh().done(function () {
apex.event.trigger('#' + regionID,'apexafterrefresh','');
})
Points:
In the above code, we are going to refresh the region, then wait till it refreshes.
After it refreshes and renders, we can trigger the event which runs the JavaScript function again.
Method 2: Monkey-Patch apex.region
Put this in Execute when Page Loads or similar place
(function (apex, $) {
"use strict";
var origRegion = apex.region; // original function with methods attached
function patchRegionInstance(region) {
if (!region || region._refreshPatched) {
return;
}
var origRefresh = region.refresh;
if (typeof origRefresh !== "function") {
return;
}
region.refresh = function () {
var self = this;
var result = origRefresh.apply(self, arguments);
if (result && typeof result.then === "function") {
// Native Promise
return result.then(function () {
$(self.element).trigger("apexafterrefresh");
});
} else if (result && typeof result.always === "function") {
// jQuery Deferred
return result.always(function () {
$(self.element).trigger("apexafterrefresh");
});
} else {
// No promise: fire immediately
$(self.element).trigger("apexafterrefresh");
return result;
}
};
region._refreshPatched = true;
}
// Wrapper that delegates to the original region function
function wrappedRegion(pRegionId) {
var region = origRegion(pRegionId);
patchRegionInstance(region);
return region;
}
// Copy all static members from original apex.region to the wrapper
Object.keys(origRegion).forEach(function (key) {
wrappedRegion[key] = origRegion[key];
});
// Replace apex.region with the wrapped version, preserving namespace
apex.region = wrappedRegion;
// Optionally patch already-created regions (if any)
if (apex.regions) {
Object.keys(apex.regions).forEach(function (id) {
patchRegionInstance(apex.regions[id]);
});
}
})(apex, apex.jQuery);
Now, just just use the Refresh Dynamic Action like normal.
Summary
Now the Update button is working
Yeah, apologies the solution is a bit ugly. I couldn't find a nicer solution here. I hope your AI can turn this blog into a suitable solution for your Template Component
ENJOY!
What's the picture? Mural of Luke Ayling's strike in the East Stand at Leeds United.






