Dropdown
- Version
- 3.x
- Updated
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
| Setting | Meaning |
|---|---|
choices | value => label map. A static set |
items | Legacy alias for choices; used only when choices is absent |
modelClass / modelMethod | A dynamic set, fetched from a model |
defaultlabel | Placeholder shown before a choice is made |
default | Pre-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 membership | yes — an out-of-set value is refused | no — the set is not known here |
| List filtering | exact match / IN (…) | base behaviour: LIKE '%value%' |
| Filter dropdown options | the choice labels | the base's possible values |
enum in generated JSON / MCP schemas | published | not 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 explicitdataType. defaultbecomes the column default in generated DDL. A dropdown that isNOT NULLwith no default has nothing to fall back on.getValueSet()readschoices, then the legacyitems, and publishes whichever it finds as theenumof the generated schemas. It used to readchoicesonly, which meant a definition supplying its set throughitemsrendered and validated normally while documenting itself as an unconstrained string —product.visualization_typeshipped with noenumand itscompositeoption was invisible to every consumer of the API description. Preferchoicesin new definitions all the same;itemsis 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