Rails表单构建器 #
一、表单构建器概述 #
1.1 什么是表单构建器 #
表单构建器是Rails提供的表单辅助方法,用于生成HTML表单元素,自动处理模型绑定、错误显示等功能。
1.2 主要方法 #
| 方法 | 说明 |
|---|---|
form_with |
创建表单 |
form_for |
旧版表单方法(已弃用) |
form_tag |
旧版表单方法(已弃用) |
二、form_with #
2.1 基本用法 #
erb
<!-- 创建模型表单 -->
<%= form_with model: @article do |form| %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
2.2 表单选项 #
erb
<!-- 指定URL和方法 -->
<%= form_with model: @article, url: articles_path, method: :post do |form| %>
...
<% end %>
<!-- 添加HTML属性 -->
<%= form_with model: @article, class: 'article-form', id: 'new_article' do |form| %>
...
<% end %>
<!-- 禁用远程表单 -->
<%= form_with model: @article, local: true do |form| %>
...
<% end %>
<!-- 添加数据属性 -->
<%= form_with model: @article, data: { turbo: false } do |form| %>
...
<% end %>
2.3 无模型表单 #
erb
<!-- 搜索表单 -->
<%= form_with url: search_path, method: :get, local: true do |form| %>
<%= form.text_field :query, placeholder: '搜索...' %>
<%= form.submit '搜索' %>
<% end %>
<!-- 登录表单 -->
<%= form_with url: login_path, scope: :session do |form| %>
<%= form.text_field :email %>
<%= form.password_field :password %>
<%= form.submit '登录' %>
<% end %>
三、表单字段 #
3.1 文本字段 #
erb
<%= form_with model: @user do |form| %>
<!-- 文本输入框 -->
<%= form.text_field :name %>
<!-- 密码输入框 -->
<%= form.password_field :password %>
<!-- 邮箱输入框 -->
<%= form.email_field :email %>
<!-- 电话输入框 -->
<%= form.telephone_field :phone %>
<!-- URL输入框 -->
<%= form.url_field :website %>
<!-- 搜索框 -->
<%= form.search_field :query %>
<% end %>
3.2 文本区域 #
erb
<%= form_with model: @article do |form| %>
<!-- 基本文本区域 -->
<%= form.text_area :body %>
<!-- 带属性 -->
<%= form.text_area :body, rows: 10, cols: 50, class: 'editor' %>
<!-- 带占位符 -->
<%= form.text_area :body, placeholder: '请输入内容...' %>
<% end %>
3.3 数字字段 #
erb
<%= form_with model: @product do |form| %>
<!-- 数字输入 -->
<%= form.number_field :price %>
<!-- 带范围 -->
<%= form.number_field :quantity, min: 1, max: 100, step: 1 %>
<!-- 范围滑块 -->
<%= form.range_field :rating, min: 1, max: 5 %>
<% end %>
3.4 日期时间字段 #
erb
<%= form_with model: @event do |form| %>
<!-- 日期选择 -->
<%= form.date_field :start_date %>
<!-- 时间选择 -->
<%= form.time_field :start_time %>
<!-- 日期时间选择 -->
<%= form.datetime_field :starts_at %>
<!-- 本地日期时间 -->
<%= form.datetime_local_field :starts_at %>
<!-- 月份选择 -->
<%= form.month_field :month %>
<!-- 周选择 -->
<%= form.week_field :week %>
<% end %>
3.5 其他字段 #
erb
<%= form_with model: @user do |form| %>
<!-- 隐藏字段 -->
<%= form.hidden_field :token %>
<!-- 颜色选择 -->
<%= form.color_field :theme_color %>
<!-- 文件上传 -->
<%= form.file_field :avatar %>
<!-- 复选框 -->
<%= form.check_box :agree_terms %>
<!-- 单选按钮 -->
<%= form.radio_button :gender, 'male' %>
<%= form.radio_button :gender, 'female' %>
<% end %>
四、选择框 #
4.1 基本选择框 #
erb
<%= form_with model: @article do |form| %>
<!-- 基本选择 -->
<%= form.select :status, [['草稿', 'draft'], ['发布', 'published']] %>
<!-- 带提示选项 -->
<%= form.select :category_id, Category.pluck(:name, :id), prompt: '选择分类' %>
<!-- 包含空白选项 -->
<%= form.select :author_id, Author.pluck(:name, :id), include_blank: true %>
<!-- 选定默认值 -->
<%= form.select :status, options_for_select([['草稿', 'draft'], ['发布', 'published']], 'draft') %>
<% end %>
4.2 分组选择框 #
erb
<%= form_with model: @article do |form| %>
<%= form.select :category_id, grouped_options_for_select({
'技术' => [['Ruby', 1], ['Rails', 2]],
'设计' => [['UI', 3], ['UX', 4]]
}) %>
<% end %>
4.3 时区选择 #
erb
<%= form_with model: @user do |form| %>
<%= form.time_zone_select :time_zone %>
<!-- 限定时区 -->
<%= form.time_zone_select :time_zone, ActiveSupport::TimeZone.us_zones %>
<% end %>
4.4 国家选择 #
erb
<%= form_with model: @user do |form| %>
<%= form.country_select :country %>
<!-- 限定国家 -->
<%= form.country_select :country, only: ['US', 'CN', 'JP'] %>
<% end %>
4.5 集合选择 #
erb
<%= form_with model: @article do |form| %>
<!-- 集合选择 -->
<%= form.collection_select :category_id, Category.all, :id, :name %>
<!-- 集合单选按钮 -->
<%= form.collection_radio_buttons :category_id, Category.all, :id, :name %>
<!-- 集合复选框 -->
<%= form.collection_check_boxes :tag_ids, Tag.all, :id, :name %>
<% end %>
五、标签和帮助文本 #
5.1 标签 #
erb
<%= form_with model: @article do |form| %>
<!-- 基本标签 -->
<%= form.label :title %>
<!-- 自定义文本 -->
<%= form.label :title, '文章标题' %>
<!-- 带HTML属性 -->
<%= form.label :title, class: 'form-label' %>
<!-- 块标签 -->
<%= form.label :title do %>
标题 <span class="required">*</span>
<% end %>
<% end %>
5.2 帮助文本 #
erb
<%= form_with model: @article do |form| %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
<small class="help-text">请输入5-100个字符</small>
</div>
<% end %>
六、错误显示 #
6.1 显示错误消息 #
erb
<%= form_with model: @article do |form| %>
<% if @article.errors.any? %>
<div class="error-messages">
<h2><%= pluralize(@article.errors.count, 'error') %> 阻止了保存:</h2>
<ul>
<% @article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
<% @article.errors[:title].each do |error| %>
<span class="error"><%= error %></span>
<% end %>
</div>
<% end %>
6.2 字段错误包装 #
erb
<%= form_with model: @article do |form| %>
<div class="field <%= 'field_with_errors' if @article.errors[:title].any? %>">
<%= form.label :title %>
<%= form.text_field :title %>
<% if @article.errors[:title].any? %>
<span class="error"><%= @article.errors[:title].join(', ') %></span>
<% end %>
</div>
<% end %>
七、嵌套表单 #
7.1 基本嵌套表单 #
ruby
# app/models/article.rb
class Article < ApplicationRecord
has_many :comments
accepts_nested_attributes_for :comments, allow_destroy: true
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def new
@article = Article.new
@article.comments.build
end
private
def article_params
params.require(:article).permit(
:title, :body,
comments_attributes: [:id, :body, :author, :_destroy]
)
end
end
erb
<!-- app/views/articles/_form.html.erb -->
<%= form_with model: @article do |form| %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<h3>评论</h3>
<%= form.fields_for :comments do |comment_form| %>
<div class="nested-fields">
<%= comment_form.label :author %>
<%= comment_form.text_field :author %>
<%= comment_form.label :body %>
<%= comment_form.text_area :body %>
<%= comment_form.check_box :_destroy %>
<%= comment_form.label :_destroy, '删除' %>
</div>
<% end %>
<%= form.submit %>
<% end %>
7.2 动态添加字段 #
erb
<!-- 使用cocoon gem -->
<%= form_with model: @article do |form| %>
<div id="comments">
<%= form.fields_for :comments do |comment_form| %>
<%= render 'comment_fields', f: comment_form %>
<% end %>
</div>
<%= link_to_add_association '添加评论', form, :comments %>
<% end %>
<!-- app/views/articles/_comment_fields.html.erb -->
<div class="nested-fields">
<%= f.text_field :author %>
<%= f.text_area :body %>
<%= link_to_remove_association '删除', f %>
</div>
八、自定义表单构建器 #
8.1 创建自定义构建器 #
ruby
# app/helpers/form_builder.rb
class FormBuilder < ActionView::Helpers::FormBuilder
def text_field(attribute, options = {})
label(attribute) + super(attribute, add_class(options, 'form-control'))
end
def text_area(attribute, options = {})
label(attribute) + super(attribute, add_class(options, 'form-control'))
end
def submit(value = nil, options = {})
value ||= '提交'
super(value, add_class(options, 'btn btn-primary'))
end
private
def add_class(options, class_name)
options[:class] = [options[:class], class_name].compact.join(' ')
options
end
end
8.2 使用自定义构建器 #
erb
<%= form_with model: @article, builder: FormBuilder do |form| %>
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= form.submit %>
<% end %>
8.3 全局设置构建器 #
ruby
# config/initializers/form_builder.rb
ActionView::Base.default_form_builder = FormBuilder
九、表单最佳实践 #
9.1 表单结构 #
erb
<%= form_with model: @article, class: 'form' do |form| %>
<!-- 错误消息 -->
<%= render 'shared/error_messages', object: @article %>
<!-- 字段组 -->
<div class="form-group">
<%= form.label :title, class: 'form-label' %>
<%= form.text_field :title, class: 'form-control' %>
<small class="form-text">请输入5-100个字符</small>
</div>
<div class="form-group">
<%= form.label :body, class: 'form-label' %>
<%= form.text_area :body, class: 'form-control', rows: 10 %>
</div>
<!-- 提交按钮 -->
<div class="form-actions">
<%= form.submit '保存', class: 'btn btn-primary' %>
<%= link_to '取消', articles_path, class: 'btn btn-secondary' %>
</div>
<% end %>
9.2 可访问性 #
erb
<%= form_with model: @article do |form| %>
<div class="form-group">
<%= form.label :title, for: 'article_title' %>
<%= form.text_field :title,
id: 'article_title',
aria: {
describedby: 'title_help',
required: true
} %>
<small id="title_help">请输入文章标题</small>
</div>
<% end %>
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| form_with | 创建表单 |
| 字段类型 | text、password、email等 |
| 选择框 | select、collection_select |
| 嵌套表单 | fields_for |
| 错误显示 | errors对象 |
| 自定义构建器 | 扩展表单功能 |
10.2 下一步 #
现在你已经掌握了表单构建器,接下来让我们学习 辅助方法,深入了解Rails的视图辅助方法!
最后更新:2026-03-28