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

OptionEffect
resources.all (required)Source of the entry list. A URL (GET, JSON array) or a function (callback) => callback(entries) for custom loading.
resources.selfBase 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.
allowCreateRender the create row in <tfoot>. Default true.
allowEditClicking a row (or its Edit button) switches it to inline editing. Default true.
allowDeletePer-row Delete button. Default true.
autoFocusAfter 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'.
noEntriesMsgText shown when the table is empty. Default is localized via Neura.i18n (key noEntries).
loadingMsgText next to the loading spinner. Default localized (key loading).

Columns

FieldEffect
id (required)The entry property this column renders and edits.
headerHeader cell text. Falls back to id.
allowEditfalse 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

MemberDescription
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):

EventFires
neura-restfultable-initializedAfter every successful load: initial and each reload().
neura-restfultable-row-addedAfter a create round-trips (POST succeeded).
neura-restfultable-row-updatedAfter an edit round-trips (PUT succeeded). entry is the merged result.
neura-restfultable-row-removedAfter 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.

RequestExpectation
GET resources.allJSON array of entries.
POST resources.selfBody: 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).