Skip to main content

Interactive Grid Tips (Part-1)

Selection Toolbar Events Validation Styling Advanced
1
Get Selected Row Primary Key
Return selected row PK values to a page item. Two approaches depending on APEX version.
Selection
// Initialization JavaScript Function
function (options) {
  options.defaultGridViewOptions = {
    selectionStateItem: "P22_SELECTED_IDS"
  };
  return options;
}
var ig$ = apex.region("EMP").call("getViews", "grid");
var model = ig$.model;
var selectedIds = [];

ig$.view$.grid("getSelectedRecords").forEach(function(rec) {
  selectedIds.push(model.getValue(rec, "EMPNO"));
});

$s("P22_SELECTED_IDS", selectedIds.join(":"));
💡 In APEX 24.2+, selectionStateItem automatically stores selected PKs — no custom JS event handler needed.
2
Persist Row Selection Across Pages
Keep selected rows remembered when navigating between grid pages.
Config
// Initialization JavaScript Function
function(config) {
  config.defaultGridViewOptions = {
    persistSelection: true
  };
  return config;
}
3
Auto-Select Rows on Page Load
Programmatically select specific rows when the page loads, based on a column condition.
Selection
// Execute on Page Load
var ig$      = apex.region("EMP").widget();
var gridView = ig$.interactiveGrid("getViews", "grid");
var model    = gridView.model;
var selected = [];

model.forEach(function(record) {
  var deptno = model.getValue(record, "DEPTNO");
  if (deptno == 20) { selected.push(record); }
});

if (selected.length > 0) {
  gridView.setSelectedRecords(selected);
}
4
Limit Selection to Max 5 Rows
Restrict how many rows a user can select at once, with an error message when the limit is exceeded.
Validation
// DA: Selection Change on IG
var grid            = apex.region("EMP").widget();
var view            = grid.interactiveGrid("getViews", "grid");
var selectedRecords = view.getSelectedRecords();
var count           = selectedRecords.length;

if (count > 5) {
  apex.message.clearErrors();
  apex.message.showErrors([{
    type:     "error",
    location: "page",
    message:  "You cannot select more than 5 records.",
    unsafe:   false
  }]);
  view.setSelectedRecords(selectedRecords.slice(0, 5));
}
5
Hide & Remove Toolbar Buttons Conditionally
Lock down the grid to read-only mode based on a page item value — hides edit, save, add row, and all action menu items.
Toolbar
// Initialization JavaScript Function
function(options) {
  options.initActions = function(actions) {
    if ($v('P4_STATUS') === 'N') {
      $(function() {
        // Hide toolbar buttons
        actions.hide("selection-add-row");
        actions.hide("save");
        actions.hide("edit");
        actions.hide("selection-duplicate");
        actions.hide("selection-clear");
        actions.hide("selection-delete");
        $(".a-IG-button--actions").hide();

        // Remove row context menu actions
        actions.remove("row-add-row");
        actions.remove("row-duplicate");
        actions.remove("row-delete");

        // Remove actions dropdown menu items
        actions.remove("show-filter-dialog");
        actions.remove("show-sort-dialog");
        actions.remove("show-columns-dialog");
        actions.remove("show-control-break-dialog");
        actions.remove("show-highlight-dialog");
        actions.remove("show-aggregate-dialog");
        actions.remove("show-flashback-dialog");
        actions.remove("reset-report");
        actions.remove("save-report");
        actions.remove("edit-report");
        actions.remove("delete-report");
      });
    }
  };
  return options;
}
⚠️ Use actions.hide() for toolbar buttons and actions.remove() for context/dropdown menu items — they behave differently.
6
Append Custom Button to IG Toolbar
Move an existing APEX page button into the Interactive Grid toolbar using JavaScript on page load.
Toolbar
// Execute on Page Load
var node = document.getElementById('Cus_Btn_IG'); // Static ID of button
$("#Loct_ig_toolbar_actions_button").parent().append(node);
7
Rename Default IG Toolbar Buttons
Relabel the built-in Save and Add Row buttons with custom, business-specific text.
Toolbar
// Initialization JavaScript Function
function(config) {
  let $ = apex.jQuery,
    toolbarData  = $.apex.interactiveGrid.copyDefaultToolbar(),
    saveAction   = toolbarData.toolbarFind("save"),
    addrowAction = toolbarData.toolbarFind("selection-add-row");

  saveAction.label   = "Submit Department";
  addrowAction.label = "Add Departments";

  config.toolbarData = toolbarData;
  return config;
}
8
Custom Rows-Per-Page Dropdown Pagination
Add a "Rows per page" label and dropdown to the toolbar. Only applicable when Pagination Type is set to "Page".
Toolbar
// Initialization JavaScript Function
function(config) {
  var $ = apex.jQuery,
    toolbarData  = $.apex.interactiveGrid.copyDefaultToolbar(),
    toolbarGroup = toolbarData.toolbarFind("actions1");

  if (toolbarGroup && toolbarGroup.controls) {
    toolbarGroup.controls.unshift({
      type: "SELECT", action: "change-rows-per-page"
    });
    toolbarGroup.controls.unshift({
      type: "STATIC", label: "Rows per page:",
      cssClasses: "u-bold u-mr-sm"
    });
  }

  config.toolbarData = toolbarData;
  config.initActions = function(actions) {
    var rowsAction = actions.lookup("change-rows-per-page");
    if (rowsAction) {
      rowsAction.choices = [
        { label: "3",   value: 3   },
        { label: "5",   value: 5   },
        { label: "20",  value: 20  },
        { label: "50",  value: 50  },
        { label: "100", value: 100 }
      ];
    }
  };
  return config;
}
9
Populate Page Items on Row Selection Change
Extract all column values from the selected IG row and push them into corresponding page items.
Events
// DA: Selection Change Event on IG
var empno, ename, job, mgr, hire, sal, comm, dept;
var model = this.data.model;

if (this.data && this.data.selectedRecords[0]) {
  var rec = this.data.selectedRecords[0];

  function getIGValue(record, colName) {
    var val = model.getValue(record, colName);
    return (val && typeof val === "object") ? val.v : val;
  }

  empno = getIGValue(rec, "EMPNO");
  ename = getIGValue(rec, "ENAME");
  job   = getIGValue(rec, "JOB");
  mgr   = getIGValue(rec, "MGR");
  hire  = getIGValue(rec, "HIREDATE");
  sal   = getIGValue(rec, "SAL");
  comm  = getIGValue(rec, "COMM");
  dept  = getIGValue(rec, "DEPTNO");
}

apex.item("P7_EMPNO").setValue(empno);
apex.item("P7_ENAME").setValue(ename);
apex.item("P7_JOB").setValue(job);
apex.item("P7_MGR").setValue(mgr);
apex.item("P7_HIREDATE").setValue(hire);
apex.item("P7_SAL").setValue(sal);
apex.item("P7_COMM").setValue(comm);
apex.item("P7_DEPTNO").setValue(dept);
✓ The helper getIGValue() safely handles LOV columns that return an object {v, d} — it extracts the raw value automatically.
10
Dynamic Column Labels from Page Item
Rename IG column headers at runtime based on a page item value using a range pattern like W1-W5.
Advanced
// Function Global / Execute on Page Load
setTimeout(function() {
  let itemValue = $v("P11_LABEL_ITEM");
  if (!itemValue) return;

  let parts  = itemValue.split("-");
  let prefix = parts[0].match(/[A-Za-z]+/)[0];
  let start  = parseInt(parts[0].replace(/\D/g, ""));
  let end    = parseInt(parts[1].replace(/\D/g, ""));
  let labels = [];

  for (let i = start; i <= end; i++) {
    labels.push(prefix + i);
  }

  let ig$  = apex.region("IG_ST_ID").widget();
  let view = ig$.interactiveGrid("getCurrentView");
  let isEditMode = view?.model?.getOption("editable");
  let skipCount  = isEditMode ? 2 : 0;
  let $headers   = $("#IG_ST_ID").find(".a-GV-header, .a-GV-columnHeader");
  let labelIdx   = 0, currIdx = 0;

  $headers.each(function() {
    if (currIdx++ < skipCount) return;
    let $label = $(this).find(".a-GV-headerLabel");
    if ($label.length && labelIdx < labels.length) {
      $label.text(labels[labelIdx++]);
    }
  });
}, 800);
⚠️ The 800ms delay ensures the IG has fully rendered before headers are queried. Adjust if your page loads faster or slower.
11
Disable Row Selection Based on Column Value
Prevent certain rows from being selectable (e.g. DEPTNO = 10). Overrides Select All to skip restricted rows.
Advanced
// Execute on Page Load
var ig$             = apex.region("EMP").widget();
var gridView        = ig$.interactiveGrid("getViews").grid;
var model           = gridView.model;
var nonSelectableIds  = [];
var isSelectAllActive = false;

model.forEach(function(record) {
  var deptno   = model.getValue(record, "DEPTNO");
  var recordId = model.getRecordId(record);
  if (deptno == 10) {
    nonSelectableIds.push(recordId);
    $("tr[data-id='" + recordId + "']")
      .find("th span.u-selector").remove();
    $("tr[data-id='" + recordId + "']").attr("data-no-select", "Y");
  }
});

$("tr[data-no-select='Y'] th").off("click").on("click", function(e) {
  e.stopImmediatePropagation();
});

$(".a-GV-header .u-selector").off("click").on("click", function(e) {
  e.preventDefault(); e.stopImmediatePropagation();
  if (!isSelectAllActive) {
    var allowed = [];
    model.forEach(function(record) {
      if (!nonSelectableIds.includes(model.getRecordId(record))) {
        allowed.push(record);
      }
    });
    gridView.setSelectedRecords(allowed);
    isSelectAllActive = true;
  } else {
    gridView.setSelectedRecords([]);
    isSelectAllActive = false;
  }
});
12
Highlight Selected Row with Custom Background
Override the APEX default selection colour using inline CSS at the page level.
Styling
/* Inline CSS — Page Level (replace #DEPT with your region static ID) */
#DEPT .a-GV-table tr.is-selected .a-GV-cell {
  background-color: #58FAAC !important;
}
💡 Replace #DEPT with your IG region's Static ID. Change #58FAAC to any colour you need.
13
Column-Level Input Validation
Restrict input in IG columns using native browser validation — letters only for ENAME, numbers only for SAL.
Validation
// DA: Column Change — ENAME column, Input Event
var input   = this.triggeringElement;
var val     = input.value;
var isValid = /^[A-Za-z ]+$/.test(val);

if (!isValid) {
  input.setCustomValidity("Only characters allowed!");
} else {
  input.setCustomValidity("");
}
input.reportValidity();
// DA: Column Change — SAL column, Input Event
var input   = this.triggeringElement;
var val     = input.value;
var isValid = /^[0-9]+$/.test(val);

if (!isValid) {
  input.setCustomValidity("Only numbers allowed!");
} else {
  input.setCustomValidity("");
}
input.reportValidity();
✓ Set the DA event to Input (not Change) so validation fires in real time as the user types, not just on blur.

Comments

Popular posts from this blog

APEX - Tip: Fix Floating Label Issue

Oracle APEX's Universal Theme provides a modern and clean user experience through features like floating (above) labels for page items.  These floating labels work seamlessly when users manually enter data, automatically moving the label above the field on focus or input.  However, a common UI issue appears when page item values are set Dynamically the label and the value overlap, resulting in a broken and confusing user interface. once the user focuses the affected item even once, the label immediately corrects itself and displays properly. When an issue is reported, several values are populated based on a single user input, causing the UI to appear misaligned and confusing for the end user. Here, I'll share a few tips to fix this issue. For example, employee details are populated based on the Employee name. In this case, the first True Action is used to set the values, and in the second True Action, paste the following code setTimeout(function () {   $("#P29_EMAIL,#P29_...

Oracle APEX UI Tip: Display Page Title Next to the APEX Logo

In most Oracle APEX applications, every page has a Page Title displayed at the top. While useful, this title occupies vertical space, especially in apps where screen real estate matters (dashboards, reports, dense forms). So the goal is simple: Show the page title near the APEX logo instead of consuming page content space. This keeps the UI clean, professional, and consistent across all pages. Instead of placing the page title inside the page body:         ✅ Fetch the current page title dynamically         ✅ Display it right after the APEX logo         ✅ Do it globally, so it works for every page All of this is achieved using:         ✅ Global Page (Page 0)         ✅ One Dynamic Action         ✅ PL/SQL + JavaScript Simple, effective, and reusable. 1️⃣ Create a Global Page Item On Page 0 (Global Page), create a hidden item:      P0_PAGE_TITLE This item wi...

Building a Custom Debug Package for Oracle APEX Using PL/SQL

While developing Oracle APEX applications, debugging page processes and backend PL/SQL logic can be challenging—especially when values are lost between processes or execution flow is unclear.  Although DBMS_OUTPUT is useful, it doesn’t work well inside APEX runtime. To solve this, I built a custom PL/SQL debug Package that logs execution flow and variable values into a database table.  This approach helps trace exactly where the code reached, what values were passed, and whether a block executed or not - even inside page-level processes and packaged procedures Why a Custom Debug Package? Works seamlessly inside Oracle APEX page processes Persists debug information even after session ends Helps trace execution flow Captures runtime values Can be turned ON/OFF dynamically Does not interrupt business logic The Package consists of:- Debug Table                         -  Stores debug messages Sequence ...