Parent Tree View developer and API guide
Parent Tree View is an additive Entity UI runtime contract. A bootstrap returns either a Flat page or a Tree root page, never both. Child requests always echo the resolved surface revision so stale branches cannot be merged.
Preconditions
Section titled “Preconditions”- Authenticate against the target workspace API host.
- Resolve the Entity Definition and configured Tree Table Surface, whether default or explicitly selected.
- Use the installed Runtime and Configuration OpenAPI documents as the authoritative schemas and enum source.
- Never supply a parent-field id or key to a runtime child route; the server resolves structural metadata from the Table Surface.
Authoritative runtime endpoints
Section titled “Authoritative runtime endpoints”| Context | Bootstrap | Child page | Additional root page |
|---|---|---|---|
| Data Explorer | POST /api/workspace/entity/{entityIdOrKey}/ui/hosted/table/bootstrap?hostSurface=EntityList | POST /api/workspace/entity/{entityIdOrKey}/ui/hosted/table/tree/children?hostSurface=EntityList | Repeat bootstrap with the next query page. |
| Direct table | POST /api/workspace/entity/{entityIdOrKey}/ui/table/bootstrap?tableSurfaceKey={optionalKey} | POST /api/workspace/entity/{entityIdOrKey}/ui/table/tree/children?tableSurfaceKey={optionalKey} | Repeat bootstrap with the next query page. |
| Generic Reference picker | POST /api/workspace/entity/{entityIdOrKey}/ui/reference-picker/table/bootstrap?tableSurfaceKey={optionalKey} | POST /api/workspace/entity/{entityIdOrKey}/ui/reference-picker/table/tree/children?tableSurfaceKey={optionalKey} | Repeat bootstrap with the next query page. |
| Persisted Reference Lookup | POST /api/workspace/entity/{sourceEntityIdOrKey}/ui/form/{sourceFormSurfaceId}/items/{formItemId}/lookup-table/bootstrap | POST /api/workspace/entity/{sourceEntityIdOrKey}/ui/form/{sourceFormSurfaceId}/items/{formItemId}/lookup-table/tree/children | Repeat bootstrap with the next query page. |
| Details related-data navigation | POST /api/workspace/entity/{hostEntityIdOrKey}/ui/hosted-layout/block?hostSurface=DetailsPage&instanceId={hostInstanceId} | POST /api/workspace/entity/{hostEntityIdOrKey}/ui/hosted-layout/related/tree/children?hostSurface=DetailsPage&instanceId={hostInstanceId} | POST /api/workspace/entity/{hostEntityIdOrKey}/ui/hosted-layout/related/tree/roots?hostSurface=DetailsPage&instanceId={hostInstanceId} |
| Details/Drawer hosted-layout block | POST /api/workspace/entity/{hostEntityIdOrKey}/ui/hosted-layout/block?hostSurface={DetailsPage-or-Drawer}&instanceId={hostInstanceId} | POST /api/workspace/entity/{hostEntityIdOrKey}/ui/hosted-layout/related/tree/children?hostSurface={DetailsPage-or-Drawer}&instanceId={hostInstanceId} | POST /api/workspace/entity/{hostEntityIdOrKey}/ui/hosted-layout/related/tree/roots?hostSurface={DetailsPage-or-Drawer}&instanceId={hostInstanceId} |
The public Runtime OpenAPI documents every bootstrap and child request. A
related root or child request includes its layout item and opaque bootstrap
ContextRevision; the server re-resolves the host record, block, binding,
target default table, and relation eligibility scope before querying.
Bootstrap exclusivity
Section titled “Bootstrap exclusivity”EntityTablePageBootstrap obeys this invariant:
| Resolved result | Page | TreePage | Model.TreeView |
|---|---|---|---|
| Flat | non-null | null | null |
| Tree | null | non-null | non-null |
| Missing surface | null | null | no usable model |
A client must reject both/neither page variants for a usable model. It must not call the flat Entity Instance list endpoint as a fallback.
Each TreePage.Items[] element contains:
Instance: the ordinary materialized row and permissions;ChildCount: exact eligible direct-child count;IsAncestorContext: true only when retained for a matching descendant;HierarchyIssue:None,SelfParentCut, orCycleCut.
Issue values are row-local and contain no related record id.
Child request
Section titled “Child request”For hosted Data Explorer or a generic picker:
{ "ParentInstanceId": "<expanded record id>", "ExpectedTableSurfaceId": "<Model.Id from bootstrap>", "ExpectedTableSurfaceRowVersion": "<Model.RowVersion from bootstrap>", "Query": { "Page": 1, "PageSize": 25, "ArchiveMode": 0, "Filter": null, "Sort": [ { "Field": { "EntityFieldDefinitionId": "<subject field id>" }, "Direction": 0 } ], "Include": null }}Copy the exact query field-reference shape and enum values from the installed
OpenAPI. Reuse the same Filter, Sort, ArchiveMode, and Include for roots and
all child pages. Only Page changes within one sibling set.
A persisted Lookup child request uses a flat request object rather than the
generic request’s nested Query object:
{ "ParentInstanceId": "<expanded record id>", "ExpectedTableSurfaceId": "<Model.Id from bootstrap>", "ExpectedTableSurfaceRowVersion": "<Model.RowVersion from bootstrap>", "ExpectedLookupFormSurfaceRowVersion": "<LookupFormSurfaceRowVersion from bootstrap>", "Page": 1, "PageSize": 25, "ArchiveMode": 0, "Include": null, "Filter": null, "Sort": []}The source Form Surface id and form item id remain route segments. The server
re-resolves the persisted item, its Lookup selector, target entity, role
selector, and current Form/Table revisions, preserves the validated request
Filter, and rejects a stale Form Surface revision. Moltaro V1 has no separate
declarative per-form-item row-constraint model; do not invent or emulate one in
client code.
For persisted Lookup, the route entity is the source entity that owns the Form Surface and Reference item. The target entity and Tree Table Surface are resolved from that persisted source item; do not substitute the target entity in the route.
Related Details/Drawer root and child requests do not accept Query, Filter,
Sort, or Include. They carry the server-owned hosted context instead. For a
child page:
{ "LayoutItemId": "<related layout item id>", "ExpectedContextRevision": "<ContextRevision from bootstrap>", "ParentInstanceId": "<expanded record id>", "ExpectedTableSurfaceId": "<Table.Id from bootstrap>", "ExpectedTableSurfaceRowVersion": "<Table.RowVersion from bootstrap>", "Page": 1, "PageSize": 25, "ArchiveMode": 0}The related root-page request uses the same fields without
ParentInstanceId. Only Page changes while appending a root sibling page.
The server re-resolves the host record, layout block, binding projection,
eligibility scope, default Table Surface, and configured sort from the opaque
context. Do not add direct-query members: the public schemas reject unknown
properties.
Sorting, filtering, and paging
Section titled “Sorting, filtering, and paging”The hierarchy runtime reuses Entity Instance Query validation and execution:
DisplayName, ordinary fields, Reference target DisplayName, joined supported
paths, multiple sort items, direction, null semantics, and stable Id ASC
tie-breaking. Generic queryability rules remain unchanged; the server’s narrow
use of a relation-activated Parent as a structural edge does not make it
available to arbitrary filters or sorts.
Filtering first determines direct matches, then adds visible ancestor closure.
TotalCount and HasNextPage describe the requested sibling set. Root and
child paging are independent, one-based, and capped at 250. Embedded related
tables append additional roots through the related tree/roots route; their
root-level More control is not the child More row of any branch.
Errors and retry contract
Section titled “Errors and retry contract”| HTTP | Meaning | Client behavior |
|---|---|---|
| 400 | Invalid request, selector, field, Tree configuration, or unavailable configured search (moltaro.search.notAvailable) | Display the stable API error; preserve the query for retry; do not flatten. |
| 403 | Caller cannot access the definition/context | Stop and preserve no hidden metadata. |
| 404 | Definition or requested readable parent/context not found | Remove/reload that branch as appropriate. |
| 409 | Expected Table, Lookup Form, or related host-context revision is stale | Abort children, clear the tree, bootstrap again. |
Automation branches on stable Errors[].Code and Errors[].Field, not English
messages.
Writing and reparenting
Section titled “Writing and reparenting”The parent is an ordinary Reference value. Use supported Entity Instance create
or patch services and the record’s current RowVersion; do not write the
physical parent column directly. Read the changed record and refresh the Tree
bootstrap after a successful mutation.
V1 does not reject cycles at write time. If business policy requires one root, allowed parent types, maximum depth, or cycle prevention, enforce it in the workspace’s supported C# business logic before save. Runtime normalization is a safe presentation boundary, not a substitute for domain validation.
Picker selection
Section titled “Picker selection”Expansion and selection are distinct operations. Send only explicit record ids on confirmation. Do not select a synthetic Load-more row or infer descendants. In Data Explorer, this selected-record workflow is desktop-only: the narrow/mobile card presentation deliberately exposes neither record-selection controls nor the bulk-action bar. This restriction does not apply to a picker, whose purpose is explicit record selection. The Data Explorer responsive transition clears desktop selected ids and dismisses pending group-action UI; selection does not reappear when the viewport returns to desktop. For persisted Lookup, never replace its dedicated routes with the generic picker route: doing so would discard the persisted source-item/selector context and its Form Surface revision guard.
Export
Section titled “Export”Data Explorer export is desktop-only. Query export uses the ordinary Entity Instance query, so context-only ancestors are excluded; selected export uses exact ids. Narrow/mobile Data Explorer exposes neither query export nor selected-row export. There is no V1 endpoint for exporting the current depth-first expanded projection.
Agent workflow
Section titled “Agent workflow”- Read the installation-local Configuration OpenAPI.
- Read Table configuration options and select a returned Ready parent field.
- Configure
TreeViewon a Table Surface and decide whether it should be the entity default. - If needed, assign it through Entity List
MainTableSelectorsand a Reference item with Lookup input. Resolve any List/LongList dependency before making it default. - Read both configurations back.
- Bootstrap Data Explorer/direct, each affected Details/Drawer related block, and the relevant picker route.
- Verify one root page, one child page, related scope, one sibling sort, and one descendant filter.
- Repeat with an ordinary reader when a test identity exists.
- Report V1 limitations; do not invent Group By, List/LongList Tree selection, subtree operations, or WYSIWYG tree export.