Recipe: A list screen with actions
Goal: the most common screen in any app — a table of things with a toolbar to add, and per-row actions to edit or remove. This recipe wires displaying data and running commands together.
The shape
Section titled “The shape”- A live table reads an observable query, so it reflects changes the instant a command lands.
- A toolbar button opens an “add”
CommandDialog. - Row actions open edit/remove command dialogs for the selected item.
import { DataPage, MenuItem } from '@cratis/components/DataPage';import { Column } from 'primereact/column';import { useDialog } from '@cratis/arc.react/dialogs';import { AllAuthors } from './Author'; // observable query proxyimport { AddAuthor } from './AddAuthor'; // CommandDialog from the form recipe
export const Authors = () => { const [AddAuthorDialog, showAddAuthor] = useDialog(AddAuthor);
return ( <> <DataPage title="Authors" query={AllAuthors} emptyMessage="No authors yet"> <DataPage.MenuItems> <MenuItem label="Add author" icon="pi pi-plus" command={() => showAddAuthor()} /> </DataPage.MenuItems> <DataPage.Columns> <Column field="name" header="Name" sortable /> </DataPage.Columns> </DataPage> <AddAuthorDialog /> </> );};Why it stays simple
Section titled “Why it stays simple”You never write the glue between the write and the read: the “add” command appends an event, the projection updates the read model, and the observable table re-renders — automatically. There’s no “refresh the list after saving” code because there’s nothing to refresh.
- Edit and remove are just more commands. Model them as commands (
RenameAuthor,RemoveAuthor) and open them in dialogs the same way; the table updates itself. - Keep the table’s read model specialized. The list query and a detail query can read different, purpose-built read models — don’t force one model to serve every screen.
- For a plain table without the detail panel, use
DataTableForObservableQuerydirectly.
- Building a form and Displaying data — the two halves in detail.
- Build a full-stack feature — the backend slice this screen sits on.