Table events and helpers
Exports, realtime updates, the metadata, the route helpers and the events of a table view.
These are the parts of a table view that run around its rows: exports, realtime updates, the metadata it registers, the route helpers and its events.
Exports
The export action is offered only when the controller registered all three ExportRoutes. Pressing it starts a background export job, polls its status, and downloads the result; the job machinery itself is shared with any other long export and is documented in Helpers. Which columns land in the file is decided by @Exported.
Realtime
Unless realtime: false is set, a table view registers two realtime topics for its page (and for each generated sub-page), and its routes publish every create, update and delete on the first and track who has a row open on the second. Both are named after the controller's location:
import {
tableViewPresenceTopic,
tableViewRowTopic,
} from "@antelopejs/interface-dms/base/table-view";
tableViewRowTopic("/api/orders"); // "tableview:row:/api/orders"
tableViewPresenceTopic("/api/orders"); // "tableview:presence:/api/orders"
The topics are registered with RegisterPageTopic from the table view's onCreated, so they belong to the module whose page it is, as described in Realtime. A module that publishes on a table's row topic itself, such as a background job updating rows, builds the name with tableViewRowTopic rather than spelling out the prefix, and passes the tenant of the rows as tenantId.
The topics are the same for every tenant, but what is published on them is not: the routes publish each change, and each presence, for the tenant the request was made in, so the sessions of the other tenants neither refetch nor see who has a row open. The DMS decides who else receives them, with no option on the table: a table whose rows live in the schema every tenant shares (CORE_SCHEMA_NAME) publishes to every tenant, and a session on a platform page (a module's page) receives the changes of every tenant, as such a page lists the data of all of them. RealtimeMutationContext and RealtimePresenceContext carry the tenant as tenantId, absent on a table of the shared schema.
Two functions let other modules observe those mutations:
import {
registerRealtimeMutationListener,
unregisterRealtimeMutationListener,
type RealtimeMutationContext,
} from "@antelopejs/interface-dms/base/table-view";
const listener = (context: RealtimeMutationContext) => {
// context.controllerLocation, context.eventType, context.ids, …
};
registerRealtimeMutationListener(listener);
Listeners are additive side observers: they run after the DMS has published the mutation, and a listener that throws is logged, never propagated to the mutating request. A listener is released with the module that registered it, including when that module reloads in development; unregisterRealtimeMutationListener is for a module that stops listening while it stays loaded.
| Type | Shape |
|---|---|
RealtimeMutationEventType | "created" | "updated" | "deleted" |
RealtimeMutationContext | controllerLocation, rowIdKey, eventType, ids: string[], sessionId?, actor?: RealtimePresenceActor |
RealtimePresenceActor | id, displayName?, avatarUrl? |
RealtimePresenceContext | controllerLocation, rowIdKey, sessionId, rowId, actor |
The routes reach the DMS through two interface functions it implements, internal.PublishMutation and internal.AcquirePresence. They are internal: nothing outside the DMS implements them, and the core binds them to each generation of the DMS module, so a mutation made while the DMS reloads waits for the next generation instead of reaching the one being torn down.
Metadata
TableViewMeta is the metadata object the decorators write to and the builder reads from. Retrieve it with GetMetadata(controller, TableViewMeta).
| Member | Description |
|---|---|
columns | Record<string, ColumnOptions> keyed by field name. |
groups | Record<string, ColumnGroupConfig> keyed by group id. |
options | The TableViewOptions of the writing TableView, or of the last one built while none writes. |
archiveField | Field name recorded by @ArchiveField. |
controllerRowActionRules | The rules the write routes enforce: the writing table view's rowActions, archive-mode defaults included. |
controllerGuards | Guards promoted to controller-wide guards. |
componentBuilders | Every live TableView builder built on the controller, in build order. |
writingComponentBuilders | The live builders of componentBuilders that write through the data routes. |
actionPermissionIds(actionId) | The permission id the action carries on each mounted table view of the controller, on the writing one only for a write action. |
addComponentBuilder(builder, access?) | Writer the builder calls to record itself, with its TableViewAccess ("read" or "write", the default). A custom component standing in for a table view (the roles editor) records itself the same way. |
resourceFormBuilders | Every live new / edit form built over the controller, in build order: the ResourceForm blocks and the forms each table view opens. |
addResourceFormBuilder | Writer the form builder calls to record a form that submits to the controller. |
writingComponents | Every live component that submits to the controller's write routes: its writing table view and its new / edit forms, including the ones a page-mode table view mounts on its form sub-pages. A native file staged by any of them is accepted on save; one staged by any other component is refused. |
inherit(parent) | Called by GetMetadata for a derived controller: copies the parent's columns (each with its own copy of its data type), groups, options and archiveField, never its table views or forms. |
config | Getter returning { location, columns }, the serialized column list plus the controller's route location. |
getFormFields(mode) | Builds the FormFieldOrGroup[] for "view", "edit" or "new". |
setColumn, setGroup, setOptions, setArchiveField, setControllerRowActionRules, setControllerGuards | Writers used by the decorators and the builder. |
Each entry of config.columns carries id, header, isVisible, type (the serialized data type), accessorKey, listable, enableSorting, enableColumnFilter, defaultValue, accessMode and readonlyBehavior.
Route helpers
table-view also exports the interface functions the wrapped routes call. They are the seam between the interface and its implementation. A consumer wires them by declaring TableViewRoutes, and does not normally call them directly.
| Function | Role |
|---|---|
listWithSearch | Runs the list query with search, filters, sorting and pagination; returns { results, total, offset, limit }. |
countWithSearch | Same query, count only; returns { total }. |
startExport | Queues an export job; returns { jobId, format, extension }. |
getExportStatus | Returns { status, progress, error? } for a job. |
downloadExport | Streams a completed job's file to the response. |
archiveRows | Sets the archive field on the given ids; returns { success, archivedCount }. |
restoreRows | Clears it; returns { success, restoredCount }. |
validateRowsAgainstRule | Splits ids into { eligibleIds, rejectedIds } against a RowActionRule. |
fetchRowForGuard | Loads one row by id field, for the edit guard and the file-cleanup passes. |
Events and functions
| Namespace member | Value |
|---|---|
TableViewEvents.ROW_CLICK | "DmsComponent.TableView.RowClick" |
TableViewEvents.ROW_SELECT | "DmsComponent.TableView.RowSelect" |
TableViewEvents.ROW_DELETE | "DmsComponent.TableView.RowDelete" |
TableViewEvents.ROW_ADD | "DmsComponent.TableView.RowAdd" |
TableViewEvents.ROW_EDIT | "DmsComponent.TableView.RowEdit" |
TableViewEvents.FILTER_CHANGE | "DmsComponent.TableView.FilterChange" |
TableViewEvents.SORT_CHANGE | "DmsComponent.TableView.SortChange" |
TableViewEvents.EXPORT | "DmsComponent.TableView.Export" |
TableViewFunctions.CUSTOM_PAGE_FORM_SUCCESS | "DmsComponent.TableView.CustomPageFormSuccess" |
CUSTOM_PAGE_FORM_SUCCESS registers { url: string; preserveQuery?: string[] } in WatchFunctionParamMap. The builder uses it itself: the generated new and edit page forms watch FormEvents.SUBMIT_SUCCESS and navigate back to the table, preserving the query parameters named in queryParamFilters. The handler resolves {{params.X}} / {{query.X}} tokens in url against the route being left before navigating; the generated forms rely on it to send the user back to a page whose slug carries parameters: each placeholder of the page's slug becomes the token reading it on the form route (/workspaces/:id becomes /workspaces/{{params.id:1}} from /workspaces/:id/invoiceTable/:id/edit, /workspaces/{{params.id}} from /workspaces/:id/invoiceTable/new). A slug without parameters is used as is. The view form submits nothing and carries no such watch. All three generated pages are registered with FormPageLayout(), the constrained-column layout (Layout and containers).