Listing<Filters, Row>
Listing<Filters, Row> is the base class for standalone listings that need custom filters, toolbar actions, or export — without routing to a detail view.
Unlike the orchestrators, a Listing is a self-contained component: it handles its own search and actions, but clicking a row does not navigate anywhere (unless you also implement Selector). Use it when you need a list on screen but no CRUD flow around it.
public abstract class Listing<Filters, Row> implements ListingBackend<Filters, Row>, ActionSupplierWhat you implement
Section titled “What you implement”| Method | Purpose |
|---|---|
search(searchText, filters, pageable, httpRequest) | Return the rows to display |
Everything else is optional.
Minimal example
Section titled “Minimal example”@Service@UI("/orders")public class OrderListing extends Listing<OrderFilters, OrderRow> {
private final OrderQueryService queryService;
public OrderListing(OrderQueryService queryService) { this.queryService = queryService; }
@Override public ListingData<OrderRow> search( String searchText, OrderFilters filters, Pageable pageable, HttpRequest httpRequest) { return queryService.search(searchText, filters, 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 {}The filter bar is generated from OrderFilters, the grid columns from OrderRow. Both are inferred from the generic parameters 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.).
@Service@UI("/orders")public class OrderListing extends Listing<OrderFilters, OrderRow> {
@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(...) { ... }}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 ListingBackend 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.
@Service@Scope("prototype")@Style("min-width: 40rem;")public class ProductSelector extends Listing<ProductFilters, ProductRow> implements Selector<String> {
@Override public ListingData<ProductRow> search( String searchText, ProductFilters filters, Pageable pageable, HttpRequest httpRequest) { return productService.search(searchText, filters, 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;Listing vs AutoCrud
Section titled “Listing vs AutoCrud”Listing<F,R> | AutoCrud<T> | |
|---|---|---|
| Filter bar | ✓ | ✓ |
| Custom row type | ✓ | with FilteredAutoCrud<F,T> |
Toolbar actions (@Toolbar) | ✓ | — |
| Navigation to detail on row click | — | ✓ |
| Export (PDF/Excel/CSV) | ✓ | — |
| Selector support | ✓ | — |
Use Listing when:
- You need toolbar actions or export.
- There is no detail view to navigate to.
- The listing is used as a lookup selector for a
@Searchablefield. - The listing is embedded inside a form via a
Callable<?>field.
Use AutoCrud<T> + @ReadOnly (or FilteredAutoCrud<Filters,T> + @ReadOnly) when you need the same filter flexibility but also want row-click navigation to a detail view.
Bulk import
Section titled “Bulk import”Implement UploadEnabled to add an import button to the toolbar. See ListingBackend — Bulk import for the full pattern.
- FilteredAutoCrud — when you need both separate filter types and detail navigation
- Listing row actions — per-row
ColumnActionandColumnActionGroup - ListingBackend reference — full API reference for
ListingData,Pageable,Page, and export modules