Skip to content

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);
}

MethodPurpose
search(request, httpRequest)Return the rows to display — request carries searchText(), filters(), criteria(), and pageable()

Everything else is optional.


@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.


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) { ... }
}

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.

MethodDefaultEffect
pdfExportable()falseAdds an “Export PDF” button
excelExportable()falseAdds an “Export Excel” button
csvExportable()falseAdds 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.


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 @Searchable annotation (lookup fields) and the Searchable capability 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<Row> + capabilitiesAutoCrud<T>
Search box / filter bardeclare Searchable / Filterable<F>✓ (entity doubles as filters)
Custom row typewith FilteredAutoCrud<F,T>
Toolbar actions (@Toolbar)
Navigation to detail on row clickdeclare Navigable<Detail,Id>
Editing / creation / deletiondeclare 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 @Searchable field.
  • 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.


Implement UploadEnabled to add an import button to the toolbar. See Listing (reference) — Bulk import for the full pattern.