Lattice Grid Buy a licence

blog

Editable data grid in JavaScript: forms and touch

Building…
Loading a live grid…

Open any customer record in your internal tool and count the forms. The account name and plan sit in a header cell. Billing details sit in a panel below it. Notes get their own cell at the bottom, next to the grid of invoices. The user sees one record. Your code sees three forms, three validation passes and three Save buttons, and every missed one is a support ticket about a change that did not stick. That is the everyday cost of an editable data grid in JavaScript once a screen is more than a table.

Three cells, one form

The record above is exactly that screen. The header, the details and the notes are three separate forms in three cells of a shell, and a form group makes them one. Edit the plan, press Validate with the name cleared, then press Lock.

const header  = createForm(shell.getCell('header').el,  { rows: [/* name, plan */] });
const details = createForm(shell.getCell('details').el, { rows: [/* email, coupon */] });
const notes   = createForm(shell.getCell('notes').el,   { rows: [/* notes */] });

const group = createFormGroup([header, details, notes]);

group.getValue();   // every form's values, merged
group.validate();   // one answer, and focus on the first invalid field in any cell
group.setReadOnly(true);   // one lock

The group never renders anything. It reads, writes and validates across its members, and when validation fails it scrolls the first invalid field into view, whichever cell it lives in. Your Save handler stops caring how many forms the layout needed.

A rule that crosses cells

The coupon field in the details cell only makes sense on an enterprise plan, and the plan lives in the header cell. Switch the plan above and the coupon appears and disappears. Nothing wires the two forms together by hand:

{ type: 'input', name: 'coupon', label: 'Enterprise coupon',
  visibleWhen: { name: 'plan', equals: 'enterprise' } }

The details form does not own plan, so the group resolves the condition against the form that does. The same works for read-only and required rules. A hidden field keeps its value but is skipped by validation, exactly as it is in a single form.

One query feeds the whole screen

The invoices grid and the three forms above all come from one feed. The router sends the invoice rows to the grid and the account record to the forms. It does not matter which gets created first: the form fills from the router the moment it exists. Bind the group once, and save writes back through the same router.

const group = createFormGroup([header, details, notes], {
  bind: { router, route: 'ACCOUNT', key: 'A-1042', onSave: (changes) => api.save(changes) },
});

await group.save();   // one router update, one onSave call

Tick “Make the save fail” above, edit a field and press Save. The server says no, and the forms keep what the user typed, ready to retry. Nobody retypes a paragraph of notes because the network blinked.

The same app, on a phone

A planning board that works only with a mouse is half a tool. Press and hold a card to pick it up, then move it. A quick swipe before the hold still scrolls the page, so the board never traps a thumb. Try the board below on a phone or a tablet.

Building…
Loading a live grid…

The same press-and-hold now works across the views that carry your schedule:

  • Kanban. Cards, column headers and swimlane headers all drag. Escape or a second finger cancels, and holding near an edge scrolls the board.
  • Gantt. Hold a task bar to move it, or hold its right end to resize it. The drop lands on the day under your finger, and a swipe still scrolls the timeline and the task table. The progress handle and dependency-link connectors get touch-sized hit areas too.
  • Calendar. Hold an event to move or resize it in the month, week and time-grid views. A time grid scrolls while a held event sits near its top or bottom edge.
  • Scheduler. Hold an assignment or one of its edge handles, then drag it along the timeline or onto another resource.

Every edit raises the same events, in the same order, as a mouse edit, so the validation, undo and write-back you already built apply on touch without a second code path.

A JavaScript Gantt chart you can zoom with two fingers

Dragging bars is only half of planning on a tablet. Pinch out over the Gantt, the scheduler or a calendar time grid and the time axis steps one level finer. Pinch in and it steps coarser. The date under your fingers stays under them, so you do not lose your place. A two-finger drag scrolls without zooming, and one finger is never affected, so the press-and-hold drags above behave as before.

What it means for your app

One data layer now drives the forms, the grids, the boards and the schedule, and all of it edits on touch as well as with a mouse. Install it and build the screen:

npm install @toclocoinc/lattice-grid

The form builder page covers groups and binding, and the Gantt page shows the timeline. What else shipped is in the changelog.

Read next

All posts RSS