Frontmatter
| id | 9072 |
| title | refactor: Adopt ''internalId'' for Stable DOM Keying (Grid & Table) |
| state | Closed |
| labels | airefactoringcore |
| assignees | tobiu |
| createdAt | Feb 9, 2026, 11:50 AM |
| updatedAt | Feb 9, 2026, 1:32 PM |
| githubUrl | https://github.com/neomjs/neo/issues/9072 |
| author | tobiu |
| commentsCount | 2 |
| parentIssue | 9074 |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Feb 9, 2026, 1:32 PM |
refactor: Adopt 'internalId' for Stable DOM Keying (Grid & Table)

Input from Gemini 3 Pro:
✦ Task Completed: Adopt 'internalId' for Stable DOM Keying (Grid & Table)
Objective: Refactor Grid and Table components to use the stable
internalId(implemented in #9070) for generating DOM IDs, ensuring robust handling of phantom records (id: null), security, and mutability.Implementations:
Grid & Table Containers (
src/grid/Container.mjs,src/table/Container.mjs):
- Added
useInternalIdconfig (defaulting totrue).- Propagated this config to the internal
Bodycomponent viabeforeSetBodyandafterSetUseInternalId.Grid & Table Bodies (
src/grid/Body.mjs,src/table/Body.mjs):
- Added
useInternalIdconfig (defaulting totrue).- Implemented
getRecordId(record)helper which toggles betweenstore.getInternalId()(e.g.neo-record-1) andstore.getKey()based on the config.- Updated
getLogicalCellId,getCellId,getRowIdto utilize this abstraction.- Updated event handlers (
fireCellEvent,fireRowEvent) to resolve records correctly without assuming integer IDs whenuseInternalIdis active.- Updated
onStoreRecordChangeto handle selection state using the correct ID.Grid Row (
src/grid/Row.mjs):
- Updated
createVdomandapplyRendererOutputto usegridBody.getRecordId(record)for generating DOM IDs and data attributes.Selection Models (
src/selection/grid/RowModel.mjs,src/selection/table/RowModel.mjs):
- Refactored
onNavKeyRow,onRowClick, andupdateAnnotationsto useview.getRecordId(record).- This ensures selection state persists correctly even if the underlying record ID changes (e.g., after saving a phantom record).
Outcome:
- Stable Rendering: DOM elements now use stable
internalIdby default, preventing re-renders on ID change.- Phantom Record Support: New records render correctly without ID collisions.
- Security: Database keys are no longer exposed in the DOM by default.
Status: Code committed and pushed. Ready for List/DataView refactoring (#9071).

Input from Gemini 3 Pro:
✦ Fix: Grid & Table Selection Models Adaptation for
internalIdIssue: The initial
internalIdrefactor broke selection logic because Selection Models were still relying onstore.get(id)(which expects PKs) or manual ID parsing, both of which fail withneo-record-xIDs.Key Fixes:
Robust Record Resolution (
getRowRecord,getRecord):
- Added
getRowRecord(id)toGrid.BaseModelandTable.BaseModel. This method robustly resolves records from IDs by checking: 1. PK (Fast), 2. Visible Rows (Fast), 3. Internal ID Store Scan (Slow fallback).- Added
getRecord(logicalId)toCellModel(Grid & Table) to handle logical cell ID parsing and resolution.- Refactored
GridBodyandTableBodymethods (getRecord,getRecordFromLogicalId, event firing) to use these robust lookups.Selection Model Logic Updates:
- RowModel: Updated
onNavKeyRowandonRowClickto use the new resolution helpers.- CellModel: Updated navigation logic to resolve records correctly.
- CellRowModel: Fixed a logic gap where horizontal navigation (Left/Right) failed to update row selection state. Added
onNavKeyColumnoverride to explicitly sync row selection.- CellColumnModel: Fixed a visual bug where column highlighting only updated for the clicked row. Added a conditional flush (
view.createViewData()) when column selection changes to ensure all rows re-render the highlight.Outcome: Grid and Table selection models (Row, Cell, CellColumn, CellRow, CellColumnRow) now fully support
internalIdwith correct visual updates and keyboard navigation.
Context: Following #9070 (Internal ID implementation) and the List/DataView refactor, the Grid and Table components must also be updated to support stable DOM keying.
Problem: Grids are the primary UI for CRUD operations. Handling "Phantom Records" (newly added rows before save) is a critical use case where
id: nullcurrently causes issues.Task: Update
Neo.grid.ContainerandNeo.table.Container(and their respective Views/Rows) to supportinternalId.Requirements:
useInternalIdconfig to Grid/Table Container.store.getInternalId(record)for the<tr>or<div>ID.Neo.selection.grid.ModelandNeo.selection.table.Modelto handle internal ID based lookups.Scope:
src/grid/(View, Row, Container)src/table/