RESTful table
Summary
An editable CRUD table bound to a REST endpoint: the core of
AUI's Backbone-based experimental RestfulTable, re-implemented
with fetch and zero dependencies. Rows load from
resources.all; clicking a row (or its Edit button)
switches it to inline editing (PUT self/{id}); the
footer row creates entries (POST self); Delete sends
DELETE self/{id}. Enter submits,
Esc cancels an edit.
Example
This demo mocks /api/devices in-page, so create,
edit, and delete all round-trip through real fetch calls.
JavaScript API
const rt = Neura.restfulTable('#devices', {
resources: { all: '/api/devices', self: '/api/devices' },
columns: [
{ id: 'name', header: 'Name' },
{ id: 'ip', header: 'IP address', allowEdit: false },
{ id: 'platform', header: 'Platform',
readView: (value, entry) => `<em>${value}</em>` },
],
noEntriesMsg: 'No devices yet',
});
Neura.restfulTable(elOrSelector, options) is a
singleton per element: repeated calls return the same
instance (options are read on first call). The target
<table> gets the neura-table +
neura-restfultable classes and is fully rendered
by the component.
Options
| Option | Effect |
|---|---|
resources.all (required) | Source of the entry list. A URL (GET, JSON array) or a function (callback) => callback(entries) for custom loading. |
resources.self | Base URL for mutations: POST self (create), PUT self/{id} (update), DELETE self/{id}. Required unless create/edit/delete are all disabled. |
columns (required) | Array of column definitions (see below), in display order. An operations column (Edit/Delete buttons) is appended automatically. |
allowCreate | Render the create row in <tfoot>. Default true. |
allowEdit | Clicking a row (or its Edit button) switches it to inline editing. Default true. |
allowDelete | Per-row Delete button. Default true. |
autoFocus | After creating an entry, focus the first input of the fresh create row (for entering many rows in a run). Default false. |
createPosition | 'bottom' (default) or 'top' - where newly created entries are inserted. Note: AUI's default was 'top'. |
noEntriesMsg | Text shown when the table is empty. Default is localized via Neura.i18n (key noEntries). |
loadingMsg | Text next to the loading spinner. Default localized (key loading). |
Columns
| Field | Effect |
|---|---|
id (required) | The entry property this column renders and edits. |
header | Header cell text. Falls back to id. |
allowEdit | false makes the column read-only when editing an existing entry; the create row still renders an input for it. Default true. |
readView | (value, entry) => htmlString: custom rendering for the read cell. The string is inserted as HTML, so escape user data yourself; without it the value renders as plain text. |
Methods
| Member | Description |
|---|---|
rt.reload() | Re-fetch resources.all and re-render (async; also runs on construction). Shows the loading row while in flight; a failed load renders an inline "Failed to load" row. |
rt.getEntries() | Copy of the current entry array. |
rt.destroy() | Empty the table, remove the component classes, forget the instance. |
Events
CustomEvents dispatched on the table element (bubbling), with
detail: { entry, table }
(entry is absent on initialized):
| Event | Fires |
|---|---|
neura-restfultable-initialized | After every successful load: initial and each reload(). |
neura-restfultable-row-added | After a create round-trips (POST succeeded). |
neura-restfultable-row-updated | After an edit round-trips (PUT succeeded). entry is the merged result. |
neura-restfultable-row-removed | After a delete round-trips (DELETE succeeded). |
document.querySelector('#devices')
.addEventListener('neura-restfultable-row-added', (e) => {
console.log('created', e.detail.entry);
});
Keyboard
Inside an edit or create row, Enter in any input submits and Esc cancels the edit (the create row has no cancel; it just stays). The first input is focused when a row enters edit mode.
Server contract
Entries are JSON objects and must carry an id (used in URLs and row tracking). All requests send Content-Type/Accept: application/json.
| Request | Expectation |
|---|---|
GET resources.all | JSON array of entries. |
POST resources.self | Body: the create-row values. Response may echo the stored entry; echoed fields win (this is how the server-assigned id gets in). An empty response keeps the submitted values. |
PUT resources.self/{id} | Body: the full entry merged with the edited values. Response may echo the stored entry; echoed fields win. |
DELETE resources.self/{id} | Any 2xx (typically 204 with no body). |
A full exchange, using the demo's device documents; every
column id is simply a property of the entry object:
// GET /api/devices → 200 - the entry collection
[
{ "id": 1, "name": "nsys-pi", "ip": "10.100.20.50", "platform": "Raspberry Pi" },
{ "id": 2, "name": "nsys-arduino", "ip": "10.200.20.51", "platform": "Arduino" }
]
// POST /api/devices - request body: the create-row values (no id yet)
{ "name": "nsys-linux", "ip": "10.200.20.52", "platform": "Debian / Ubuntu Linux" }
// → 200/201 response: echo of the stored entry - the server-assigned
// id arrives here (echoed fields win over the submitted ones)
{ "id": 3, "name": "nsys-linux", "ip": "10.200.20.52", "platform": "Debian / Ubuntu Linux" }
// PUT /api/devices/3 - request body: the full entry merged with the edits
{ "id": 3, "name": "nsys-linux", "ip": "10.200.20.99", "platform": "Debian / Ubuntu Linux" }
// → 200 response: echo of the stored entry (or an empty body to
// keep the submitted values as-is)
// DELETE /api/devices/3 → 204 No Content
Any non-2xx response aborts that operation: a failed create or
update leaves the form row open with the typed values, a
failed delete leaves the row in place, and a failed initial
load renders an inline error row. No dialogs are shown;
surface errors your own way by wrapping fetch or
watching your API layer.
AUI compatibility
new AJS.RestfulTable({
el: AJS.$('#devices'),
resources: { all: '/api/devices', self: '/api/devices' },
columns: [{ id: 'name', header: 'Name' }],
});
AJS.bind(AJS.RestfulTable.Events.ROW_ADDED, handler);
The shim's new AJS.RestfulTable({el, …}) accepts a
jQuery-wrapped or plain element and re-fires the DOM events
through the AJS.bind bus using
AJS.RestfulTable.Events.{INITIALIZED, ROW_ADDED,
ROW_EDITED, ROW_REMOVED}.
Not ported from AUI's Backbone version:
custom Backbone model classes,
editView/createView view classes
(Neura has the readView function instead),
allowReorder (drag ordering),
reverseOrder, deleteConfirmation,
fieldName/emptyText column extras,
submit/cancel access keys, and the per-row
EditRow/Row sub-view methods and
event sets. Also note the createPosition default
difference ('bottom' here, 'top' in
AUI).