For the longest time, as APEX developers, we have had to utilize workarounds to refresh Dynamic Content regions in APEX dynamically. I even wrote a blog post with an approach I use to solve this problem Solving the APEX PL/SQL Dynamic Content Region Partial Page Refresh Problem. APEX 22.2 brought a new Dynamic Content Region that supports CLOBs and dynamic refreshing out of the box. This post will demonstrate how the new APEX Dynamic Content Region works.
Why Use Dynamic Content Regions
Before starting, let's look at why you would want to use an APEX Dynamic Content Region in the first place. I typically only use them when standard APEX components and templates (e.g., Card Regions, Comments, Content Row, Value Attribute Pairs, etc.) do not fit my requirements. Even then, I would suggest using CSS to modify standard APEX templates before using a Dynamic Content Region.
Dynamic Content Regions are helpful when you need to build HTML content dynamically using PL/SQL. For example, you may need to show data in a non-tabular layout, or you may need to show HTML that has been generated from multiple sources (DB table, REST Services, etc.).
In my demo, we have provided users with a Rich text editor field where they can enter notes. These notes are appended to a CLOB column in a table, and the Dynamic Content Region is refreshed to show the CLOB with all of the notes.
How Dynamic Content Regions Work
An APEX Dynamic Content Region emits whatever HTML you return in the Dynamic Content Region
PL/SQL Function Body returning a CLOB attribute. The simplest possible example would look like this:
The output for the above example looks like this:
I made my demo a little more complicated to illustrate all of the features of Dynamic Content Regions.
Let's look at the code and the APEX page behind this:
Enter Notes Region
Enter a New Note field is a simple Rich Text Editor Item. There is a button
+ Add Note which runs a Dynamic Action to:
1. Append the New Note to notes history CLOB in a table
Enter a New Notefield
Refresh the Note History Region so that we see the new notes. ⭐ This is one of the aspects of the new Dynamic Content Region, which differs from APEX versions before 22.2. Being able to refresh a Dynamic Content Region from a Dynamic Action gives us the ability to generate dynamic content without having to submit the page
The complete code for the Server Side code can be seen below. In real life, this code would be inside a PL/SQL package.
DECLARE l_notes cndemo_table.notes%TYPE; l_new_note CLOB; l_clob_len NUMBER; GC_ACTIVITY_TPL CONSTANT VARCHAR2(32000) := q'[ <ul class="t-ContentRow t-ContentRow--styleCompact t-ContentRow--hideDescription activity-header t-Report--hideNoPagination u-color-21 margin-bottom-md" id="1" data-region-id="RX_1"> <li class="t-ContentRow-item "> <div class="t-ContentRow-wrap"> <div class="t-ContentRow-body"> <div class="t-ContentRow-content"> <h3 class="t-ContentRow-title">Entry Date: #ACTIVITY_NAME#</h3> </div> </div> </div> </li> </ul>]'; l_tpl VARCHAR2(32000); BEGIN -- Replace the token #ACTIVITY_NAME# with the current date and time. l_tpl := REPLACE(GC_ACTIVITY_TPL, '#ACTIVITY_NAME#', TO_CHAR(CURRENT_TIMESTAMP,('DD-MON-YYYY HH:MI:SS pm'))); -- Create a CLOB for the New Note. dbms_lob.createtemporary(lob_loc => l_new_note, cache => true, dur => dbms_lob.call); l_new_note := :P11_TODAYS_NOTES; -- Get the existing note history from a CLOB in a table. SELECT notes INTO l_notes FROM cndemo_table WHERE id = 1 FOR UPDATE; -- Append the new note content to the CLOB from the table. l_clob_len := dbms_lob.getlength(l_notes); IF NVL(l_clob_len,0) = 0 THEN -- If there is no note history. dbms_lob.createtemporary(lob_loc => l_notes, cache => true, dur => dbms_lob.call); l_notes := l_tpl || l_new_note; ELSE dbms_lob.append(l_notes, l_tpl || l_new_note); END IF; -- Update the Notes Table with the combined CLOB. UPDATE cndemo_table SET notes = l_notes WHERE id = 1; -- Free up the temporary CLOB. dbms_lob.freetemporary(l_new_note); END;
- The constant
GC_ACTIVITY_TPL, is there to illustrate that we can use APEX Universal Theme CSS classes to format HTML content in a Dynamic Content Region. I have picked out CSS classes from the 'Content Region' to format the date heading I have between each note
Note History Dynamic Content Region
This is the Dynamic Content Region.
Here is the code from the
PL/SQL Function Body returning a CLOB attribute from the screenshot above. Note: this code is just here as an example. Your code could be fetching HTML from a web service, looping through hundreds of records, etc. If you return a CLOB, it will be rendered in the region.
DECLARE l_notes cndemo_table.notes%TYPE; l_note_len NUMBER; BEGIN -- Fetch the Notes History CLOB from a table. SELECT notes , dbms_lob.getlength(notes) note_len INTO l_notes , l_note_len FROM cndemo_table WHERE id = 1; -- If the size of the note is IF NVL(l_note_len,0) = 0 THEN -- If there is no current note then return message to indicate there are no notes. Similar to a Classic Or Interactive Reports region where we have No Data Found. RETURN '<h4>You do not have any notes</h4>'; ELSE -- Return the CLOB containing the HTML for the notes. -- APEX will display whatever HTML you have in the region. RETURN l_notes; END IF; END;
⭐ In the code above, you will notice the other change to Dynamic Content Regions that was introduced in APEX 22.2. In previous versions, you had to output HTML using
apex_util.prn. From APEX 22.2 on, you simply build a CLOB and return it.
In the Attributes section of the region, we have the option to enable
Lazy Loading. This will defer the fetching and rendering of the HTML in the Dynamic Content Region until after the page is rendered. This can be useful if the Dynamic Content Region takes a long time to process, and you don't want the user to have to wait for this before the main page is loaded.
And that is all there is to it!
Guarding Against XSS
See the WASP XSS Guide for more details on Cross-Site Scripting (XSS) Attacks.
You should not need to use Dynamic Content Regions often, but they are essential when you do need them. Having the ability to refresh them dynamically (since APEX 22.2) makes them even more useful.