Skip to content

Lookup Editors

Lookup editors are dropdowns that let users pick a value from a list of options. There are two main kinds:

  • LookupEditor — loads options from a lookup script (a server-side [LookupScript]).
  • ServiceLookupEditor — loads options from a service (a list endpoint).

Both are combobox-based and support searching, in-place add, and cascading.

LookupEditor

LookupEditor renders a combobox over items from a lookup script. The lookup key is determined from the lookupKey option, or derived from the editor class name.

On the server side, a form property uses [LookupEditor]:

[LookupEditor(typeof(CustomerRow))]
public string CustomerID { get; set; }

The generated form declares the editor type:

export interface UserForm {
    Roles: LookupEditor;
    // ...
}

LookupEditor Options

Option Description
lookupKey Key of the lookup to load items from.
async Whether items are loaded asynchronously.
inplaceAdd Whether the editor supports adding new items in place.
dialogType Dialog type used for in-place add.

The editor subscribes to lookup changes, so when the lookup script is reloaded, the editor updates its items automatically.

ServiceLookupEditor

ServiceLookupEditor loads options from a service list endpoint instead of a lookup script. This is useful when the option list is too large or dynamic for a lookup script.

On the server side:

[ServiceLookupEditor(typeof(MyRow))]
public string SomeID { get; set; }

The editor can work with services that are not standard Serenity list handlers: it can infer the lookup endpoint and the service URL from the row's metadata / endpoint type (so service or endpointType often don't need to be specified), infers ID and text fields for both row and non-row (POCO) item types, and supports primitive services returning List<string> and other array responses.

ServiceLookupEditor Options

Option Description
service Service endpoint to load items from.
idField Id field name.
textField Text field name.
pageSize Page size for paged searches.
sort Sort order for results.
includeColumns / excludeColumns Columns to include/exclude.
includeDeleted Whether to include deleted rows.
containsField Field used for contains-text search.
equalityFilter Equality filter applied to the request.
criteria Criteria applied to the request.
capabilities ListRequestCapabilities flags describing which list request options the endpoint applies.
quickSearchFields Fields used when contains-text search is run locally.
iconField Field whose value is rendered as an icon in results and selections. Can also be inferred from IconClass / Icon; on the server side, set it with [ServiceLookupEditor(IconField = nameof(MyRow.Fields.Icon))].

Capabilities and Local Processing

The capabilities option takes a ListRequestCapabilities flags value describing which ListRequest options the target endpoint actually applies:

const editor = new ServiceLookupEditor({
    service: "Northwind/Product",
    // run ContainsText locally if the endpoint does not support it
    capabilities: ListRequestCapabilities.Baseline & ~ListRequestCapabilities.ContainsText
});

When omitted, all standard properties are sent. When set, the editor infers capabilities from the endpoint actions when possible, and falls back to client-side processing for unsupported operations. Local processing covers contains text (with optional quickSearchFields), equality filtering, criteria, sorting and paging, and is used automatically when the endpoint lacks the matching capability. Endpoints can declare what they support with [ListRequestCapabilities] — see List Request Capabilities.

Loading State and Selected Items

LookupEditor and ServiceLookupEditor are built on the combobox, which gained an explicit API for the asynchronous selection load:

  • selectionLoadState — "idle", "loading" or "error".
  • ensureSelectionLoaded(): Promise<void> — waits for the pending initial selection load instead of racing it. Waiters are rejected with an AbortError when the load is aborted or superseded by a newer one, and the last failure is replayed to later callers.
  • getSelectedItemAsync() / getSelectedItemsAsync() — return the selected item(s) once the load completes, so you don't have to subscribe to events and re-run the load yourself.
const editor = new ServiceLookupEditor({ service: "Northwind/Product" });

await editor.ensureSelectionLoaded();
const product = await editor.getSelectedItemAsync();

The change event raised by setValue is marked with a dataset flag, so listeners can tell a programmatic value change apart from a user edit (useful for avoiding reload loops in cascaded editors).

Request Optimizations

  • When the editor is given a single id, it sends an equality criteria instead of an IN list.
  • When idList is empty, it returns early without a service round trip.

Cascaded Editors

Lookup editors support cascading — a child editor filters its options based on the parent editor's value. This is done with the cascadeFrom and cascadeField options:

[LookupEditor(typeof(CityRow), CascadeFrom = "CountryID", CascadeField = "CountryId")]
public string CityID { get; set; }
  • cascadeFrom — the id of the parent editor (e.g. CountryID).
  • cascadeField — the field on the child's lookup items that must match the parent's value.

When the parent changes, the child editor reloads and filters its options to those matching the parent value.

The CascadedWidgetLink class implements this behavior — it links a child widget to a parent widget and reacts to the parent's changes.

See How To: Setup Cascaded Editors for a full example.

CheckLookupEditor

CheckLookupEditor renders a checkbox list from a lookup, for multi-select fields:

[CheckLookupEditor(typeof(MyRow))]
public List<int> MyIDs { get; set; }

See Also