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