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

PropertyRole
vt_TableNameMandatory. Without it the listbox renders empty, with no error
vt_FirstRequestThe 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_FieldDisplay wholesale. 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.
  • reloadListbox is a client-side function; the server-side action is displayListbox. 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.

  1. vt_TableName present as soon as the source is a 4D table
  2. every vl_* property is an integer (20), never a string ("20"): vl_LengthByPage, vl_DisplayLine, vl_MinDisplayLine
  3. vt_DisplayListboxType set (fill is the usual answer)
  4. vt_FirstRequest base64-encoded
  5. vt_FirstRequest goes through a relation and returns one single type: no ternary mixing an entity selection and a collection
  6. events.onDisplayListboxRows present: without it no data is ever loaded
  7. each column of vc_FieldDisplay carries its breakpoints
  8. each column's vt_howgetpk is Entity (table) or Collection (hard-coded data)
  9. vt_ConditionColor / vt_ConditionBackColor declared, even empty
  10. before applying Year of / Day of in a vt_FieldFormula, check the field's real type in catalog.4DCatalog: a field called startDate may 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 in htmlProperties / blockProperties, which no longer exist.

See also