Relation Fields
Use this document for relation-oriented field APIs:
RelationTableSelectTreeManyToOne/OneToOneOneToManyManyToMany- relation query / pagination / patch behavior
Related docs:
- Fields: core
Fieldprops, conditions,filters,Field.onChange, value contracts - Widget matrix: widget compatibility and widget-specific examples
- ModelForm: page shell and relation-form examples
- ModelTable: read cells and inline edit behavior
Import
import { Field, RelationTable } from "@/components/fields";RelationTable is used only inside relation field declarations such as Field.tableView.
RelationTable
RelationTable is the relation-table view definition for OneToMany and ManyToMany.
Example:
const optionItemsTableView = (
<RelationTable orders={["sequence", "ASC"]} pageSize={10}>
<Field fieldName="sequence" />
<Field fieldName="itemCode" />
<Field fieldName="itemName" />
<Field fieldName="active" />
</RelationTable>
);Props
| Prop | Type | Required | Notes |
|---|---|---|---|
children | ReactNode | Yes | Ordered <Field /> column declarations for the relation table. |
orders | OrderCondition | No | Default relation-table sorting. Supports a single tuple or multiple tuples. |
pageSize | number | No | Relation-table page size. Only affects paged relation tables (isPaged={true}). |
Sorting examples:
<RelationTable orders={["sequence", "ASC"]}>
<Field fieldName="sequence" />
<Field fieldName="itemCode" />
</RelationTable><RelationTable
orders={[
["sequence", "ASC"],
["itemCode", "DESC"],
]}
>
<Field fieldName="sequence" />
<Field fieldName="itemCode" />
</RelationTable>Behavior notes:
RelationTable.pageSizeonly affects paged relation tables (isPaged)RelationTable.orderssupports both a single tuple and multiple tuples- column declaration still comes from child
<Field />order
ManyToOne / OneToOne
Default behavior is searchable reference selection:
<Field fieldName="departmentId" />Dependent relation filter example:
<Field fieldName="companyId" />
<Field
fieldName="departmentId"
filters={[
["companyId", "=", "{{ companyId }}"],
"AND",
["active", "=", true],
]}
/>Notes:
filtersis applied to the default searchable reference query- when a dependent
{{ fieldName }}has no current value, the selector stays query-disabled instead of loading all options
SelectTree
Use SelectTree when the related model is hierarchical:
<Field fieldName="departmentId" widgetType="SelectTree" />Common pattern with dependent filtering:
<Field fieldName="companyId" />
<Field
fieldName="departmentId"
widgetType="SelectTree"
filters={[
["companyId", "=", "{{ companyId }}"],
"AND",
["active", "=", true],
]}
/>Behavior:
SelectTreeis the recommended developer-facing tree entry for forms and inline editors- it is still declared through
Field, not by renderingSelectTreePaneldirectly - it uses the same
Field.filtersrules as searchable reference fields - when a dependent
{{ fieldName }}value is missing, the tree selector stays query-disabled instead of loading an unfiltered tree - low-level
Tree/SelectTreePanelare internal infrastructure
OneToMany
Rendered as relation table with inline or dialog editing. Public usage stays on Field.
Example:
const optionItemsTableView = (
<RelationTable orders={["sequence", "ASC"]} pageSize={10}>
<Field fieldName="sequence" />
<Field fieldName="itemCode" />
<Field fieldName="itemName" />
<Field fieldName="active" />
</RelationTable>
);
<Field fieldName="optionItems" tableView={optionItemsTableView} />;Common props:
tableView: relation-table columns via<RelationTable><Field /></RelationTable>formView: dialog form for row create/editisPaged: enable pagination / remote relation mode
Default submit behavior is incremental patch map:
{
"Create": [{ "name": "new row" }],
"Update": [{ "id": "101", "name": "changed" }],
"Delete": ["102", "103"]
}Behavior:
false(default): include relationsubQueryingetById; relation table does not paginate in UI and renders local rowstrue: relation table enables pagination UI; whenrecordId + relatedModel + scoped relation filterare ready, data is loaded byrelatedModel.searchPage(remote mode), otherwise paginated locally- editable cells are limited to declared
RelationTablecolumns intersected with editable related-model fields - unresolved
{{ expr }}dependencies pause remote relation queries until the dependent parent form value exists
ManyToMany
Rendered as relation table plus picker dialog by default.
Example:
const userTableView = (
<RelationTable orders={["username", "ASC"]} pageSize={10}>
<Field fieldName="username" />
<Field fieldName="nickname" />
<Field fieldName="email" />
<Field fieldName="status" />
</RelationTable>
);
<Field fieldName="userIds" tableView={userTableView} />;Default submit behavior is incremental patch map:
{
"Add": ["1", "2"],
"Remove": ["3"]
}TagList
widgetType="TagList" switches ManyToMany to a searchable multi-select dropdown with tags rendered below the trigger.
<Field fieldName="userIds" widgetType="TagList" tableView={userTableView} />Behavior:
- searchable dropdown with multi-select interactions
- selected values are rendered as tags below the trigger
- trigger text stays compact and only shows selection count
- field layout follows surrounding
FormSectioncolumns by default; passfullWidthexplicitly when you want it to span the whole row - top-level
ModelFormgetByIdonly adds the field name tofields; it does not add a relationsubQuery - field UI value is
ModelReference[], while top-level submit still uses the normal incremental patch map
Query Notes
ManyToManypicker dialog merges the effective field filter, internal relation-scoped filters, search filter, and column filters usingAND- unresolved
{{ expr }}dependencies pause remote picker and relation-table queries until the source value exists formViewis optional; inManyToMany, row-click opensModelDialogin read mode while add/remove still uses picker behavior
Shared Read / Inline Behavior
Shared behavior across relation fields:
ModelTable/RelationTableread-mode cells render bothOneToManyandManyToManyas compact tag lists usingdisplayName -> idfallback instead of JSON stringswidgetPropsis not propagated intoRelationTableread-mode cell renderers- relation tables reuse the same compact file/image read renderers as
ModelTable RelationTable.pageSizeonly affects paged relation tables (isPaged=true)- remote relation table and picker queries use the effective field filter (
Field.filters ?? metaField.filters), relation-scoped filters, and runtime search / column filters
Form View Example
formView is typically paired with ModelDialog:
function UserRoleUserIdsFormView() {
return (
<ModelDialog title="User Detail">
<FormSection labelName="General" hideHeader>
<Field fieldName="username" />
<Field fieldName="nickname" />
<Field fieldName="email" />
<Field fieldName="mobile" />
<Field fieldName="status" />
</FormSection>
</ModelDialog>
);
}