Forms Reference
Complete reference for form customization using Phlexi::Form.
Overview
Plutonium forms are built on Phlexi::Form, providing a Ruby-first approach to form building.
Form Class Hierarchy
Phlexi::Form::Base
└── Plutonium::UI::Form::Base # Base with Plutonium components
├── Plutonium::UI::Form::Resource # Resource CRUD forms
│ └── Plutonium::UI::Form::Interaction # Interactive action forms
└── Plutonium::UI::Form::Query # Search/filter formsCustom Form Template
Override form rendering in your definition:
class PostDefinition < ResourceDefinition
class Form < Form
def form_template
# Your custom layout
render_fields
render_actions
end
end
endForm Template Methods
| Method | Description |
|---|---|
form_template | Main template method to override |
render_fields | Render all permitted fields |
render_resource_field(name) | Render a single field by name |
render_actions | Render submit buttons |
fields_wrapper { } | Wrapper div for field grid |
actions_wrapper { } | Wrapper div for buttons |
Form Attributes
| Attribute | Description |
|---|---|
object / record | The form object being edited |
resource_fields | Array of permitted field names |
resource_definition | The definition instance |
Custom Layouts
Sectioned Form
class PostDefinition < ResourceDefinition
class Form < Form
def form_template
section("Basic Information") {
render_resource_field :title
render_resource_field :slug
}
section("Content") {
render_resource_field :content
render_resource_field :excerpt
}
render_actions
end
private
def section(title, &)
div(class: "mb-8") {
h3(class: "text-lg font-semibold mb-4") { title }
fields_wrapper(&)
}
end
end
endTwo-Column Layout
class Form < Form
def form_template
div(class: "grid grid-cols-1 lg:grid-cols-3 gap-6") {
# Main content - 2 columns
div(class: "lg:col-span-2") {
fields_wrapper {
render_resource_field :title
render_resource_field :content
}
}
# Sidebar - 1 column
div(class: "space-y-4") {
Panel {
h4(class: "font-medium mb-2") { "Settings" }
render_resource_field :status
render_resource_field :visibility
}
}
}
render_actions
end
endField Builder
When using render_resource_field, Plutonium applies definition configuration. For custom rendering, use the field method directly.
Basic Field Usage
def form_template
# Using field builder directly
render field(:title).wrapped { |f| f.input_tag }
render field(:content).wrapped { |f| f.easymde_tag }
render field(:published).wrapped { |f| f.checkbox_tag }
render_actions
endStandard Tag Methods
| Method | Input Type |
|---|---|
f.input_tag | Text input (auto-detects type) |
f.string_tag | Text input |
f.text_tag | Textarea |
f.number_tag | Number input |
f.email_tag | Email input |
f.password_tag | Password input |
f.url_tag | URL input |
f.phone_tag | Telephone input |
f.hidden_tag | Hidden input |
f.date_tag | Date input |
f.time_tag | Time input |
f.datetime_tag | Datetime input |
f.checkbox_tag | Checkbox |
f.boolean_tag | Checkbox (themed as boolean) |
f.select_tag | Select dropdown |
f.radio_button_tag | Radio button |
f.collection_radio_buttons_tag | Radio button collection |
f.collection_checkboxes_tag | Checkbox collection |
f.range_tag | Range slider |
f.file_input_tag | File input |
Plutonium-Enhanced Tags
Plutonium extends the form builder with additional tags. See the Form::Base::Builder source for the current list.
Common ones include:
f.easymde_tag/f.markdown_tag- Markdown editorf.slim_select_tag- Enhanced selectf.flatpickr_tag- Date/time pickerf.uppy_tag/f.file_tag- File uploadf.secure_association_tag- Association with SGID
Field with Options
# Select with choices
render field(:status).wrapped { |f|
f.select_tag(choices: %w[draft published archived])
}
# Date picker with options
render field(:published_at).wrapped { |f|
f.flatpickr_tag(min_date: Date.today, enable_time: true)
}
# File upload with restrictions
render field(:avatar).wrapped { |f|
f.uppy_tag(
allowed_file_types: %w[.jpg .png .gif],
max_file_size: 5.megabytes
)
}Wrapped vs Unwrapped
# Wrapped - includes label, hint, errors
render field(:title).wrapped { |f| f.input_tag }
# Unwrapped - just the input element
render field(:title).input_tag
# Custom wrapper options
render field(:title).wrapped(class: "col-span-full") { |f|
f.input_tag
}Input Configuration in Definitions
Define inputs in the definition, render them in the form:
class PostDefinition < ResourceDefinition
# Configure inputs
input :title, hint: "Be descriptive", placeholder: "Enter title"
input :content, as: :markdown
input :status, as: :select, choices: %w[draft published]
input :published_at, as: :flatpickr
# Custom input with block
input :category do |f|
choices = Category.active.pluck(:name, :id)
f.select_tag(choices: choices)
end
class Form < Form
def form_template
# render_resource_field uses the input configuration
render_resource_field :title
render_resource_field :content
render_resource_field :status
render_resource_field :published_at
render_resource_field :category
render_actions
end
end
endNested Forms
For has_many / has_one associations with accepts_nested_attributes_for:
Model Setup
class Post < ResourceRecord
has_many :comments
accepts_nested_attributes_for :comments, allow_destroy: true
endDefinition Setup
class PostDefinition < ResourceDefinition
nested_input :comments do |n|
n.input :author_name
n.input :body, as: :text
end
# Or reference another definition
nested_input :comments, using: CommentDefinition, fields: %i[author_name body]
endRendering
class Form < Form
def form_template
render_resource_field :title
render_resource_field :content
# Nested fields are automatically handled
render_resource_field :comments
render_actions
end
endDynamic Forms (pre_submit)
Fields with pre_submit: true trigger form re-rendering on change:
class PostDefinition < ResourceDefinition
input :post_type, as: :select,
choices: %w[article video podcast],
pre_submit: true
input :video_url,
condition: -> { object.post_type == "video" }
input :podcast_url,
condition: -> { object.post_type == "podcast" }
endWhen post_type changes, the form re-renders via Turbo and shows/hides conditional fields.
Form Actions
Default Actions
def render_actions
actions_wrapper {
render submit_button
}
endCustom Actions
def render_actions
actions_wrapper {
# Cancel link
a(href: resource_url_for(resource_class), class: "btn btn-secondary") {
"Cancel"
}
# Save as draft
button(type: :submit, name: "draft", value: "1", class: "btn") {
"Save Draft"
}
# Primary submit
render submit_button
}
endForm Context
Inside form templates:
class Form < Form
def form_template
# Form object
object # The record
record # Alias for object
object.new_record? # Check if creating
# Request context
current_user
current_parent
request
params
# Definition
resource_definition
resource_fields # Permitted fields
# URL helpers
resource_url_for(object)
resource_url_for(Post, action: :new)
# Rails helpers
helpers.link_to(...)
end
endInteraction Forms
Forms for interactive actions:
class PublishPostInteraction < ResourceInteraction
attribute :publish_date, :date
attribute :notify_subscribers, :boolean, default: true
input :publish_date, as: :flatpickr
input :notify_subscribers
# Custom form (optional)
class Form < Plutonium::UI::Form::Interaction
def form_template
div(class: "space-y-4") {
render_resource_field :publish_date
render_resource_field :notify_subscribers
}
render_actions
end
end
endRelated
- Fields Reference - Input configuration
- Views Reference - Custom page classes
- Theming Guide - TailwindCSS and styling
