Introduction
Oracle APEX provides several standard components for tracking progress, but they are mostly percentage-based. In applications where a batch or job moves through defined sequential operations, users need a step-by-step visual representation rather than a single progress value.
This blog explains how to build a dynamic, step-based process progress bar in Oracle APEX using AJAX Callback (On-Demand Process), Dynamic Actions, JavaScript, and CSS, and clearly shows where each piece of code is placed inside APEX.
Why We Need to Do This
Standard APEX progress bars:
- Do not represent individual steps
- Do not reflect real process flow
- Are difficult to map to operation sequences
A custom process progress bar:
- Visually mirrors the actual workflow
- Improves usability and clarity
- Reduces dependency on textual status indicators
How Do We Solve This
We split the solution into four APEX layers:
- AJAX Callback (On-Demand Process) – Fetch data
- Dynamic Action – Trigger the refresh
- JavaScript – Render the progress bar
- CSS – Style the UI
Each layer has a clear responsibility and location inside APEX.
Step 1: Create the Container Region (UI)
Where:
APEX Page → Create Region
- Region Type: Static Content
- Static ID: process_progress
This region acts as the placeholder where the progress bar will be rendered.
Step 2: Create AJAX Callback (On-Demand Process)
Where:
APEX Page → Processing → Ajax Callback
- Name: GET_PROCESS_PROGRESS
Code:
DECLARE
BEGIN
apex_json.open_array;
FOR r IN (
SELECT
w.OPERATION_SEQ_NUM AS process_code,
w.DESCRIPTION || ‘ (‘ || w.OPERATION_SEQ_NUM || ‘)’ AS process_name,
CASE
WHEN COUNT(DISTINCT e.OPERATION_SEQUENCE_ID) > 0
THEN ‘Y’
ELSE ‘N’
END AS completed
FROM apps.WIP_OPERATIONS_V w
JOIN apps.wip_discrete_jobs_v j
ON w.WIP_ENTITY_ID = j.WIP_ENTITY_ID
LEFT JOIN XXTTK_ODM_RUN_CARD_ENTRIES e
ON e.OPERATION_SEQUENCE_ID = w.OPERATION_SEQ_NUM
AND e.BATCH_NO = j.WIP_ENTITY_NAME
WHERE j.WIP_ENTITY_NAME = :P11_BATCH_NO
GROUP BY w.OPERATION_SEQ_NUM, w.DESCRIPTION
ORDER BY w.OPERATION_SEQ_NUM
) LOOP
apex_json.open_object;
apex_json.write(‘process_code’, r.process_code);
apex_json.write(‘completed’, r.completed);
apex_json.write(‘process_name’, r.process_name);
apex_json.close_object;
END LOOP;
apex_json.close_array;
END;
This process:
- Runs via AJAX
- Returns process data as JSON
- Keeps business logic on the server side
Step 3: Create Dynamic Action to Trigger JavaScript
Where:
APEX Page → Dynamic Actions
- Event: Page Load
(or Change of P11_BATCH_NO if batch can change)
- Selection Type: Page
- True Action: Execute JavaScript Code
This ensures the progress bar loads automatically when the page opens or when batch changes.
Step 4: JavaScript to Call AJAX Process
Where:
Inside the Dynamic Action → Execute JavaScript Code
apex.server.process(
“GET_PROCESS_PROGRESS”,
{},
{
success: function (data) {
let wrapper = document.createElement(“div”);
wrapper.className = “process-wrapper”;
let rowContainer = document.createElement(“div”);
rowContainer.className = “process-container”;
wrapper.appendChild(rowContainer);
data.forEach(function (row, index) {
let step = document.createElement(“div”);
step.className = “process-step”;
if (row.completed === “Y”) {
step.classList.add(“completed”);
}
step.innerText = row.process_code;
step.title = row.process_name;
rowContainer.appendChild(step);
if (index < data.length – 1) {
let line = document.createElement(“div”);
line.className = “process-line”;
if (row.completed === “Y”) {
line.classList.add(“completed”);
}
rowContainer.appendChild(line);
}
});
let region = document.getElementById(“process_progress”);
region.innerHTML = “”;
region.appendChild(wrapper);
}
}
);
This JavaScript:
- Calls the AJAX Callback
- Dynamically creates HTML elements
- Applies styles based on completion status
Step 5: Add CSS for Styling
Where (Recommended):
- Page → CSS → Inline
(or Application → Shared Components → Theme → Custom CSS)
.process-wrapper {
display: flex;
flex-direction: column;
gap: 3px;
}
.process-container {
display: flex;
align-items: center;
flex-wrap: nowrap;
}
.process-step {
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 11px;
border: 1px solid #ccc;
background: #eee;
color: #555;
}
.process-step.completed {
background: #1b1c6d;
border-color: #1b1c6d;
color: #fff;
}
.process-line {
width: 10px;
border-top: 2px solid #ccc;
margin: 0 1px;
}
.process-line.completed {
border-top-color: #1b1c6d;
}

Conclusion
This implementation demonstrates how Oracle APEX can be extended beyond standard components to create custom, business-aligned visualizations.
By clearly separating:
- Data logic (AJAX Callback)
- UI behavior (Dynamic Actions & JavaScript)
- Presentation (CSS)
we achieve a solution that is scalable, maintainable, and easy to enhance.