AJAX lists
Open the live demo at /documentation/examples/run/lists.
Introduction
A growing table is a backend problem and a frontend problem. PHP paginates with paginate(), encrypts row ids, and returns HTML fragments. dotapp.js patches #listInner after $dotapp().load(). Row toggle and delete are buttons, not one <fo-rm> per row.
- Secure forms — the save form on the same page
- Custom JS library — reusable widgets around the same
load()transport
Routes
GET renders the page. POST endpoints return ajaxReply JSON for the list, save, delete, and toggle actions:
Router::get($pair($p . '/lists'), 'Examples:Lists@page!', Router::STATIC_ROUTE);
Router::post($pair($p . '/lists/list'), 'Examples:Lists@list!', Router::STATIC_ROUTE);
Router::post($pair($p . '/lists/save'), 'Examples:Lists@save!', Router::STATIC_ROUTE);
Router::post($pair($p . '/lists/delete'), 'Examples:Lists@delete!', Router::STATIC_ROUTE);
Router::post($pair($p . '/lists/toggle'), 'Examples:Lists@toggle!', Router::STATIC_ROUTE);
PHP list + pager
Search runs from three characters. Ids leave PHP as Crypto::encrypt($id, 'Examples.item.id'). Decrypt with the same extra key. Failure is === false.
$result = DB::module('RAW')->q(function ($qb) use ($q, $useSearch) {
$qb->select(['id', 'title', 'active', 'created_at'])->from('examples_items')->orderBy('id', 'DESC');
if ($useSearch) {
$esc = str_replace(['\\', '%', '_'], ['\\\\', '\%', '\_'], $q);
$qb->where('title', 'LIKE', '%' . $esc . '%');
}
})->paginate(10, $page);
The fragment view lists-inner.view.php is what JS puts into #listInner. Return it in html next to status and message.
JavaScript
Cover the list with .ex_busy until load() finishes. Patch a child, not the overlay wrapper. Delete uses a graphical dialog — never alert() or window.confirm().
$dotapp().load(listUrl, "POST", { page: page, q: q },
function (raw) {
var reply = $dotapp().parseReply(raw);
if (reply && reply.html) $dotapp("#listInner").html(reply.html);
listDone();
},
function () { listDone(); }
);
The page script lives at /app/modules/Examples/assets/js/lists.js.
Live demo
/documentation/examples/run/lists
needs MySQL in app/config.php. Without a database the page explains that the demo is not ready.