Skip to content

Event: subgridOnClosed

Category: Grid Events → Subgrid Lifecycle Applies to: MERCI ERP — Form/Grid Engine Defined in: <project>_core.js Scope: Client-side (browser)


Overview

subgridOnClosed is a per-field lifecycle hook that fires immediately after a subgrid popup is dismissed and control returns to the parent grid.

A subgrid is a lookup/detail grid launched from a cell in the parent grid (grid1). When the operator closes it — whether by selecting a record, pressing Escape, or clicking the close control — the engine looks for a handler matching the naming convention below and, if one exists, invokes it.

The hook is the correct place to:

  • refresh or recalculate cells in the parent row that depend on the subgrid selection,
  • re-apply formatting or validation after values are written back,
  • force dependent formulas to re-evaluate,
  • reposition focus for the next data-entry step.

Naming convention

<subgridtable>_<callerfield>_subgridOnClosed()
Token Meaning
<subgridtable> Table name bound to the subgrid that was opened
<callerfield> Column (field) in the parent grid from which the subgrid was launched

The engine resolves the handler by name at close time. If no function with the exact matching name exists, the event is silently skipped — no error is raised. This means the handler is opt-in per field, and you only define it where post-close logic is genuinely required.

Example

A subgrid bound to taka_master, opened from the taka_no column of the parent grid, resolves to:

function taka_master_taka_no_subgridOnClosed(){ … }

Signature

function <subgridtable>_<callerfield>_subgridOnClosed()
  • Parameters: none
  • Returns: nothing (return value is ignored)
  • this binding: not guaranteed — do not rely on it; use the global grid handle instead

Because no arguments are passed, the handler must derive its own context from the current grid selection (see below).


Reference implementation

function <subgridtable>_<callerfield>_subgridOnClosed(){
   let aElem = getSelected();
   let hotrow = aElem[0];
   let hotcol = aElem[1];
   let foldcol = gridColNum('fold');
   let fold = grid1.getDataAtCell( hotrow, foldcol );
   grid1.setDataAtCell(hotrow, foldcol, fold);
}

Code walkthrough

1. Recover the active cell context

let aElem = getSelected();
let hotrow = aElem[0];
let hotcol = aElem[1];

getSelected() returns the current selection coordinates of the parent grid as an array:

Index Value
0 Start row (hotrow)
1 Start column (hotcol)
2 End row
3 End column

Since the subgrid was launched from a specific cell, the selection still points at that cell when the popup closes. This is how the handler learns which row it is operating on without receiving parameters.

hotcol is captured here for readability and for handlers that branch on the caller column. In the reference implementation it is not consumed — remove it if your handler does not need it.

2. Resolve the target column by name

let foldcol = gridColNum('fold');

gridColNum(columnName) maps a logical field name to its physical column index in grid1. Always use this helper rather than hard-coding an index — column order changes when fields are added, hidden, or re-sequenced in the form designer, and hard-coded indices break silently.

3. Read and write back the same value

let fold = grid1.getDataAtCell( hotrow, foldcol );
grid1.setDataAtCell(hotrow, foldcol, fold);

This is the touch pattern. Writing a value back into a cell — even the identical value — raises the grid's change pipeline (afterChange and the dependent-cell recalculation chain) for that row.

The purpose is not to alter data. It is to notify the engine that the row has been updated by the subgrid, so that:

  • computed columns depending on fold re-evaluate,
  • row-level validation re-runs,
  • the rendered cell repaints with any conditional formatting.

Without this, values written into the parent row by the subgrid may display correctly but leave downstream formulas stale until the operator manually edits a cell.


Placement

Define the handler in <project>_core.js, alongside the other grid event handlers for the project.

/js/
  └── <project>_core.js      ← handler goes here

<project>_core.js is loaded on every form in the project, so the handler is resolvable regardless of which form opened the subgrid. Do not place these handlers in form-specific or inline scripts — the resolver will not find them reliably.

Group related handlers together and keep the naming order consistent with the form layout for maintainability:

/* ---------- Subgrid close handlers ---------- */

function taka_master_taka_no_subgridOnClosed(){ … }
function yarn_master_yarn_code_subgridOnClosed(){ … }
function party_master_party_code_subgridOnClosed(){ … }

Practical variations

Recalculate a derived column

function yarn_master_yarn_code_subgridOnClosed(){
   let aElem   = getSelected();
   let hotrow  = aElem[0];

   let qtycol  = gridColNum('qty');
   let ratecol = gridColNum('rate');
   let amtcol  = gridColNum('amount');

   let qty  = Number(grid1.getDataAtCell(hotrow, qtycol))  || 0;
   let rate = Number(grid1.getDataAtCell(hotrow, ratecol)) || 0;

   grid1.setDataAtCell(hotrow, amtcol, qty * rate);
}

Move focus to the next entry field

function party_master_party_code_subgridOnClosed(){
   let aElem  = getSelected();
   let hotrow = aElem[0];

   grid1.selectCell(hotrow, gridColNum('remarks'));
}

Guard against an empty selection

function taka_master_taka_no_subgridOnClosed(){
   let aElem = getSelected();
   if (!aElem || aElem.length < 2) { return; }

   let hotrow  = aElem[0];
   let takacol = gridColNum('taka_no');
   let taka    = grid1.getDataAtCell(hotrow, takacol);

   if (taka === null || taka === '') { return; }   // operator cancelled

   let foldcol = gridColNum('fold');
   grid1.setDataAtCell(hotrow, foldcol, grid1.getDataAtCell(hotrow, foldcol));
}

Guidelines

Do

  • Resolve every column through gridColNum().
  • Read the row from getSelected() at the top of the handler; do not cache it across events.
  • Keep the handler short and synchronous — it runs on the UI thread while the popup is unwinding.
  • Return early when the operator cancelled the subgrid without choosing a record.
  • Coerce values with Number() before arithmetic; grid cells return strings.

Avoid

  • Hard-coded column indices.
  • Opening another subgrid or modal from inside the handler — the close sequence is still in progress.
  • Long-running or blocking AJAX calls; if a server round-trip is unavoidable, make it asynchronous and update the grid in the callback.
  • Assuming grid1 refers to the subgrid. Inside this handler grid1 is always the parent grid.
  • Writing to a column that is itself the caller field unless you intend to override the operator's selection.

Common issues

Symptom Likely cause
Handler never runs Function name does not match <subgridtable>_<callerfield>_subgridOnClosed exactly (case-sensitive), or it is not in <project>_core.js
aElem is null / undefined Parent grid lost selection during close; add the empty-selection guard
foldcol is -1 or undefined Field name passed to gridColNum() does not exist or is spelled differently in the form definition
Dependent totals still stale Touch pattern applied to the wrong column, or the dependency chain keys off a different field
Value written by the subgrid gets overwritten Handler is writing to the caller field itself after the subgrid already set it
Infinite loop / grid freeze An afterChange hook re-triggers the subgrid, which re-fires this handler

  • Grid Events → subgridOnOpen
  • Grid Events → afterChange
  • Grid Helpers → gridColNum()
  • Grid Helpers → getSelected()
  • Form Designer → Configuring subgrid lookups on a column