Form Mode
A common way to use a spreadsheet in a business app is not as a spreadsheet at all: the layout is fixed, most cells are protected, and the user only fills values into a handful of boxes. Since v1.6.0 four worksheet switches cover that shape directly. Available in both editions.
All four are render and interaction settings only — none of them is stored in xlsx or JSON, so re-apply them after loadJson or reset.
const ws = grid.worksheet
ws.layoutLocked = true
ws.clickToEdit = true
ws.formNavigation = true
ws.setCellPlaceholder(2, 1, 'e.g. Taro Yamada')
Lock the layout
ws.layoutLocked = true
Stops the user changing the layout: header drag-resize of column widths and row heights, the resize cursor, double-click auto-fit, and whole-row / whole-column reorder drags are all disabled at once.
Sizing from the API still works, so your own template code is unaffected:
ws.column(1).width = 180 // still applies
Open the editor on one click
ws.clickToEdit = true
An editable cell enters edit mode on a single click — no double-click — with the caret at the end of the existing text, and the pointer shows an I-beam over such cells so it looks like the input it behaves as.
Protected cells are unaffected, and so are cell types that replace the text (checkbox, and friends) — those keep their click behaviour.
Placeholder hints
ws.setCellPlaceholder(2, 1, 'e.g. Taro Yamada')
ws.getCellPlaceholder(2, 1) // 'e.g. Taro Yamada' | null
ws.getCellPlaceholderEntries() // [{ row, column, text }, …]
ws.clearCellPlaceholders()
Draws muted hint text in an empty cell, like an HTML input’s placeholder. It:
- stays inside the cell — it never spills into the neighbour,
- is never right-aligned as a number,
- is never part of the cell value, and never appears in PDF, print, xlsx or JSON,
- shifts with row and column inserts and deletes, the way comments do.
Pass an empty string to remove one.
Walk the input cells with Enter and Tab
ws.formNavigation = true
Enter and Tab commit the cell and move to the next input cell, opening the editor there with its text selected. Shift reverses the direction.
The built-in order is a reading-order scan over editable cells that:
- skips protected cells,
- skips hidden rows and columns,
- treats a merged block as one stop, at its anchor,
- wraps at the end of a row — so the last field of a row continues into the first field of the next one.
That is what makes a form fill in end-to-end without ever reaching for the mouse.
A tab order of your own
When the app knows its own layout — a fixed report template, say — install a navigator to walk the fields deliberately and skip editable cells that are not part of the form:
const order = [
{ row: 2, column: 1 },
{ row: 2, column: 4 },
{ row: 4, column: 1 },
]
ws.setFormNavigator((from, direction) => {
const i = order.findIndex(c => c.row === from.row && c.column === from.column)
return order[i + direction] ?? null // null = stop at the end of the form
})
// Back to the built-in scan
ws.setFormNavigator(null)
Returning null commits the value and stays put, which is what you want at the end of a form.
To ask where navigation would go without moving:
const next = ws.nextInputCell(2, 1, 1) // +1 forward, -1 backward
List validation from the keyboard
Data-validation list rules can now be driven entirely from the keyboard, which is what makes a dropdown field part of the same flow:
| Key | Action |
|---|---|
| ↓ while editing | Opens the dropdown |
| Alt+↓ while the cell is selected | Opens the dropdown |
| ↑ / ↓ while open | Moves through the options |
| Enter | Commits the highlighted option |
| Escape | Closes without choosing |
The list opens at the current value (or the first entry when the cell is empty), so ↓ then Enter is a two-keystroke pick. The choice is committed through the editor, so validation rules and form navigation both still apply.
Putting it together
const ws = grid.worksheet
// 1. Protect everything, then unlock the input cells
ws.protected = true
ws.range('C3:C8').setLock('unlocked')
// 2. Form behaviour
ws.layoutLocked = true
ws.clickToEdit = true
ws.formNavigation = true
// 3. Hints
ws.setCellPlaceholder(2, 2, 'e.g. Taro Yamada')
ws.setCellPlaceholder(3, 2, 'e.g. 03-1234-5678')
// 4. A dropdown field
ws.range('C5').setValidation({ type: 'list', options: ['Tokyo', 'Osaka', 'Nagoya'] })
The user clicks the first box, types, presses Enter, and walks the whole form to the end.
Not persisted
layoutLocked, clickToEdit, formNavigation, the installed navigator and every placeholder are screen state. They are not written to xlsx or ReoGrid JSON, and a loadJson() or reset() clears them — re-apply them from the host afterwards.
Related
- Form mode demo — an application form you can fill in without touching the mouse.
- Protection — cell lock state and sheet protection, which decide what counts as an input cell.
- Data Validation — list rules and the dropdown.
- Cell Editor — editor behaviour and keyboard focus.
- Report Binding — materializing the template a form fills in.
Sorry to hear that. What could be improved?