Derived controllers
Show the same data in two writing screens: derive the controller
A data controller has one writing TableView. Its permission guards the controller's write routes, its row rules apply to every write, and its forms are the ones whose files those routes accept. Other pages may show the same rows in read-only table views, but a second table view that writes through the same controller fails at boot. To give the same data a second writing screen, derive a controller for it: same table, same fields, routes of its own.
Writing and read-only table views
A TableView writes when it enables at least one write action the controller offers: add, duplicate, edit, delete, or archive / restore under archiveMode. An action left out of rowActions is enabled, so a table view writes unless you turn every write action off:
static table = TableView(taskDataAPI, {
caption: "Overview",
rowActions: { add: false, duplicate: false, edit: false, delete: false },
});
A read-only table view may share the controller with the writing one. It opens no new or edit form, and, once the writing table view is registered, it changes neither the controller's row rules nor the options its routes read (rowIdKey, strictRuleValidation). Holding its add, edit or delete permission grants nothing on the write routes: only the writing table view's permission does.
A second writing table view over the same controller fails the registration of its page, with an error naming both table views:
[dms] TableView "pages.archive.table" (page "pages.archive") and TableView "pages.board.table" (page "pages.board") both write through the data controller taskDataAPI. A data controller has one writing TableView: make the other one read-only (rowActions add, duplicate, edit and delete set to false, no archiveMode), or give it a controller of its own derived from taskDataAPI -- see …
The rule holds for two table views on the same page as well. A page that registers again — a hot reload — takes the controller back; a page that goes away leaves it to the next writer.
Derive the controller
DataController(tableClass, routes, baseController) accepts a controller as its base: pass Controller(location, parentController) and an empty route set, and the new controller inherits everything the parent declares.
import { Controller } from "@antelopejs/interface-api";
import { DataController, RegisterDataController } from "@antelopejs/interface-data-api";
import { Task } from "../db/tasks";
import { taskDataAPI } from "./tasks";
@RegisterDataController()
export class boardTaskDataAPI extends DataController(
Task,
{},
Controller("/api/board-tasks", taskDataAPI),
) {}
@RegisterPage()
export class BoardPage extends PageController("board", { displayName: "Board", category: pagesCategory }) {
static table = TableView(boardTaskDataAPI, {
caption: "Board",
defaultDisplay: "kanban",
kanban: { groupByField: "status" },
});
}
The derived controller starts with:
- the parent's routes, registered again at the new location (
/api/board-tasks/list,/api/board-tasks/new, …), and the parent's fields, filters and model (data API metadata); - the parent's columns and column groups, its
@ArchiveField, the options of the parent's writing table view, and its@Searchablefields (DMS metadata). Each column gets its own copy of its data type, so a file column stores its attachments under the derived controller's address.
It does not inherit the parent's table views, their row rules, guards or permissions: the table view you build over the derived controller brings its own. You may declare more @Column fields on the derived class; the parent does not see them.
What each screen gets
Both controllers read and write the same rows, through routes of their own:
| Parent screen | Derived screen | |
|---|---|---|
| Routes | /api/tasks/* | /api/board-tasks/* |
| Write permission | the parent table view's add / edit / delete | the derived table view's |
Row rules, rowIdKey, strictRuleValidation | the parent table view's | the derived table view's |
| Files staged from the forms | saved through /api/tasks/* | saved through /api/board-tasks/* |
A role granted edit on one screen cannot edit through the other one: grant each screen's actions where they apply.
When a read-only screen is enough
A screen that only lists the rows — an overview, a dashboard tile, a recovery page — needs no derived controller: turn its write actions off and mount it on the same controller (see Tables).