DomUI

LookupInput2

LookupInput2<T> holds one record as its value. The user finds that record either by typing a few letters into the control itself, or by pressing the lookup button and searching in a dialog.

LookupInput2<Customer> customer = new LookupInput2<>(Customer.class);
customer.setMandatory(true);

FormBuilder fb = new FormBuilder(cp);
fb.label("Customer").control(customer);

The three things it can show

State What is on screen
empty, with quick search an input box plus a lookup and a clear button
empty, without quick search the text (no selection) plus those two buttons
a value the record, rendered; the clear button becomes usable

Which of the first two it is depends on whether the control has a quick search - it needs to know which properties to search on. It gets those from the KEYWORD (or BOTH) search properties of the class, or from addKeywordProperty(); with neither, the button is the only way in. setAllowKeyWordSearch(false) turns the box off even when metadata offers one.

A read-only control with a value shows just the value (ui-lui-selected-ro) and drops both buttons; a disabled one keeps them, greyed.

What the quick search does

Typing in the box searches after a short pause, and what comes back decides what happens:

Records found What the control does
none shows no matches under the box
exactly one selects it, without asking
2 to 100 drops down the list to pick from
more than 100 shows n record(s) instead of a list

The single-match rule is the one to remember: a search precise enough to match one record fills the control straight away, change handler and all. setDisableSelectOne(true) switches that off for one control, and LookupInputBase2.setDisableSelectOneGlobal(true) for a whole application.

The query itself is built by an IStringQueryFactory. The default one ors a like over every keyword property, anchored at the start (ilike 'abc%' for a string, = for a number), and skips any property whose minimum length the typed text has not reached yet - which is what addKeywordProperty("artist.name", 2) sets. It understands two prefixes of its own:

Typed Searches
$$3 the record whose primary key is 3
$$city=Oslo the record whose city property is exactly Oslo
* anywhere as a wildcard - it becomes %

Pressing the lookup button instead of picking from the list opens the dialog, carrying whatever was typed into it as its first search.

Saying what may be searched

Method What it does
addKeywordProperty(name) a property the quick search covers - replaces the metadata list
addKeywordProperty(name, minlen) the same, but only once that many characters are typed
setAllowKeyWordSearch(boolean) switch the box off entirely
setKeySearchHint(String) the tooltip of the box; the default names the properties being searched
setStringQueryFactory(...) build the quick-search query yourself

A keyword property may be a path: addKeywordProperty("artist.name", 2) searches albums by the name of their artist.

Limiting what can be found at all

Way When to use it
new LookupInput2<>(QCriteria<T>) a fixed limit for the life of the control: every search is anded with this
setQueryManipulator(...) a limit that changes: it is asked on every search, and may return null to refuse one
new LookupInput2<>(Class<T>, List<T>) no database at all - the control searches the list it was given

The root criteria and the manipulator stack: the manipulator runs first, then the root criteria is merged in.

What it shows for a value

Three different things are rendered, and each has its own hook:

What How to change it
the selected value in the control new LookupInput2<>(clz, "lastName", "city"), or setValueRenderer(...) for full control
the drop-down under the search box setKeywordSearchResultsDropDownRenderer(...)
the table in the lookup dialog setFormRowRenderer(...)

Without any of them, all three come from the class's @MetaObject display properties. The dialog is configurable too:

Method What it does
setSearchProperties(QField...) which fields the dialog's own search form offers
setDefaultTitle(String) the dialog's title
setPopupSearchImmediately(true) search at once when the dialog opens, rather than waiting
setPopupInitiallyCollapsed(true) open with the search form folded away
setOnPopupOpen(INotify<Dialog>) get at the dialog itself as it opens
setPopupOpener(IPopupOpener) replace the dialog wholesale

The dialog fills most of the window. That is fine on a page, but a LookupInput2 inside a small frame - a documentation page embedding a demo, for instance - has no room for it. The quick search works in any size.

Value, states and changes

Method What it does
setValue(T) / getValue() the record; getValue() on an empty mandatory control reports Mandatory field and throws
setMandatory(boolean) see above
setReadOnly(boolean) / setDisabled(boolean) / setDisabledBecause(String) the usual three states
setOnValueChanged(...) called when a record is picked or cleared - including by the single-match rule
getWorkValue() the value without the mandatory check

Selecting a record moves the value into the model straight away (OldBindingHandler.controlToModel), so a bound page sees the new value in the same request that selected it - not on the next one.

From a property

A relation property gets a LookupInput2 from the form builder by default:

fb.property(invoice, Invoice_.customer()).control();

The alternative for a small table is a ComboLookup2, chosen by putting componentTypeHint = Constants.COMPONENT_COMBO on the property.

Last modified: 2026/09/02