Fields
As of 0.4.0, every field type — built-in or unknown — resolves through a
single rendering path (a total registry keyed by type:). An unrecognized
type: no longer errors: it renders a plain text field, the same fallback
built-ins like :text and :string use. This is a mechanism change only;
recognized types render exactly as before.
Fields are defined in the resource form do ... end block:
form do
field :name
field :status, type: :select, collection: [["Active", "active"], ["Inactive", "inactive"]]
end
Common options
All fields support:
type:(defaults to:text)required:(true/false)label:(String)help:(String)placeholder:(String)readonly:(true/false)if:Proc (render only if truthy)unless:Proc (render only if falsy)
Example conditional field:
field :admin_notes, type: :textarea, if: ->(record) { record.admin? }
Supported field types
Text-like
:text(default):textarea(rows:supported):email:url:number:date:time:datetime
Toggle
:toggle(renders a switch)
field :enabled, type: :toggle
Select
:select(uses Railsselect)
Options:
collection:Array of[label, value]or simple values
field :status, type: :select, collection: [["Active", "active"], ["Inactive", "inactive"]]
Searchable select
:searchable_select(Stimulus-powered searchable dropdown)
Options:
collection:either:- an Array (static options), or
- a String URL (advanced; used by the JS controller as a “search URL”,
always wins over
resource:below when present)
resource:(Symbol/String, new in 0.5.0) — the AdminSuite resource key to search remotely (e.g.resource: :companies); see Remote search viaresource:belowcreate_url:(String) enables “creatable” behavior in the UI
field :company_id,
type: :searchable_select,
collection: Company.order(:name).pluck(:name, :id),
placeholder: "Search companies..."
Remote search via resource:
As of 0.5.0, AdminSuite provides its own JSON search endpoint, so hosts no
longer need to hand-build a search action for searchable_select. Give the
field a resource: instead of a static collection: array:
field :company_id, type: :searchable_select, resource: :companies
This resolves (at render time, by looking up Admin::Base::Resource
.registered_resources for a matching plural or singular resource name) to
GET <mount>/:portal/:resource_name/search?q=<term> — the same route the
resource’s own admin pages are served from. If resource: doesn’t resolve
(a typo, an unregistered key, or a resource with no portal_name), the
field renders with no remote search configured rather than raising — it
degrades the same way a bad collection: URL would.
A String collection: still overrides this unconditionally — set both and
collection: wins, matching pre-0.5.0 behavior exactly.
The endpoint’s contract (GET .../:resource_name/search?q=term):
- Requires authentication (the engine’s existing
admin_suite_authenticate!) andconfig.authorizewithaction: :read— a denied actor gets403, same as any other resource action. - Only searches the resource’s declared
searchablefields (index do searchable :name, :email end) — never an arbitrary column, and never the whole table. - Hard-capped at 25 results, regardless of how many rows match.
- An unknown/unregistered resource name in the URL 404s, same as every other resource route.
- A blank or missing
qreturns[]— never the unfiltered table (this differs slightly from the index page’s own search box, which falls back to the unfiltered scope on a blank query; a raw JSON endpoint must not). - A resource with no
searchablefields declared (or noindexblock at all) also returns[]. - Response shape is a bare JSON array of
{ "id": ..., "name": ... }objects —namefalls back totitle, thento_s, a smaller version of the same “try a few label-ish methods” ideaitem_display_titleuses elsewhere in the gem (that one also triesdisplay_titleand a truncatedcontentbefore falling back to"##{id}"; this endpoint doesn’t need that many rungs, since a record with none ofname/titlejust falls through to its own#to_s). This is exactly whatsearchable_select_controller.js’sfetchOptionsalready expects (item.id,item.name), so no JS change is required.
The search term is always bound (never interpolated) into the SQL, so the
endpoint carries the same injection-safety guarantee as the index’s
existing search box. It also inherits that same box’s ILIKE characteristic
that %/_ in the value act as SQL wildcards — worth knowing if this
field is exposed to less-trusted input than the index page’s own search box.
Multi-select & tags
:multi_select:tags
Options:
collection:Array of options (used for suggestions)create_url:enables “creatable” behaviormultiple:boolean (reserved; arrays are permitted automatically)
Notes:
- These submit arrays and are permitted automatically by AdminSuite.
- For
:tags, AdminSuite uses atag_listparameter by default (or#{field_name}_listif your model exposes it).
field :tag_list, type: :tags, placeholder: "Add tags..."
field :roles, type: :multi_select, collection: %w[admin editor viewer]
File uploads / attachments
:file:attachment:image
Options:
accept:MIME accept string (e.g."image/*","application/pdf")
These assume your host app uses Active Storage.
field :avatar, type: :image, accept: "image/*"
field :resume, type: :file, accept: "application/pdf"
Rich text
:trix:rich_text
These assume your host app uses Action Text.
field :bio, type: :rich_text
Markdown
:markdown(textarea enhanced by EasyMDE, vendored with the engine — no CDN, works air-gapped and under a strict CSP)
field :prompt_template, type: :markdown, rows: 16
JSON editor
:json(renders the engine’s JSON editor partial)
field :settings, type: :json
Code editor
:code(monospace editor container; enhanced by engine JS)
field :ruby_code, type: :code, rows: 20
Label (read-only)
:labelrenders a badge-like value (useful for status fields)
Options:
label_color:Symbol or Proclabel_size::sm/:mdor Proc
field :status, type: :label, label_color: ->(r) { r.active? ? :emerald : :slate }, label_size: :sm
Layout helpers
Inside form do ... end you can group fields:
section
section "Billing", description: "Payment settings", collapsible: true do
field :stripe_customer_id, readonly: true
end
row
row cols: 2 do
field :first_name
field :last_name
end