Show data on a page
A page is a route, an access rule, and a list of regions. A region reads one table and renders it in one shape. This guide covers the four regions that only read.
List rows in a table
Section titled “List rows in a table”report is the region for a screen somebody works in. It gives you search, filters, sortable
columns, pagination, and an empty state.
page members "Members" { route: /members access: board
report of members "All members" { columns: last_name, first_name, email, joined_on search: last_name, first_name, email filter: fee_group, active sort: last_name page_size: 25 empty: "No members yet." }}Every column in columns is sortable. The reader sorts by clicking a heading, and the link
works with JavaScript turned off.
Search is case-insensitive and folds letters beyond ASCII, so a search for öztürk finds Öztürk. The
engine compares the text itself rather than leaving it to the database’s byte order.
Add page_size: for any table that grows. Without it, a report renders every row it can read.
Open one row from the list
Section titled “Open one row from the list”row -> names the page to open when somebody clicks a row.
report of members "All members" { columns: last_name, first_name, email row -> member}The target page carries the row in its route:
page member "Member" { route: /members/{member} new_route: /members/new access: board
form of members { fields: first_name, last_name, email, fee_group actions: save, delete }}new_route: gives the same page a second address for creating a row. The engine cannot guess
a URL segment, so you write both.
For a second link per row, use row_action:
row_action "Signups" -> event_signupsShow one record
Section titled “Show one record”detail renders one row as a record rather than as a table row. Use it for an article, a
member file, or any page that is about one row.
A detail can show a multi or a list, because a metadata line has room for a set of
links. A report cannot, because a cell holds one value.
page post "{:post.title}" { route: /news/{slug} access: public
data post of posts where status.public
detail of posts { fields: published_at, author, tags body: body }}Two things are happening here.
data post of posts binds the row that the URL names. The name post is then available to
the rest of the page.
{:post.title} interpolates the bound row into the page title. Any text an author writes can
interpolate a value this way.
Show a public list
Section titled “Show a public list”list renders the same rows as a report, in a layout for reading. It has no search box and no
filters.
Use a list for a page a visitor reads and a report for a screen a member works in. They
make different promises, which is why they are two regions and not one region with a flag.
Count rows on a dashboard
Section titled “Count rows on a dashboard”stats groups a table and renders the totals as tiles.
page dashboard "Dashboard" { route: / access: board
stats of members "Members by status" { group_by: active }
stats of posts "Posts by state" { access: editor group_by: status }}The second region carries its own access:. An editor sees both tiles. A board member without
the editor role sees only the first, and the second is absent rather than refused.
Narrow a region to some rows
Section titled “Narrow a region to some rows”where: adds a fixed condition on top of the access rule.
report of events "Coming up" { columns: name, starts_at, place where: starts_at >= now sort: starts_at}A condition can compare columns, use now and today, walk a reference, and ask whether a
related set is empty. It cannot call a function you write, because a condition has to become
SQL that the engine can reason about.
NOTE: On a page that binds a row, a region over a different table must reference that row
in its where:, such as where: event = :event.id. The compiler refuses a region that does
not, because nothing else relates the two.