使用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
相关产品推荐
相关产品推荐

