Reference picker
Import MoltaroReferencePickerDialog from @moltaro/ui to reuse the native
table, renderers, filters, search, sorting, and paging. The dialog loads pages;
it does not download the whole catalog. All API paths below resolve against
the configured workspace API, never against moltaro.com.
This component requires a host build that exports it. After upgrading that
build, run Check → Build → Activate and fully reload the WebApp.
HostContractVersion remains 1, but the host fingerprint has changed.
Existing saved projects are not rewritten.
Choose the context
Section titled “Choose the context”| Context | Required identity | What selection proves |
|---|---|---|
FormLookup | Source entity, saved Form Surface ID, item ID, expected Form revision | The authoritative field selection endpoint accepted this value in the supplied context |
Generic | Target entity; optional table surface key | A protected read could resolve the record at selection time |
For FormLookup, obtain the runtime Form through
GET /api/workspace/entity/{sourceEntityId}/ui/form, with surfaceKey when
needed. Use its Model.Id, Model.RowVersion, and the intended saved item
ID. Pass the same optional key as formSurfaceKey. For Update, also provide
sourceInstanceId. Supply only the selected item’s permitted user-owned
drivers in currentValues; this is not the entire draft.
The dialog verifies the runtime Form and binding, performs lookup preflight, and resolves the target from server metadata. It never accepts a caller-chosen target for this context. Before emitting a choice it calls the authoritative selection route, including the separate Tree route. A stale Form revision blocks selection until the page obtains and passes a fresh context. Do not retry with an empty revision or switch to Generic after an error.
Generic selection is independent of any source field. It does not prove that
the record is active or eligible for a form. Saving still runs server
authorization and validation. Use an explicit stable purpose per catalog
within the page to persist local filters. Without a purpose, local filter
persistence is disabled. User, page, target, and surface isolation still apply.
Component contract
Section titled “Component contract”| Prop | Meaning |
|---|---|
v-model | Whether the dialog is open |
context | Typed WorkspaceUiReferencePickerContext |
selectedId | Current record ID, or null; the page remains its owner |
attach | Explicit usePageContext().overlayTarget |
title | Optional localized title |
disabled / readOnly | Further restrict interaction; neither grants server permission |
clearable | Defaults to false; clear is shown only when allowed |
refreshKey | Change a string/number to invalidate and reload the context |
select emits a WorkspaceUiReferenceSelection containing entityId,
id, nullable displayName, and nullable subtitle. It never exposes an
internal grid row. The dialog emits select and then closes. Explicit clear
emits clear and then closes. Closing without a result emits cancel once.
Closing from the parent or unmounting an open dialog also emits cancel once;
neither emits cancel after a completed select or clear. Loading, errors, and
context refresh never write a value into the parent draft.
For saved Forms, clear is deliberately conservative: statically required fields, or bindings with RequiredWhen, EditableWhen, or VisibleWhen conditions, do not expose it. It also waits for a ready context without pending work or an error. A client flag cannot override requiredness or permissions.
state-change reports loading, nullable error,
contextRefreshRequired, and selectedValueState
(Empty, Loading, Resolved, Unavailable, or Error).
An off-page selection receives a separate bounded protected label read.
An unavailable value is not shown as resolved; label hydration is not field
eligibility validation. Network errors offer retry. Aborted and superseded
requests cannot commit a choice. Changes to context, drivers, selected ID,
user, refresh key, closing, and unmount invalidate pending work.
The first public version supports one value, Flat/Tree tables, and readable Projection targets. It explicitly rejects List/LongList, separate Hierarchy input, inverse/multiple bindings, and Table-filter contexts. Creating records or editing manual order is unavailable in this wrapper, including nested reference dialogs; existing native consumers keep their own capabilities.
A dependent saved Form Lookup
Section titled “A dependent saved Form Lookup”This component can be placed in a compiled Workspace UI project. Its parent supplies the saved IDs and revision from the runtime Form response and owns the draft values. Replace the props with your page’s actual form state.
<script setup lang="ts"> import { MoltaroReferencePickerDialog, type WorkspaceUiReferencePickerContext, type WorkspaceUiReferenceSelection } from '@moltaro/ui' import { usePageContext } from '@moltaro/workspace-ui' import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' import { VBtn } from 'vuetify/components'
const props = defineProps<{ sourceEntityId: string formSurfaceId: string beneficiaryItemId: string formRevision: string sourceInstanceId?: string | null settlementId: string | null beneficiaryId: string | null }>() const emit = defineEmits<{ (event: 'update:beneficiary-id', value: string | null): void }>() const page = usePageContext() const { t } = useI18n() const opened = ref(false) const context = computed<WorkspaceUiReferencePickerContext>(() => ({ kind: 'FormLookup', sourceEntityId: props.sourceEntityId, formSurfaceId: props.formSurfaceId, formItemId: props.beneficiaryItemId, expectedFormSurfaceRowVersion: props.formRevision, sourceInstanceId: props.sourceInstanceId, currentValues: { Settlement: props.settlementId }, })) function select (value: WorkspaceUiReferenceSelection) { emit('update:beneficiary-id', value.id) }</script>
<template> <VBtn @click="opened = true">{{ t('assistance.chooseBeneficiary') }}</VBtn> <MoltaroReferencePickerDialog v-model="opened" :attach="page.overlayTarget" clearable :context="context" :selected-id="beneficiaryId" @clear="emit('update:beneficiary-id', null)" @select="select" /></template>Add assistance.chooseBeneficiary to every project locale. Configure this
saved item as Lookup and allow Settlement as its eligibility driver.
In the parent’s explicit user driver-change handler, clear the dependent
beneficiary when the settlement changes. Do not reset it in an unconditional
watch of hydrated props: loading a saved record must preserve its saved
settlement/beneficiary pair. Keep this cascade reset in one place.
The wrapper passes the explicit attachment through its menus and child dialogs;
closing/unmounting it closes those owned overlays and discards late callbacks.
For an independent catalog, use:
const context: WorkspaceUiReferencePickerContext = { kind: 'Generic', targetEntityId: catalogEntityId, tableSurfaceKey: 'default', purpose: 'catalog-browser',}Keep that result separate from a Form field unless the Form’s own selection and save path validate it.
Preview, save, and verify
Section titled “Preview, save, and verify”After changing a reference, preview the page’s user-owned draft through the
existing form-evaluation endpoint. Wait for both an empty FieldErrors and
empty LoadingFieldKeys. Treat Patch, ReferencePatchValues, defaults,
and effects as server output; do not copy them back into user-origin inputs.
Save the intended user inputs with the actual definition/record revision,
then read the record again.
Preserve an immutable mutation payload and OperationKey across an unknown
network outcome. Resolve it by exact replay before sending another Create.
Adopt a successful command’s identity/revision immediately: failure of the
following GET is a readback failure, not a failed write. Retry that GET alone.
A Receipt is a successful committed write with restricted result visibility.
See reliable automation.
Large catalogs and acceptance
Section titled “Large catalogs and acceptance”Native List still loads at most 200 candidates. LongList still returns
at most 25 search results. Their hints report an incomplete result using
TotalCount; they do not silently become full catalogs. Choose Lookup in
Constructor when users need to browse every permitted page. Existing
configurations are not converted automatically.
Verify a choice near the beginning, middle, and end of a catalog larger than 250 records; search, clear, cancel, off-page current selection, and changing a driver while a request is pending. Repeat with an ordinary role and a restricted role, then at 375px with keyboard/Escape. Confirm that filters and menus remain inside the page-owned overlay, focus returns to the opener, and navigation leaves no overlay or scroll lock.