Skip to main content
Version: 4.0 preview

Dropdown

Version
4.0 preview
Updated
View markdown

One value from a set, rendered as a select. It folds in the former dedicated enum type.

Storage kind: column · Column: varchar(50) — a choice key is a short token, not prose

Use it when the set of valid values is a closed list. Reach for radio when the set is short enough that showing every option at once helps; for join when the options are rows in a table and the stored value is their id; for pseudojoin when a model method computes the options and the stored value is a string key.

Settings

SettingMeaning
choicesvalue => label map. A static set
itemsLegacy alias for choices; used only when choices is absent
modelClass / modelMethodA dynamic set, fetched from a model
defaultlabelPlaceholder shown before a choice is made
defaultPre-selected value on a new record

Static versus dynamic sets

This is the distinction that matters, and it reaches well beyond rendering. A dropdown is static when it declares choices (or the legacy items), and dynamic when its options come from modelClass + modelMethod.

Static (choices)Dynamic (modelClass/modelMethod)
check() validates membershipyes — an out-of-set value is refusedno — the set is not known here
List filteringexact match / IN (…)base behaviour: LIKE '%value%'
Filter dropdown optionsthe choice labelsthe base's possible values
enum in generated JSON / MCP schemaspublishednot published

Two consequences worth internalising:

Membership validation is a real gate, not a nicety. The base check() only enforces required, so without a static choices map a tampered POST can store anything the column will hold. The column stays a plain varchar rather than a SQL ENUM precisely so that such a value produces a friendly refusal from check() instead of a fatal INSERT error.

Exact filtering is a correctness fix. The base filter is a substring LIKE, so filtering a static dropdown on draft would also match draft_archived. A static dropdown filters with = or IN (…) instead.

Publishing an enum for a dynamic set would be worse than publishing none: it would be a snapshot that goes stale the moment the underlying data changes. The model validates instead.

Notes

  • Keys are what gets stored, labels are display only. Keep keys short and stable — renaming one is a data migration, and varchar(50) is sized for tokens rather than sentences. A picker whose keys are genuinely longer carries an explicit dataType.
  • default becomes the column default in generated DDL. A dropdown that is NOT NULL with no default has nothing to fall back on.
  • getValueSet() reads choices, then the legacy items, and publishes whichever it finds as the enum of the generated schemas. It used to read choices only, which meant a definition supplying its set through items rendered and validated normally while documenting itself as an unconstrained string — product.visualization_type shipped with no enum and its composite option was invisible to every consumer of the API description. Prefer choices in new definitions all the same; items is supported, not recommended.

@see radio.md — the same thing rendered differently @see join.md · pseudojoin.md — when the options come from data @see ../com_configbox_type_generation.md — where a static choice set becomes an enum