Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Elyra
Editing (inline / modal / slideover / in-cell)

Editing (inline / modal / slideover / in-cell)

The grid supports full CRUD with four presentation modes you can switch between:

  • inline — edit the whole row's cells in place, with Save/Cancel.
  • modal — a centered dialog form.
  • slideover — a right-side flyout form.
  • incell — edit a single cell in place (spreadsheet-style); optionally batch multiple edits and save them together.

Editing is server-validated and only writes columns you mark editable.

Double-click to edit. Double-clicking anywhere on a row enters edit mode. In incell mode it opens the cell you double-clicked when that cell is editable, otherwise the first editable cell of the row. In inline, modal and slideover modes it opens the row editor.

Server side

Mark columns editable with validation rules (the write allow-list):

Grid::table('sales')->key('id')
    ->column('status', editable: true, editor: 'select',
        rules: 'required|in:pending,paid,shipped,delivered,returned')
    ->column('qty', type: 'number', editable: true, rules: 'required|integer|min:0')
    ->column('revenue', type: 'number', editable: true, rules: 'required|numeric');

Expose a mutation endpoint (Vue/Svelte) or let Livewire call it directly:

use Elyra\DataGrid\Protocol\GridMutation;

Route::post('/api/sales/mutate', fn (Request $r) =>
    salesGrid()->mutate(GridMutation::fromArray($r->all())));

mutate() returns a GridMutationResult:

{ "ok": true, "id": 42, "row": { "id": 42, "status": "paid", ... } }
{ "ok": false, "errors": { "status": ["The selected status is invalid."] } }

Validation. On create, every editable column's rules are applied — so required fires even for fields the client omitted. On update, only the submitted fields are validated (partial updates). Only columns you declared editable are ever written; anything else in the payload is dropped.

Authorization (secure-by-default). mutate(), mutateBatch() and export() refuse to run until you configure authorization — either declare a policy with authorize(), or explicitly opt out with withoutAuthorization() (use the latter only when the surrounding route/controller/policy already guards the operation). Without one of those, the call throws a GridException before touching the DB.

The authorize() callback receives the ability (create | update | delete | export) and the request/mutation, and must return true; returning false throws an unauthorized GridException. Reads (respond()) are not gated here, so protect the route/policy for those.

Grid::table('sales')->key('id')
    ->authorize(fn (string $ability, $ctx) => match ($ability) {
        'delete' => auth()->user()?->can('delete', Sale::class) ?? false,
        'export' => auth()->user()?->can('export', Sale::class) ?? false,
        default  => auth()->check(),
    });

Livewire

class SalesGrid extends DataGrid
{
    public bool $editable = true;
    public string $editMode = 'slideover';   // 'inline' | 'modal' | 'slideover'
    // ... definition() + columns() (with editor/options) + optional aggregates()
}

The grid adds a command column (edit / delete), a New button, and renders the chosen editor for each editable column. Validation errors appear inline in the form. Persistence goes straight through GridDefinition::mutate().

Vue / Svelte

Provide an editing option with a mutate function and the mode:

const options = {
  /* columns with editable/editor/options ... */
  editing: {
    mode: 'slideover',            // 'inline' | 'modal' | 'slideover'
    mutate: async (m) => (await fetch('/api/sales/mutate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
      body: JSON.stringify(m),
    })).json(),
  },
};

Programmatic (client core):

grid.beginEdit(rowId);    // edit an existing row
grid.beginCreate();       // new row
grid.setDraft('status', 'paid');
await grid.commitEdit();  // validates + persists via mutate()
grid.cancelEdit();
await grid.removeRow(rowId);
grid.isEditing(rowId);

On success the edited row updates in place; create/delete invalidate the cached total and refetch.

Inline editing

In inline mode, clicking Edit turns the row's editable cells into inputs (using each column's editor), with Save / Cancel in the command column. Press Enter to save.

In-cell & batch editing

incell mode is spreadsheet-style: double-click an editable cell to edit it in place. Press Enter or blur to commit, Escape to cancel.

  • Immediate (default): each committed cell persists straight away (one update mutation for that field).
  • Batch (batch: true): committed cells accumulate as unsaved changes, highlighted in the grid. A toolbar Save (n) / Cancel bar appears; Save persists every dirty row (one update mutation per row) and Cancel discards them.
// Vue / Svelte
editing: { mode: 'incell', batch: true, mutate }

Bulk mutations (one request)

By default a batch Save and a clipboard paste send one update mutation per affected row. For a client-driven grid (Vue/Svelte) that means N HTTP calls. Provide an optional mutateBatch to collapse them into a single request:

editing: {
  mode: 'incell', batch: true,
  mutate: (m) => post('/api/sales/mutate', m),
  mutateBatch: async (mutations) =>
    (await post('/api/sales/mutate-batch', { mutations })), // GridMutationResult[]
}
use Elyra\DataGrid\Protocol\GridBatchMutation;

Route::post('/api/sales/mutate-batch', fn (Request $r) =>
    salesGrid()->mutateBatch(GridBatchMutation::fromArray($r->all())));

mutateBatch() runs the whole batch in one transaction (a thrown or unauthorized mutation rolls it back; per-row validation failures come back as ok: false without rolling back) and returns one GridMutationResult per input, in order. When mutateBatch is omitted the grid falls back to sequential mutate calls, so it's fully backward-compatible. (Livewire already batches server-side in a single request.)

// Livewire
public string $editMode = 'incell';
public bool $batchEdit = true;

Unsaved cells are overlaid on the displayed data and flagged with a dg-cell-dirty style, so users see pending edits before saving. Client-core exposes the machinery for custom UIs: commitCell, isDirty, dirtyValueOf, cellValue, dirtyRowCount, saveBatch, cancelBatch.

Editors

text, number, date, select (uses options), and checkbox. See Columns.

Clipboard paste

With editing enabled, paste tab-separated data (e.g. from a spreadsheet) starting at the active cell — it maps onto editable columns and persists each affected row. See Selection & keyboard.