Oracle APEX Hack: Move Aggregate Row And Display Column Totals on Top

Introduction 

In Oracle APEX Interactive Reports (IR), aggregate values such as SUM or AVG are always displayed at the bottom of the report. For reports with many rows, users must scroll down every time just to see totals, which affects usability and visibility.

There is no built-in option in Oracle APEX to move these aggregates to the top of the report region. As a result, users often miss important summary information during analysis.

This blog explains how to display column totals at the top of an Interactive Report using a pure JavaScript solution, without using plugins or making any backend or SQL changes.

 

Why We Need This 

 

When working with Interactive Reports:

• Aggregate rows are fixed at the bottom
• Users must scroll to see totals
• Important summary values are easily missed
• Poor user experience in large reports

This issue is common in scenarios where:

• Reports contain many rows
• Totals need quick visibility
• Managers or users focus more on summary data
• Clean and user-friendly UI is required

The impact includes:

• Reduced usability
• Time wasted scrolling
• Missed or overlooked totals
• Less professional report layout

 

How Do We Solve This 

 

We solve this problem by:

• Reading aggregate values directly from the IR DOM
• Creating a custom top aggregate bar
• Injecting totals above the report toolbar
• Refreshing totals automatically after report refresh

This solution uses:

• JavaScript to fetch and display aggregate values
• No plugins
• No backend or SQL changes.

 

 

Steps to Implement:

 

Step 1: Assign a Static ID to the Interactive Report

Example Static ID:
IRR

Step 2: Add JavaScript Code

Add the following code in Function and Global Variable Declaration:

(function ($) {

var REGION_ID = “IRR”;                   // IR region Static ID

var COL_IDS   = [“A_QTY”,”A_VAL”,”U_QTY”, “U_VAL”,”STOCK_VALUE”];  // column Static IDs

 

function getHeaderLabel($r, colId) {

var $th = $r.find(“.a-IRR-table th#” + colId + “, .a-IRR-table th[headers='” + colId + “‘], .a-IRR-table th[data-column*='” + colId + “‘]”).first();

 

if ($th.length) {

if ($th.attr(“abbr”)) return $th.attr(“abbr”).trim();

if ($th.attr(“title”)) return $th.attr(“title”).trim();

 

var txt = $th.children(“span”).first().text().trim();

if (txt) return txt;

 

txt = ($th.text() || “”).trim();

if (txt) return txt;

}

return colId;

}

 

function putTopTotals() {

var $r = $(“#” + REGION_ID);

if (!$r.length) return;

 

$r.find(“.js-top-agg-bar”).remove(); // clear old

 

var container = document.createElement(“div”);

container.className = “js-top-agg-bar”;

container.style.cssText = [

“padding:12px”,

“margin:8px 0 10px”,

“border:1px solid #e0e0e0”,

“border-radius:10px”,

“background:#fafafa”

].join(“;”);

 

// Heading

var heading = document.createElement(“div”);

heading.textContent = “Stock Quantity Value”;

heading.style.cssText = [

“font-weight:700”,

“margin-bottom:8px”,

“font-size:14px”,

“color:#333”

].join(“;”);

container.appendChild(heading);

 

// Row container for pills

var pillRow = document.createElement(“div”);

pillRow.style.cssText = [

“display:flex”,

“gap:16px”,

“flex-wrap:wrap”,

“align-items:center”

].join(“;”);

container.appendChild(pillRow);

 

var found = 0;

 

COL_IDS.forEach(function (colId) {

var $val = $r.find(“.a-IRR-table td.a-IRR-aggregate[headers='” + colId + “‘] .a-IRR-aggregate-value”).last();

if ($val.length) {

found++;

var label = getHeaderLabel($r, colId);

 

var pill = document.createElement(“div”);

pill.style.cssText = [

“display:inline-flex”,

“align-items:center”,

“gap:8px”,

“padding:6px 12px”,

“border:1px solid #ddd”,

“border-radius:9999px”,

“font-size:13px”,

“background:#fff”

].join(“;”);

 

var strong = document.createElement(“span”);

strong.style.fontWeight = “600”;

strong.textContent = label + “:”;

 

var span = document.createElement(“span”);

span.textContent = $val.text().trim();

 

pill.appendChild(strong);

pill.appendChild(span);

pillRow.appendChild(pill);

}

});

 

if (found) {

var toolbar = $r.find(“.a-IRR-toolbar”).get(0);

if (toolbar && toolbar.parentNode) {

toolbar.parentNode.insertBefore(container, toolbar);

} else {

var regionBody = $r.find(“.t-Region-body, .a-IRR”).get(0);

if (regionBody) regionBody.insertBefore(container, regionBody.firstChild);

}

}

}

 

$(document).on(“apexafterrefresh”, “#” + REGION_ID, putTopTotals);

putTopTotals();

})(apex.jQuery);

 

Result:

After applying this solution:

• Aggregate totals appear above the report
• Totals remain visible after filtering or refresh
• Multiple columns can be displayed
• UI becomes cleaner and more professional

 

 

 

Conclusion

Using this lightweight JavaScript approach, we successfully:

• Moved Interactive Report aggregates to the top
• Improved visibility of important totals
• Enhanced user experience
• Avoided plugins and backend changes

Recent Posts