Interactive Grid Bulk Actions with AJAX Callbacks in Oracle APEX (JSON Edition)

Intro

Interactive Grids in Oracle APEX are powerful for editing rows, but what if you need to apply bulk actions — like updating or deleting multiple rows at once?

This post walks through how I built bulk update and bulk delete features for an IG using apex.server.process, JSON payloads, and proper error handling. Along the way, I discovered some important concepts that every APEX developer should understand.


Step 1: Enable Row Selection in IG

  • Go to your Interactive Grid’s attributes.

  • Enable Row Selection → this adds the leftmost checkbox column.

  • Now users can tick multiple rows, Gmail-style.

(Screenshot: IG with row checkboxes)


Step 2: Collecting Selected Rows in JavaScript

Use IG’s API to get the selected records and pull primary keys (e.g., EMPNO):

var ig$   = apex.region("emp").widget(); 
var model = ig$.interactiveGrid("getViews","grid").model;

var sel = ig$.interactiveGrid("getSelectedRecords");
var ids = sel.map(function(rec){
  return model.getValue(rec, "EMPNO");  // primary key
});

(Screenshot: console log of IDs when rows are selected)


Step 3: Sending Data to the Server (JSON Payload)

Instead of old CSV tricks, send structured JSON.

var payload = JSON.stringify({ ids: ids, setTo: 0, reason: "bulk clear" });

apex.server.process(
  "BULK_CLEAR_COMM_JSON",              // Ajax Callback name
  { x01: payload },                    // JSON string → g_x01
  {
    success: function(pData){
      if (pData && pData.ok === false) {
        apex.message.showErrors(pData.errors);
        return;
      }
      var n = (pData && pData.updated) ? pData.updated : 0;
      apex.message.showPageSuccess("Updated COMM for " + n + " row(s).");
      apex.region("emp").refresh();
    },
    error: function(req, status, err){
      apex.message.showErrors([{ type:'error', location:'page', message:'Server error: ' + err }]);
    }
  }
);

Step 4: The PL/SQL Ajax Callback

On the server, parse JSON, loop IDs, perform the update, and return a JSON result:

declare
  j       apex_json.t_values;
  l_count number := 0;
  l_id    number;
  l_size  pls_integer;
  l_setto number;
  l_reason varchar2(4000);

  procedure send_error(p_msg varchar2) is
  begin
    apex_json.open_object;
    apex_json.write('ok', false);
    apex_json.open_array('errors');
      apex_json.open_object;
      apex_json.write('type','error');
      apex_json.write('location','page');
      apex_json.write('message', p_msg);
      apex_json.close_object;
    apex_json.close_array;
    apex_json.close_object;
  end;
begin
  -- Parse JSON {"ids":[..], "setTo":0, "reason":"..."}
  apex_json.parse(j, apex_application.g_x01);

  l_setto  := apex_json.get_number(j,'setTo');
  l_reason := apex_json.get_varchar2(j,'reason');
  l_size   := apex_json.get_count(j,'ids');

  if l_size = 0 then
    send_error('No rows selected.'); return;
  end if;

  for i in 1..l_size loop
    l_id := apex_json.get_number(j,'ids[%d]', i);
    update emp_copy set comm = l_setto where empno = l_id;
    l_count := l_count + sql%rowcount;
  end loop;

  apex_json.open_object;
  apex_json.write('ok', true);
  apex_json.write('updated', l_count);
  apex_json.close_object;
end;

(Screenshot: IG before & after COMM column cleared)

BEFORE

AFTER


Step 5: Bulk Delete Example

Same pattern, but PL/SQL performs a delete:

JavaScript:

apex.message.confirm(
  "Delete " + ids.length + " row(s)?",
  function(ok){
    if(!ok) return;
    apex.server.process("BULK_DELETE_EMP",
      { x01: JSON.stringify({ ids: ids }) },
      { success: function(pData){ ... } }
    );
  }
);

PL/SQL:

for i in 1..l_size loop
  l_id := apex_json.get_number(j, 'ids[%d]', i);
  delete from emp_copy where empno = l_id;
  l_count := l_count + sql%rowcount;
end loop;

apex_json.open_object;
apex_json.write('ok', true);
apex_json.write('deleted', l_count);
apex_json.close_object;

(Screenshot: rows disappearing from IG after delete)

BEFORE

AFTER


Step 6: Error Handling the Right Way

One big lesson: AJAX “success” callback fires even on business errors because HTTP 200 means protocol success.

  • Business/validation errors → handle inside PL/SQL with ok:false JSON.

  • Client inspects if (pData.ok===false) → show errors with apex.message.showErrors.

  • Only network/500 errors trigger the error callback.

(Diagram: HTTP vs App error flow)


Step 7: Real-World Extensions

  • Audit logging → log who updated/deleted, old/new values, and reason.

  • Soft deletes → mark is_deleted='Y' instead of hard delete.

  • UX polish → disable button while Ajax in progress.

  • Inline errors → tie errors to specific items (pageItem: 'P1_SETTO').


Key Takeaways

  • Bulk IG actions = row selectors + apex.server.process + JSON payloads.

  • Always return structured JSON (ok:true/false, errors[]).

  • Use apex.message.showErrors for clean UX.

  • Separate HTTP failures (500, network) from business errors.


Closing

Building bulk update/delete in APEX IGs shows how client-side JS and server-side PL/SQL work hand in hand.

👉 Next up, I’ll share a focused post on why apex.server.process success fires even when there’s an error — and how to handle it with proper error JSON.

#OracleAPEX #LowCode #PLSQL #WebDev #SQL

1
Subscribe to my newsletter

Read articles from Bhuvnesh Singh Chauhan (bhuvi_champ) directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bhuvnesh Singh Chauhan (bhuvi_champ)
Bhuvnesh Singh Chauhan (bhuvi_champ)