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

使用form_with时丢失params[:search]哈希,无法保留表单选中值的问题

问题描述

我有一个Rails表单,通过form_with以GET方法提交,配合params[:search]哈希实现航班搜索,表单提交后能正常筛选结果。现在想通过Model自动填充表单提交后的选中值,避免用户选择的内容消失,于是修改了表单和控制器,但修改后丢失了原本的params[:search]哈希,导致无法回显选中值。我是Ruby/Rails新手,求解决办法。

原表单代码

<%= form_with method: :get do |f| %>
    <div class='search_param'>
        <h3>Departure Airport:</h3>
        <%= select_tag('search[departure_airport_id]', options_for_select(@airports, params.dig(:search, :departure_airport_id)), :prompt => '-') %> 
    </div>
    <div class='search_param'>
        <h3>Arrival Airport:</h3>
        <%= select_tag('search[arrival_airport_id]', options_for_select(@airports, params.dig(:search, :arrival_airport_id)), :prompt => '-') %> 
    </div>
    <div class='search_param'>
        <h3>Number of Passengers:</h3>
        <%= select_tag('search[passengers]', options_for_select([1, 2, 3, 4], params.dig(:search, :passengers)), :prompt => '-') %>
    </div>
    <div class='search_param'>
        <h3>Date:</h3>
        <%= select_tag('search[flight_date]', options_from_collection_for_select(@flights, 'flight_date', :flight_date_formatted, params.dig(:search, :flight_date)), :prompt => '-') %>
    </div>

    <%= f.submit 'Search' %>
<% end %>

原控制器代码

class FlightsController < ApplicationController
  def index
    @airports = Airport.all.map{|a| [a.airport_city, a.id]}
    @flights = Flight.all
    @search = params[:search] ? search_params.select{|k, v| v != ''} : nil
    if @search
      @results = !@search.empty? ? Flight.where(@search).includes(:departure_airport, :arrival_airport) : @flights
    end
  end

  private

  def search_params
    params.require(:search).permit(:departure_airport_id, :arrival_airport_id, :flight_date)
  end
end

修改后的表单代码

<%= form_with model: @flights, method: :get do |f| %>
        <div class='search_param'>
            <h3>Departure Airport:</h3>
            <%= f.select(:departure_airport_id, options_for_select(@airports), :prompt => '-') %> 
        </div>
        <div class='search_param'>
            <h3>Arrival Airport:</h3>
            <%= f.select(:arrival_airport_id, options_for_select(@airports), :prompt => '-') %> 
        </div>
        <div class='search_param'>
            <h3>Number of Passengers:</h3>
            <%= f.select(:passengers, options_for_select([1, 2, 3, 4]), :prompt => '-') %>
        </div>
        <div class='search_param'>
            <h3>Date:</h3>
            <%= f.select(:flight_date, options_from_collection_for_select(@flights, 'flight_date', :flight_date_formatted), :prompt => '-') %>
        </div>

        <%= f.submit 'Search' %>
    <% end %>

修改后的控制器代码

class FlightsController < ApplicationController
  def index
    @flight = params[:search] ? Flight.new(search_params) : Flight.new
    @airports = Airport.all.map{|a| [a.airport_city, a.id]}
    @flights = Flight.all
    @search = params[:search] ? search_params.select{|k, v| v != ''} : nil
    if @search
      @results = !@search.empty? ? Flight.where(@search).includes(:departure_airport, :arrival_airport) : @flights
    end
  end

  private

  def search_params
    params.require(:search).permit(:departure_airport_id, :arrival_airport_id, :flight_date)
  end
end
解决方案

问题出在修改后的表单绑定了集合对象@flights,导致参数命名空间和回显逻辑失效。以下是两种可行方案:

方案一:绑定单个Model对象实现自动回显

修正后的控制器代码

class FlightsController < ApplicationController
  def index
    # 用单个Flight对象承载搜索参数,自动回显表单值
    @flight = Flight.new(search_params)
    @airports = Airport.all.map { |a| [a.airport_city, a.id] }
    @flights = Flight.all

    # 过滤空参数,用于查询航班
    @search = search_params.to_h.select { |k, v| v.present? }
    @results = @search.present? ? Flight.where(@search).includes(:departure_airport, :arrival_airport) : @flights
  end

  private

  def search_params
    # 从:flight命名空间取参数(绑定model后自动生成),同时补上passengers参数
    params.fetch(:flight, {}).permit(:departure_airport_id, :arrival_airport_id, :passengers, :flight_date)
  end
end

修正后的表单代码

<%= form_with model: @flight, method: :get do |f| %>
  <div class='search_param'>
    <h3>Departure Airport:</h3>
    <%= f.select(:departure_airport_id, options_for_select(@airports), prompt: '-') %> 
  </div>
  <div class='search_param'>
    <h3>Arrival Airport:</h3>
    <%= f.select(:arrival_airport_id, options_for_select(@airports), prompt: '-') %> 
  </div>
  <div class='search_param'>
    <h3>Number of Passengers:</h3>
    <%= f.select(:passengers, options_for_select([1, 2, 3, 4]), prompt: '-') %>
  </div>
  <div class='search_param'>
    <h3>Date:</h3>
    <%= f.select(:flight_date, options_from_collection_for_select(@flights, 'flight_date', :flight_date_formatted), prompt: '-') %>
  </div>

  <%= f.submit 'Search' %>
<% end %>

说明:

  • 绑定单个@flight对象(而非集合@flights),表单参数会自动以:flight为命名空间,控制器通过params[:flight]获取参数
  • Form Builder的f.select会自动读取@flight的属性值,实现选中值回显,无需手动指定selected参数
  • 补上之前Strong Params遗漏的passengers参数,确保该参数能被正确接收

方案二:保持params[:search]命名空间,手动指定选中值

如果希望继续使用原有的params[:search]结构,可以用scope: :search替代model绑定:

修正后的表单代码

<%= form_with scope: :search, method: :get do |f| %>
  <div class='search_param'>
    <h3>Departure Airport:</h3>
    <%= f.select(:departure_airport_id, options_for_select(@airports), prompt: '-', selected: params.dig(:search, :departure_airport_id)) %> 
  </div>
  <div class='search_param'>
    <h3>Arrival Airport:</h3>
    <%= f.select(:arrival_airport_id, options_for_select(@airports), prompt: '-', selected: params.dig(:search, :arrival_airport_id)) %> 
  </div>
  <div class='search_param'>
    <h3>Number of Passengers:</h3>
    <%= f.select(:passengers, options_for_select([1, 2, 3, 4]), prompt: '-', selected: params.dig(:search, :passengers)) %>
  </div>
  <div class='search_param'>
    <h3>Date:</h3>
    <%= f.select(:flight_date, options_from_collection_for_select(@flights, 'flight_date', :flight_date_formatted), prompt: '-', selected: params.dig(:search, :flight_date)) %>
  </div>

  <%= f.submit 'Search' %>
<% end %>

修正后的控制器代码

class FlightsController < ApplicationController
  def index
    @airports = Airport.all.map { |a| [a.airport_city, a.id] }
    @flights = Flight.all

    @search = params[:search] ? search_params.select { |k, v| v.present? } : nil
    @results = @search.present? ? Flight.where(@search).includes(:departure_airport, :arrival_airport) : @flights
  end

  private

  def search_params
    # 保持原有的:search命名空间,补上passengers参数
    params.require(:search).permit(:departure_airport_id, :arrival_airport_id, :passengers, :flight_date)
  end
end

说明:

  • 使用scope: :search让表单参数保持在params[:search]哈希中
  • 通过selected: params.dig(:search, :key)手动指定选中值,实现回显
  • 同样需要在Strong Params中添加passengers参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:24:16