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)
thisbinding: 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.
hotcolis 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
foldre-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
grid1refers to the subgrid. Inside this handlergrid1is 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 |
Related topics
- Grid Events →
subgridOnOpen - Grid Events →
afterChange - Grid Helpers →
gridColNum() - Grid Helpers →
getSelected() - Form Designer → Configuring subgrid lookups on a column