In-cell Progress Bars Excel-Style Data Bars inside Oracle APEX Interactive Reports
Two production-ready approaches to rendering beautiful progress bars directly inside Interactive Report columns — no plugins, no external libraries. One uses APEX's native Percent Graph column type; the other uses fully custom HTML expressions with traffic-light color coding. Both cover all three columns: Salary, Performance, and Experience.
width percentage driven by hidden SQL columns. The trick was computing hex color strings right inside the SQL CASE expression, then substituting them via #COLUMN_NAME# tokens. No JavaScript, no Dynamic Actions — just a clean SQL query and a few lines of HTML per column.
Using the Percent Graph Column Type
The simplest approach. APEX has a native Percent Graph column type that renders a built-in horizontal progress bar directly in the Interactive Report cell. All you need is a SQL column with a value between 0 and 100 — APEX does the rest.
SELECT e.EMP_ID, e.EMP_NAME, e.DEPARTMENT, e.JOB_TITLE, e.LOCATION, e.SALARY, e.PERFORMANCE, e.EXPERIENCE, e.STATUS, ROUND( (e.SALARY / MAX(e.SALARY) OVER()) * 100 ) AS SALARY_PCT, ROUND( e.PERFORMANCE ) AS PERF_PCT, ROUND( LEAST( (e.EXPERIENCE / 20) * 100, 100 ) ) AS EXP_PCT FROM EMPLOYEE_DATA e ORDER BY e.DEPARTMENT, e.EMP_ID
After adding the SQL, set the column type for each computed column in Page Designer:
Percent Graph — renders a bar proportional to max salary in the result set
Percent Graph — performance score already on 0–100 scale
Percent Graph — experience capped at 20 years = 100%
Using Custom HTML Expressions
The powerful approach. You write raw HTML in the Column Formatting → HTML Expression field, using #COLUMN_NAME# substitution tokens to inject SQL values. This gives you complete control over bar width, colour, labels, and layout — for all three columns.
<meter> HTML element. It rendered, but the styling was completely browser-controlled and inconsistent across Chrome and Firefox. The second attempt used CSS clip-path — that just disappeared entirely in APEX's report rendering. Settled on a plain position:absolute filled div inside a relative container — dead simple, works everywhere.
SELECT e.EMP_ID, e.EMP_NAME, e.DEPARTMENT, e.JOB_TITLE, e.SALARY, e.PERFORMANCE, e.EXPERIENCE, e.HIRE_DATE, e.LOCATION, e.STATUS, ROUND( (e.SALARY / MAX(e.SALARY) OVER()) * 100 ) AS SAL_PCT, ROUND( e.PERFORMANCE ) AS PERF_PCT, ROUND( LEAST(e.EXPERIENCE / 20 * 100, 100) ) AS EXP_PCT, CASE WHEN e.PERFORMANCE >= 75 THEN '#43a047' WHEN e.PERFORMANCE >= 50 THEN '#fb8c00' ELSE '#e53935' END AS PERF_COLOR, CASE WHEN ROUND((e.SALARY / MAX(e.SALARY) OVER())*100) >= 70 THEN '#1a73e8' WHEN ROUND((e.SALARY / MAX(e.SALARY) OVER())*100) >= 40 THEN '#7b61ff' ELSE '#1a73e8' END AS SAL_COLOR, '#6d28d9' AS EXP_COLOR FROM EMPLOYEE_DATA e
Configure the three visible columns as Plain Text, then set the HTML Expression for each one:
<div style="display:flex;align-items:center;gap:6px;width:100%">
<div style="flex:1;height:18px;background:#dce8f8;border-radius:3px;
overflow:hidden;position:relative">
<div style="position:absolute;left:0;top:0;height:100%;
border-radius:3px;width:#SAL_PCT#%;
background:#SAL_COLOR#"></div>
</div>
<span style="font-size:11px;font-weight:600;color:#0C447C;
min-width:58px;text-align:right">
₹#SALARY#
</span>
</div>
<div style="display:flex;align-items:center;gap:6px;width:100%">
<div style="flex:1;height:18px;background:#e8f5e9;border-radius:3px;
overflow:hidden;position:relative">
<div style="position:absolute;left:0;top:0;height:100%;
border-radius:3px;width:#PERF_PCT#%;
background:#PERF_COLOR#"></div>
</div>
<span style="font-size:11px;font-weight:600;color:#1b5e20;
min-width:36px;text-align:right">
#PERFORMANCE#%
</span>
</div>
<div style="display:flex;align-items:center;gap:6px;width:100%">
<div style="flex:1;height:18px;background:#ede7f6;border-radius:3px;
overflow:hidden;position:relative">
<div style="position:absolute;left:0;top:0;height:100%;
border-radius:3px;width:#EXP_PCT#%;
background:#EXP_COLOR#"></div>
</div>
<span style="font-size:11px;font-weight:600;color:#4a148c;
min-width:44px;text-align:right">
#EXPERIENCE# yrs
</span>
</div>
Set all helper columns to Hidden so they remain available as substitution tokens without appearing as visible columns:
#SAL_PCT#, #PERF_COLOR#, and #EXP_COLOR# available inside HTML expressions of other columns.
What the Report Looks Like
| Employee | Dept | Salary | Performance | Experience | Status |
|---|---|---|---|---|---|
| Ananya R. | Engineering |
88%
|
82%
|
75%
|
Active |
| Rajan M. | Finance |
55%
|
62%
|
40%
|
Active |
| Divya S. | HR |
34%
|
38%
|
20%
|
Inactive |
| Karthik P. | Operations |
100%
|
90%
|
100%
|
Active |
MAX(e.SALARY) OVER() returns the maximum salary across the whole result set without grouping. Dividing each salary by this max and multiplying by 100 gives a percentage relative to the top earner.LEAST(experience/20*100, 100) treats 20 years as the ceiling. Anyone with 20+ years still shows as 100% — preventing bars from overflowing their container.'#43a047' based on the numeric threshold. This is injected directly into the HTML as a CSS background value via the #PERF_COLOR# token.#COLUMN_NAME# tokens in HTML Expression fields with the row value before the HTML reaches the browser. Hidden columns are still substituted even though they aren't visible.display:flex wrapper. The bar track gets flex:1 to fill the cell width, and a fixed min-width label sits right-aligned beside it — keeping all rows consistent.| Feature | Solution 1 — Percent Graph | Solution 2 — HTML Expression |
|---|---|---|
| Setup complexity | ✓ Very simple | ~ Moderate |
| Salary column bar | ✓ Yes | ✓ Yes + ₹ symbol |
| Performance column bar | ✓ Yes | ✓ Yes + traffic-light color |
| Experience column bar | ✓ Yes | ✓ Yes + "yrs" label |
| Custom bar color per row | ✗ Not supported | ✓ Full control via CASE |
| Traffic-light coloring | ✗ Not supported | ✓ Green / Amber / Red |
| Currency / unit symbol | ✗ Not supported | ✓ Any symbol |
| Works with IR search/filter | ✓ Yes | ✓ Yes |
Comments
Post a Comment