Tables and Record Lists
A theme never ships its own grid library: record lists run on the core Table component, and everything else is plain markup.
Overview
Two paths cover every table a theme needs. The question is not how many rows you have, but what the rows are.
Table with 'renderer' => 'list'.
Prerequisites
- Your theme is active, so
Theme::active()resolves to its directory. - You know the component itself; this article only covers the client-side list mode.
- A
tables/directory exists in your theme root.
Structure
Four layers, and each one owns a different file. The core controller already exists for the pages listed above, so a theme usually writes only the second and third.
templates/website/{Theme}/tables/{name}.php.
views/account/{name}.tpl.
list.js: client search, filter, paging, counter text, empty state.
Step by Step
Adding a record list
- Copy the preset for the page from another theme into your own
tables/directory. The file name matches the table name the controller passes. - Rewrite the markup inside
setRowRender()in your own design language, and keep the data and the order untouched. - Mirror that markup in the view's
{foreach}branch, so the first page and the later pages look the same. - Give the container its three text bridges, then load the page and page through it. The counter now reads in your language and the empty state appears when a filter matches nothing.
Reference
The list mode changes three things about the component.
templates/admin/tables/; on the site it is Theme::active()->dir()."tables". A preset placed in the admin directory is never found.
$row["html"]. The column matrix under $row["data"] is unused in this mode.
justBody returns the same thing as the full call.
The controller finishes the work the template cannot do, because a theme template may not call the component under the sandbox:
data-ajax.
Below the threshold every row is already in the first answer, data-ajax is absent and searching, filtering and paging all happen in the browser. Above it the same address serves one page at a time. Your markup is identical in both, which is why the row attributes below carry their own values.
The container carries the wording the interaction script needs:
Example
/** @var \WISECP\Components\Table $table */
if (!isset($table)) return;
$table->setRowRender(function ($row) {
$service = $row["model"];
$name = htmlspecialchars($service["name"]);
$status = $service["status"];
$danger = in_array($status, ['suspended', 'expired'], true) ? ' is-danger' : '';
$link = LinkGenerator::client("service-detail", [$service["id"]]);
// The whole row, in your own markup. No <td> anywhere.
$row["html"] = '<article class="list-item' . $danger . '">'
. '<a href="' . $link . '">' . $name . '</a>'
. '<span class="badge">' . $status . '</span>'
. '</article>';
return $row;
});
<div class="list-rows" data-noun="services"
data-txt-count="{lang key='website/index/list-count'}"
data-txt-nores="{lang key='website/index/list-nores'}"
data-txt-noun="{lang key='website/services/noun'}">
{$rows nofilter}
</div>
Pitfalls
The lookup asks whether the file exists and moves on when it does not. Nothing is logged, no error appears, and the list comes back empty. If your rows are missing, check the path before you check your code.
The view builds page one, and the preset builds every page after it. When the two drift apart, the rows change shape as soon as the visitor pages forward.
No records at all replaces the toolbar, the list and the pager together, and it may offer a way to create the first record. A filter that matches nothing lives inside the container and offers a way to clear the filter. Writing only one of them leaves the visitor stuck.
Presets live in the theme, so each theme carries its own copy of all seven. A missing one costs that theme an empty list.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.