| title | Select Field |
|---|---|
| description | Dropdown selection field with single or multiple options |
import { ComponentDemo, DemoGrid } from '@/app/components/ComponentDemo';
The Select Field component provides a dropdown for selecting one or more options from a predefined list.
An option can carry a visibleWhen CEL predicate — it is offered only when the
predicate is TRUE. The predicate is evaluated against the live record plus
current_user, the same engine and binding environment as a field-level
visibleWhen. This single mechanism covers two needs:
- Cascading / dependent options — narrow a child list by a parent field (country → province → city).
- Role / context gating — offer an option only to certain users.
Declare the sibling field(s) a select reacts to with dependsOn. While any is
empty the control is gated (a "Select country first" hint) instead of
showing an unfiltered list; once the parent changes, the list re-evaluates and
any now-invalid selection is cleared automatically (no stale "China +
California" pair).
{
"type": "form",
"fields": [
{ "name": "country", "label": "Country", "type": "select", "options": [
{ "label": "China", "value": "cn" },
{ "label": "United States", "value": "us" }
]},
{ "name": "province", "label": "Province", "type": "select", "dependsOn": "country", "options": [
{ "label": "Zhejiang", "value": "zj", "visibleWhen": "record.country == 'cn'" },
{ "label": "Guangdong", "value": "gd", "visibleWhen": "record.country == 'cn'" },
{ "label": "California", "value": "ca", "visibleWhen": "record.country == 'us'" },
{ "label": "Texas", "value": "tx", "visibleWhen": "record.country == 'us'" }
]}
]
}Chain a third level (city, dependsOn: "province") the same way — the gate and
cascade-clear propagate down the chain.
{ "name": "tier", "type": "select", "options": [
{ "label": "Standard", "value": "standard" },
{ "label": "Admin only", "value": "admin_only", "visibleWhen": "'admin' in current_user.roles" }
]}Security — hiding is UX, not authorization. A
visibleWhenon an option only removes it from the dropdown on the client; a determined caller can still submit the value. When an option is gated for access-control reasons the server must also reject writes of that value (the rule-validator evaluates the picked value'svisibleWhen). Use optionvisibleWhenfor convenience and cascades freely; for real authorization, pair it with server-side enforcement.
visibleWhen options are for small, static dictionaries (category →
subcategory, a handful of provinces). When the data is large, changes over time,
or is shared across forms (real country/province/city tables, org units, product
catalogs), model each level as a lookup with depends_on instead — the
candidate query is filtered server-side and paginated. See
Lookup Field.
interface SelectFieldSchema {
type: 'select';
name: string; // Field name/ID
label?: string; // Field label
placeholder?: string; // Placeholder text
value?: string | string[]; // Default value(s)
multiple?: boolean; // Allow multiple selections
required?: boolean; // Is field required
readonly?: boolean; // Read-only mode
disabled?: boolean; // Disabled state
className?: string; // Additional CSS classes
// Options
options: SelectOption[]; // Available options
// Cascading: sibling field(s) whose value drives this option list. While any
// is empty the field is gated ("Select <parent> first"); it re-evaluates as
// they change. Same knob dependent lookups use.
dependsOn?: string | string[];
}
interface SelectOption {
label: string; // Display label
value: string; // Option value
color?: string; // Badge color (gray, red, blue, etc.)
disabled?: boolean; // Disable specific option
// Per-option visibility predicate (CEL). The option is offered only when TRUE,
// evaluated against the live record + current_user (same engine/env as a
// field-level visibleWhen). Omit = always available.
visibleWhen?: string;
}
gray- Default neutral colorred- For errors, urgent itemsorange- For warnings, high priorityyellow- For pending, attention neededgreen- For success, completedblue- For info, in progressindigo- For special itemspurple- For creative, designpink- For featured items
In tables/grids, select values are displayed as colored badges:
import { SelectCellRenderer } from '@object-ui/fields';
// Single value: Colored badge
// Multiple values: Multiple badges in a row
- Status Fields: Order status, task status
- Categories: Product categories, content types
- Tags: Multi-tag selection
- Priorities: Task or ticket priorities
- Roles: User roles or permissions