Bulk actions
Status: ✅ Implemented
Intent
Section titled “Intent”Operate on many records at once from a listing — approve 50 invoices, deactivate a batch of products, assign a set of tickets — without visiting each detail view. The listing offers row selection (checkbox column with select-all) and toolbar buttons that act on the selection.
Solution
Section titled “Solution”Annotate a method of your AutoCrud (or any Crud) with @ListToolbarButton. It renders as a
toolbar button over the listing, and a List<Row> parameter receives the typed selection:
@UI("/invoices")public class InvoicesCrud extends AutoCrud<Invoice> {
@Override public CrudStore<Invoice> store() { return store; }
@ListToolbarButton @Label("Aprobar seleccionadas") public Message approve(List<Invoice> selection, HttpRequest httpRequest) { selection.forEach(invoice -> service.approve(invoice.id())); return new Message(selection.size() + " facturas aprobadas"); }}- The selection column is enabled by default on every crud listing (opt out by overriding
selectionEnabled()tofalse). - The frontend keeps the selected rows in the component state (
crud_selected_items); the framework hydrates them typed into anyList<Row>parameter of the method. - By default the button requires a selection: clicking it with nothing selected shows a
notice instead of calling the server. Opt out with
@ListToolbarButton(rowsSelectedRequired = false)for toolbar actions that don’t need rows. @ListToolbarButton(confirmationRequired = true)asks for confirmation first — combine both for destructive bulk operations.- The button label follows the usual rules:
@Label(translated) or the humanized method name. - Return value semantics are the standard action ones: a
Messagetoasts, aPageBannershows a banner,void/nullsimply re-runs the search so the listing reflects the changes.
Built-in bulk delete
Section titled “Built-in bulk delete”Every crud listing already ships a bulk Delete (selection + confirmation) wired to
CrudStore.deleteAllById(List<IdType>) — no code needed.
Other servers
Section titled “Other servers”Same wire contract from .NET and Python:
public class InvoicesCrud : AutoCrud<Invoice>{ [ListToolbarButton] [Label("Approve selected")] public Message Approve(List<Invoice> selection) => new Message($"{selection.Count} approved");}class InvoicesCrud(AutoCrud[Invoice]): @list_toolbar_button(label="Approve selected") def approve(self, selection: list[Invoice]): return Message(f"{len(selection)} approved")Principles served
Section titled “Principles served”- Workflow over screens — the operation happens where the user already is: the listing.
- Recoverability — destructive bulk actions can require confirmation.
Related
Section titled “Related”- Filters & Listing — finding the rows to act on
- Task queue — when each item needs individual attention instead