The listbox
The block's own properties are on the Listbox page. This page covers what the schema cannot express: the column collection, and the behaviours that fail silently.
Where the rows come from
| Property | Role |
|---|---|
vt_TableName | Mandatory. Without it the listbox renders empty, with no error |
vt_FirstRequest | The starting set |
vt_FirstRequest expects a relation or a collection, not a query string: Entity.MyLines works, a hand-written query(...) does not. And never build it with a ternary returning two different types: the block fails as a whole.
For a listbox fed by a hard-coded collection rather than a table, the same property carries the collection.
Columns are not in the schema
vc_FieldDisplay is a collection of column objects, rendered asynchronously by columnProperties.html, and deliberately absent from the block schema: its shape depends on the table and on vt_FirstRequest. A column object carries its label, format, alignment, colours, conditions, widths, per-breakpoint visibility, and its own events.
Trap: never overwrite
vc_FieldDisplaywholesale. An update replaces the value: read the current collection, modify it, and send it back complete. Rebuilding it from memory is how column styling gets silently lost.
Breakpoints: absent means visible
Each column may declare vb_DisplayField per breakpoint (default, sm, md, lg, xl), plus vl_Order to reorder columns responsively.
When the key is absent, the column is visible: the listboxRow.html template defaults to True.
Worth knowing: V20 documentation states the opposite as a critical pitfall ("without breakpoint configuration, listbox cells are invisible", null treated as false for cells but true for headers). That is no longer true in the V21 component: there is a single computation for both, and it defaults to visible.
At the small breakpoint the listbox collapses all columns. That is the intended card-like behaviour, not a layout bug.
Sorting
The key that makes a column sortable is vt_sortable, not vt_SortFieldName.
For a computed attribute, sorting needs a Function orderBy on the attribute, otherwise 4D has nothing to sort on. Same for searching: a computed attribute queried without a Function query triggers a sequential scan.
Searching
A listbox has a native search zone. Do not build separate fieldText blocks to filter it: wire the native zone instead.
- One search wrapper drives one listbox.
- Searching targets the real field, not a custom column name.
@is a wildcard inside a search value, so an email search matches loosely unless the comparison is exact.
Events
Block-level: onSelect, onSelectRows, onDoubleClick.
A per-row action is a COLUMN event, not a block event: put onClick on the column and read This.uuidKey. An editable cell is also a column event (popupSelect, popupInput*), and an empty key is ignored.
A callback behind popupInput* must return vo_Line for the client to refresh the cell without reloading the whole listbox.
Things that look like bugs
- Rendering is asynchronous (a few seconds). A test that reads the DOM immediately finds nothing: wait for the rows before concluding.
- A listbox inside a hidden tab is empty on first display and populated on the second: BWEB skips listboxes in hidden panes. Call
loadAllListboxesIfNotLoaded(pane)when activating the tab. reloadListboxis a client-side function; the server-side action isdisplayListbox. Calling the wrong one from the wrong side does nothing.- "Select all" means the whole selection, not the visible page.
- An
<a href>inside a cell both opens the detail panel and navigates.
Checklist when creating one through the API
A misconfigured listbox produces no error at all: the block appears, the column headers appear, and the body stays on "no result". Nothing client-side, nothing server-side, nothing in the debugger. So check point by point rather than waiting for a message.
vt_TableNamepresent as soon as the source is a 4D table- every
vl_*property is an integer (20), never a string ("20"):vl_LengthByPage,vl_DisplayLine,vl_MinDisplayLine vt_DisplayListboxTypeset (fillis the usual answer)vt_FirstRequestbase64-encodedvt_FirstRequestgoes through a relation and returns one single type: no ternary mixing an entity selection and a collectionevents.onDisplayListboxRowspresent: without it no data is ever loaded- each column of
vc_FieldDisplaycarries its breakpoints - each column's
vt_howgetpkisEntity(table) orCollection(hard-coded data) vt_ConditionColor/vt_ConditionBackColordeclared, even empty- before applying
Year of/Day ofin avt_FieldFormula, check the field's real type incatalog.4DCatalog: a field calledstartDatemay well be stored as ISO text
If it still comes up empty, compare it property by property with a listbox that works on the same page. The difference shows up in seconds; reading the render engine costs an hour.
Worth knowing: in V21 these properties live in
properties. Notes written for V20 place them inhtmlProperties/blockProperties, which no longer exist.
See also
- Listbox: every property with its type and default
- Events and the POST: how
_selectedRowsreaches$vo_POST

