You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ruby on Rails中自定义多步骤表单(不使用gem)

嘿,我来帮你搞定Ruby on Rails里的多步骤表单问题!不管是纯手动实现(不依赖任何Gem)还是用你提到的Wicked,我都给你讲得明明白白~

一、不依赖Gem的自定义多步骤表单实现

这种方式完全可控,适合不想引入额外依赖的场景,核心思路是用session跟踪步骤、分步验证字段、拆分视图。

1. 状态跟踪与临时数据存储

首先得记录用户当前处于哪个步骤,同时临时保存已填写的字段(避免跳步后数据丢失)。推荐用session:

  • 在控制器的new动作里初始化步骤:session[:step] ||= 1
  • 用session[:business_params]来临时存储已提交的字段值

2. 模型层添加分步验证

借助ActiveModel的验证上下文,给不同步骤的字段设置专属验证规则:

class Business < ApplicationRecord
  # 第一步:主信息验证
  validates :name, :email, :phone, presence: true, on: :step1
  # 第二步:地址验证
  validates :address_line1, :city, :zip_code, presence: true, on: :step2
  # 第三步:经营地址验证
  validates :business_address_line1, :business_city, :business_zip_code, presence: true, on: :step3
end

3. 控制器逻辑实现

处理步骤切换、字段验证和数据提交:

class BusinessesController < ApplicationController
  def new
    @business = Business.new
    # 初始化步骤和临时存储
    session[:step] ||= 1
    session[:business_params] ||= {}
  end

  def create
    @business = Business.new(session[:business_params].merge(business_params))

    case session[:step]
    when 1
      # 只验证第一步的规则
      if @business.valid?(:step1)
        session[:business_params].merge!(business_params)
        session[:step] = 2
        render :new
      else
        render :new
      end
    when 2
      session[:business_params].merge!(business_params)
      if @business.valid?(:step2)
        session[:step] = 3
        render :new
      else
        render :new
      end
    when 3
      session[:business_params].merge!(business_params)
      @business.attributes = session[:business_params]
      if @business.save
        # 完成后清空session
        session[:step] = nil
        session[:business_params] = nil
        redirect_to businesses_path, notice: '企业信息创建成功!'
      else
        render :new
      end
    end
  end

  # 处理「上一步」逻辑
  def back
    if session[:step] > 1
      session[:step] -= 1
      @business = Business.new(session[:business_params])
      render :new
    else
      redirect_to new_business_path
    end
  end

  private

  def business_params
    params.require(:business).permit(step_permitted_fields)
  end

  # 根据当前步骤返回允许的字段
  def step_permitted_fields
    case session[:step]
    when 1 then [:name, :email, :phone]
    when 2 then [:address_line1, :address_line2, :city, :zip_code]
    when 3 then [:business_address_line1, :business_address_line2, :business_city, :business_zip_code]
    end
  end
end

4. 视图拆分与渲染

把不同步骤的表单拆成partial(比如_step1.html.erb、_step2.html.erb),然后在new.html.erb里根据当前步骤渲染对应的内容:

<h1>创建企业信息(第<%= session[:step] %>/3步)</h1>

<%= form_with(model: @business, url: businesses_path) do |form| %>
  <% if @business.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@business.errors.count, "错误") %>阻止了信息保存:</h2>
      <ul>
        <% @business.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <%= render "step#{session[:step]}", form: form %>

  <% if session[:step] > 1 %>
    <%= button_to "上一步", back_businesses_path, method: :get %>
  <% end %>
  <%= form.submit session[:step] == 3 ? "完成创建" : "下一步" %>
<% end %>

5. 配置路由

别忘了添加「上一步」的路由:

Rails.application.routes.draw do
  resources :businesses do
    collection do
      get :back
    end
  end
end
二、使用Wicked实现多步骤表单

Wicked是专门做多步骤表单的轻量Gem,能帮你简化步骤管理逻辑,不用自己写session的切换代码。

1. 安装与初始化

首先在Gemfile里添加:

gem 'wicked'

然后执行bundle install。

2. 生成步骤控制器

生成一个专门处理步骤的控制器,继承自Wicked::Wizard:

rails generate controller BusinessSteps

然后修改控制器代码:

class BusinessStepsController < Wicked::Wizard
  # 定义步骤顺序
  steps :main_info, :address, :business_address

  # 显示当前步骤的表单
  def show
    @business = Business.find(params[:business_id])
    render_wizard
  end

  # 提交当前步骤的数据
  def update
    @business = Business.find(params[:business_id])
    @business.update(business_params)
    # 自动跳转到下一步,验证失败则停留在当前步骤
    render_wizard @business
  end

  # 所有步骤完成后的跳转逻辑
  def after_all_steps
    redirect_to @business, notice: '企业信息创建成功!'
  end

  private

  def business_params
    params.require(:business).permit(permitted_fields)
  end

  # 根据当前步骤返回允许的字段
  def permitted_fields
    case step
    when :main_info then [:name, :email, :phone]
    when :address then [:address_line1, :address_line2, :city, :zip_code]
    when :business_address then [:business_address_line1, :business_address_line2, :business_city, :business_zip_code]
    end
  end
end

3. 调整主控制器逻辑

主控制器的create动作只需要创建一个未完成的记录,然后跳转到步骤流程:

class BusinessesController < ApplicationController
  def new
    @business = Business.new
  end

  def create
    @business = Business.new(business_params)
    # 先不验证,因为只填了部分字段
    if @business.save(validate: false)
      redirect_to business_steps_path(@business, step: :main_info)
    else
      render :new
    end
  end

  private

  def business_params
    params.require(:business).permit(:name) # 这里可以只允许第一步的初始字段
  end
end

4. 创建步骤视图

在app/views/business_steps/目录下创建对应步骤的视图文件,比如main_info.html.erb:

<h1>填写主信息</h1>
<%= form_with(model: @business, url: wizard_path, method: :put) do |form| %>
  <%= form.label :name %>
  <%= form.text_field :name %>

  <%= form.label :email %>
  <%= form.email_field :email %>

  <%= form.label :phone %>
  <%= form.text_field :phone %>

  <%= form.submit "下一步" %>
<% end %>

address.html.erb和business_address.html.erb同理,只渲染对应步骤的字段。

5. 配置路由

添加步骤控制器的路由:

Rails.application.routes.draw do
  resources :businesses
  resources :business_steps, only: [:show, :update]
end

内容的提问来源于stack exchange,提问作者Ivo Dimitrov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:59