検索フォームなど、モデルに基づかないフォームを作る時に、ActiveModelを使ってフォームを作りたかったが、配列やハッシュをフォームで扱う時に若干詰まったのでメモ。
結論から言うと、スマートな方法ではないので、諦めた方が良さそう。
良さげな方法があったら教えてください。
例えば、レベルがmin〜maxの間のUserを検索するフォームを作りたいという場合で、levelをハッシュでやりとりしたい場合。
関係ない部分は諸々大分省略してるので脳内補完してください。
class UserSearchForm
include ActiveModel::Model
attr_accessor :name, :level
validates :name, length: { in: 6..20 }
end
<%= form_for(@user_search_form, url: search_user_url)
<%= f.text_field :name %>
<%= f.number_field :level, name: "user_search_form[level[min]]", value: @user_search_form.level.try(:fetch, :min, nil) %>
<%= f.number_field :level, name: "user_search_form[level[max]]", value: @user_search_form.try(:fetch, :max, nil) %>
<%= f.submit "検索" %>
<% end %>
class UsersController < ApplicationController
def search
@user_search_form = UserSearchForm.new(params[:user_search_form])
@results = User.order(:id)
@results = @results.where("name LIKE ?", @user_search_form.name) if @user_search_form.name.present?
if @user_search_form.level.present?
@results = @results.where("level >= #{@user_search_form.level[:min]}") if @user_search_form.level[:min].present?
@results = @results.where("level <= #{@user_search_form.level[:max]}") if @user_search_form.level[:max].present?
end
end
end
Viewのフォームでname属性を指定しているところがミソ。
これでControllerに値が渡る時にparams[:user_search_form]
内にlevel
がハッシュとして入る。
でも、valueをよしなにしてくれなかったりで、そもそもActiveModelを使う利点が薄れてしまっているのでかなり微妙。
今回の例なら、わざわざハッシュにしなくてもattr_accessor
に:level_min
と:level_max
を定義して使えば良さそう。
参考リンク
ActiveModelを使ってDBと関係ないFormを作成する
stackoverflow - Rails: form_for with field for Hash
hashに対してtryする