pub struct Table<M> { /* private fields */ }Expand description
Table description of a Resource’s list view declaring columns and how they map to queries.
A clone shares every column, filter and projection.
The list keeps its whole state in the URL: ?tab= picks a tab, ?q= searches,
?sort= and ?dir= sort, ?page=, ?per_page=, ?after= and ?before= page,
?f.<name>= filters and ?group_by= groups. Each row is keyed by its record’s primary key,
which the action URLs and bulk checkboxes carry; a composite key has no URL form, so its rows
render without row actions or bulk selection.
- Search matches a row any searchable column contains the term in. It escapes
%and_, trims the term and caps it at 128 characters, and matches case-insensitively: PostgreSQL runs it asILIKE, SQLite (for ASCII) and MySQL (under its default collations) asLIKE. - Without a
?sort=naming a sortable column, the rows sort by thedefault_sort, else by the first sortable column, ascending. The primary key breaks ties, so a sort over duplicate values pages deterministically, and a page number past the last page reads as the last. - A filter that cannot apply, an unknown name, a value the filter rejects or one past the limits, is never dropped silently: the list shows a warning banner naming it, and the export answers 400. At most 32 filters apply, each with at most 64 values and parts, each name, part and value at most 256 bytes. Active filters combine with AND.
?group_by=groups the rows by one of the table’s groupings, which orders them by its field ahead of the sort, so a group runs on across pages; one the table does not declare is ignored.- The list updates in place: a search, filter, sort or page reruns the page through the runtime, keeping focus and scroll, and without JavaScript its links and its search form load the page. A table that fails to load shows an error state with a retry link in place of the rows.
When the policy allows DeleteAny, or RunAny for an action placed on the bulk bar, the list
adds a checkbox column and a bulk bar; a row that no delete or bulk action allows gets no
checkbox. A bulk delete takes at most Limits::bulk_records, 400
by default, runs in one transaction and skips the
records the policy refuses Delete. Both deletes ask first through a confirmation dialog,
which needs JavaScript; a POST not confirmed through it answers 400. Each lands back on the
list as it was left.
§Export
GET {list}/-/export returns the list as a CSV file named {slug}.csv, with the current
search, filters and sort applied and the relations the columns include loaded: the rows the
list shows, each of them, whatever the policy answers View of it. It delivers at most
Limits::export_rows, 10,000 by default, and answers 413 rather
than a truncated file when more match. It writes each column’s formatted value, a date column’s
stored value, and escapes a cell a spreadsheet would read as a formula; ?bom=1 prefixes the
file with a byte-order mark for Excel.
Implementations§
Source§impl<M> Table<M>
impl<M> Table<M>
Sourcepub fn group_by<T>(self, lens: Lens<M, T>) -> Self
pub fn group_by<T>(self, lens: Lens<M, T>) -> Self
Offer grouping the rows by the field lens reads, under the field’s name. Call it once
per field a reader may group by; ?group_by= picks one, and the list groups by none
without it.
use tablo_core::{Table, TextColumn, lens};
Table::new(TextColumn::new(lens!(Post.title)))
.group_by(lens!(Post.status))
.group_by(lens!(Post.author));A grouping orders the rows by its field ahead of the sort, so each group runs on across pages, and its header shows its field’s value, or “No value” over the rows holding none. Two groupings by one field are a declaration error.
Source§impl<M> Table<M>
impl<M> Table<M>
Sourcepub fn row_color(
self,
color: impl Fn(&M) -> Option<Color> + Send + Sync + 'static,
) -> Self
pub fn row_color( self, color: impl Fn(&M) -> Option<Color> + Send + Sync + 'static, ) -> Self
Tint each row in color(row), such as an overdue invoice in Color::Danger; a row it
gives None stays plain.
use tablo_core::{Color, Reads, Table, TextColumn, lens};
Table::new(TextColumn::new(lens!(Invoice.number)))
.row_color(|invoice: &Invoice| invoice.overdue.then_some(Color::Danger))
.row_color_reading(Reads::of(lens!(Invoice.overdue)));The closure may read any field, so a resource that
restricts a field refuses the table until
row_color_reading declares the ones it reads, and a user who
may not view one of them sees every row plain.
Sourcepub fn row_color_reading(self, reads: Reads<M>) -> Self
pub fn row_color_reading(self, reads: Reads<M>) -> Self
Declare the fields the row_color reads. A color declaring it reads
none of the row’s relations, beside a column loading one, reads a copy of the row without
them, which ModelColumns builds: it then mounts beside a column
loading a record of a model a resource restricts a field of.
Source§impl<M> Table<M>
impl<M> Table<M>
Sourcepub fn default_sort<T>(self, path: impl Into<Path<M, T>>) -> Selfwhere
M: Model,
pub fn default_sort<T>(self, path: impl Into<Path<M, T>>) -> Selfwhere
M: Model,
Sort by the field path names, smallest first, when the URL names no sortable column; by
the first sortable column, ascending, by default. The field needs no column: the rows
sort by it all the same, and no header marks the sort.
use tablo_core::{Table, TextColumn, lens};
Table::new(TextColumn::new(lens!(Post.title)).sortable()).default_sort(lens!(Post.title));Sourcepub fn default_sort_desc<T>(self, path: impl Into<Path<M, T>>) -> Selfwhere
M: Model,
pub fn default_sort_desc<T>(self, path: impl Into<Path<M, T>>) -> Selfwhere
M: Model,
Sort by the field path names, largest first, when the URL names no sortable column, as
default_sort does ascending.
use tablo_core::{DateColumn, Table, TextColumn, lens};
// The newest posts first.
Table::new((
TextColumn::new(lens!(Post.title)).sortable(),
DateColumn::new(lens!(Post.published_at)).sortable(),
))
.default_sort_desc(lens!(Post.published_at));Sourcepub fn default_sort_by(self, sort: Sort) -> Self
pub fn default_sort_by(self, sort: Sort) -> Self
Sort by the sortable column sort names when the URL names none: the default for a
column of your own, whose name no field path spells.
A field’s column takes default_sort, whose path a rename fails
to compile.
use tablo_core::{Sort, Table, TextColumn, lens};
// "revenue" is the name a column of your own returns.
Table::new(TextColumn::new(lens!(Post.title))).default_sort_by(Sort::desc("revenue"));A name the table declares no sortable column for is a declaration error.
Source§impl<M> Table<M>
impl<M> Table<M>
Sourcepub fn option_tabs<T>(self, lens: impl Into<Path<M, T>>) -> Self
pub fn option_tabs<T>(self, lens: impl Into<Path<M, T>>) -> Self
Offer a tab admitting every row, keyed all, then a tab per option of the Options
field lens binds, in declaration order, after the declared ones. An option’s tab is keyed
by its value and labelled by its label, admits the rows holding it, and shows their count.
use tablo_core::{Table, TextColumn, lens};
// `?tab=all`, `?tab=draft` and `?tab=published`.
Table::new(TextColumn::new(lens!(Post.title))).option_tabs(lens!(Post.status));An option whose value the field’s type does not parse is a declaration error, since its tab would admit no row.
Source§impl<M> Table<M>
impl<M> Table<M>
Sourcepub fn new(cols: impl IntoColumns<M>) -> Self
pub fn new(cols: impl IntoColumns<M>) -> Self
Declare a table of cols.
Each row is keyed by its record’s primary key, which the row’s action URLs carry.
Sourcepub fn column(self, column: impl Column<M> + 'static) -> Self
pub fn column(self, column: impl Column<M> + 'static) -> Self
Append column after the declared ones.
Sourcepub fn filters(self, filters: impl IntoFilters<M>) -> Selfwhere
M: Model,
pub fn filters(self, filters: impl IntoFilters<M>) -> Selfwhere
M: Model,
Append filters after the declared ones, such as those a field’s #[columns(filter)]
declares.
Sourcepub fn pagination(self, pagination: Pagination) -> Self
pub fn pagination(self, pagination: Pagination) -> Self
Page through the rows as pagination says; Pagination::Pages by default.
Sourcepub fn action_layout(self, layout: ActionLayout) -> Self
pub fn action_layout(self, layout: ActionLayout) -> Self
Put a row’s actions where layout says; ActionLayout::Inline by default.
Sourcepub fn per_page_options(self, options: impl IntoIterator<Item = usize>) -> Self
pub fn per_page_options(self, options: impl IntoIterator<Item = usize>) -> Self
The page sizes a reader may pick, beside the table’s own; none offers no choice.
DEFAULT_PER_PAGE_OPTIONS by default. A cursor table offers none.
Sourcepub fn page_size(self, size: usize) -> Self
pub fn page_size(self, size: usize) -> Self
Show size rows a page; DEFAULT_PAGE_SIZE by default.
Sourcepub fn declaration_errors(&self) -> Vec<DeclarationErrorKind>
pub fn declaration_errors(&self) -> Vec<DeclarationErrorKind>
What is wrong with this declaration.
Sourcepub fn per_page(&self) -> usize
pub fn per_page(&self) -> usize
The rows a page shows, as Self::page_size set them.
Sourcepub fn hide_search(self) -> Self
pub fn hide_search(self) -> Self
Render no search toolbar in the table.
Render no filter menu in the table’s toolbar.
Sourcepub fn deferred_filters(self) -> Self
pub fn deferred_filters(self) -> Self
Apply the filter menu’s controls only when its Apply button is pressed, rather than as each one changes, so a reader sets several filters before the list reruns once. A search, a sort or a tab applies the controls as they stand too, since the toolbar’s form carries them.