Listing<Row>
Listing<Row> is the interface behind every standalone listing: implement its single search(SearchRequest, HttpRequest) method and Mateu renders a sortable, paginated grid of Row objects. Search box, filter bar, and every interaction (navigation, editing, creation, deletion) are capabilities you declare on the same class — see Listings and capabilities for the full model.
This page covers the standalone-listing extras: toolbar actions, export, and using a listing as a lookup selector.
public interface Listing<Row> extends ActionHandler, ActionSupplier { ListingData<Row> search(SearchRequest request, HttpRequest httpRequest);}What you implement
Section titled “What you implement”| Method | Purpose |
|---|---|
search(request, httpRequest) | Return the rows to display — request carries searchText(), filters(), criteria(), and pageable() |
Everything else is optional.
Minimal example
Section titled “Minimal example”@UI("/orders")@Trigger(type = TriggerType.OnLoad, actionId = "search")public class OrderListing implements Listing<OrderRow>, Searchable, Filterable<OrderFilters> {
private final OrderQueryService queryService;
public OrderListing(OrderQueryService queryService) { this.queryService = queryService; }
@Override public ListingData<OrderRow> search(SearchRequest request, HttpRequest httpRequest) { return queryService.search(request.searchText(), filters(request), request.pageable()); }}public record OrderFilters( OrderStatus status, LocalDate from, LocalDate to) {}public record OrderRow( @PrimaryKey String id, String customer, LocalDate date, OrderStatus status, BigDecimal total) implements Identifiable {}Searchable shows the free-text search box, Filterable<OrderFilters> generates the filter bar from OrderFilters (and filters(request) returns the hydrated instance, typed), and the grid columns come from OrderRow — all inferred via reflection.
Toolbar actions
Section titled “Toolbar actions”Annotate methods with @Toolbar to add buttons to the listing toolbar. The method receives the HTTP request and can return any action result (message, navigation, etc.).
@UI("/orders")public class OrderListing implements Listing<OrderRow>, Searchable {
@Toolbar public Object exportSelected(HttpRequest httpRequest) { var selectedIds = httpRequest.getSelectedIds(); exportService.export(selectedIds); return new Message("Export started for " + selectedIds.size() + " orders"); }
@Override public ListingData<OrderRow> search(SearchRequest request, HttpRequest httpRequest) { ... }}Export
Section titled “Export”Override any of the three export methods to add the corresponding button to the toolbar. The framework reuses search() to gather the data — no additional implementation needed.
| Method | Default | Effect |
|---|---|---|
pdfExportable() | false | Adds an “Export PDF” button |
excelExportable() | false | Adds an “Export Excel” button |
csvExportable() | false | Adds an “Export CSV” button |
@Override public boolean pdfExportable() { return true; }@Override public boolean excelExportable() { return true; }@Override public boolean csvExportable() { return true; }Excel and PDF require optional modules on the classpath. See Listing (reference) for the dependency details.
Selector support
Section titled “Selector support”When a Listing also implements Selector<IdType>, it can be used as the search modal for a @Searchable field. Clicking a row closes the modal and sets the field value.
@Trigger(type = TriggerType.OnLoad, actionId = "search")@Style("min-width: 40rem;")public class ProductSelector implements Listing<ProductRow>, Searchable, Filterable<ProductFilters>, Selector<String> {
private String _fieldId;
@Override public String fieldId() { return _fieldId; }
@Override public Selector withFieldId(String fieldId) { _fieldId = fieldId; return this; }
@Override public ListingData<ProductRow> search(SearchRequest request, HttpRequest httpRequest) { return productService.search(request.searchText(), filters(request), request.pageable()); }
@Override public SelectedItem<String> selected(HttpRequest httpRequest) { var row = httpRequest.getClickedRow(ProductRow.class); return new SelectedItem<>(row.id(), row.name()); }}// In a form that uses the selector@Searchable(selector = ProductSelector.class, label = ProductLabelSupplier.class)String productId;Note: the
@Searchableannotation (lookup fields) and theSearchablecapability interface (search box on a listing) are different things that share a name — the annotation goes on the form field, the interface on the listing class.
Listing vs AutoCrud
Section titled “Listing vs AutoCrud”Listing<Row> + capabilities | AutoCrud<T> | |
|---|---|---|
| Search box / filter bar | declare Searchable / Filterable<F> | ✓ (entity doubles as filters) |
| Custom row type | ✓ | with FilteredAutoCrud<F,T> |
Toolbar actions (@Toolbar) | ✓ | — |
| Navigation to detail on row click | declare Navigable<Detail,Id> | ✓ |
| Editing / creation / deletion | declare Editable / Creatable / Deletable | ✓ (or @Not* / @ReadOnly) |
| Export (PDF/Excel/CSV) | ✓ | — |
| Selector support | ✓ | — |
Use Listing when:
- You need toolbar actions or export.
- The rows come from a query service and differ from your domain entity.
- The listing is used as a lookup selector for a
@Searchablefield. - The listing is embedded inside a form via a
Callable<?>field. - You want only some interactions — declare exactly the capabilities you need.
Use AutoCrud<T> (or FilteredAutoCrud<Filters,T>) when a single entity backed by a CrudStore is the right model for every screen.
Bulk import
Section titled “Bulk import”Implement UploadEnabled to add an import button to the toolbar. See Listing (reference) — Bulk import for the full pattern.
- Listings and capabilities — the capability model: search, filters, navigation, editing, creation, deletion
- FilteredAutoCrud — when you need both separate filter types and detail navigation
- Listing row actions — per-row
ColumnActionandColumnActionGroup - Listing reference — full API reference for
ListingData,Pageable,Page, and export modules