LoginSignup
1
1

More than 3 years have passed since last update.

railsの検索フォームで簡単に入力した値を戻す

Posted at

概要

検索用フォームは、例えばクエリに付く変数名を短くしたいとか、複数のモデルにまたがるとか、単純にカラムに対応してない値で検索させたいなど、モデルを使わず実装することが多いと思います。

モデルを使わないとinput-textやtextareaならまだしも、select、checkboxなどは入力させた値をHTMLタグに反映させるのに、IF文で分岐して属性値を書き込むので結構結構めんどくさいです。数が増えればなおさら。編集フォームの表にサクッと値を戻したい!というのが今回のテーマです。

ポイント

ポイントは3つです。

  • ActiveModel::ModelとActiveModel::Attributesを使って検索専用のモデルを作る。
  • ActiveRecord::Type::Valueを継承したカスタムタイプを使う。
  • パラメータにモデルの名前がつかないようにする。

検索用のモデル

# app/models/searches/sales_type.rb
module Searches
  class SalesType
    include ActiveModel::Model
    include ActiveModel::Attributes

    attribute :cid, :integer
    attribute :itid, :integer
    attribute :price_type, :string
    attribute :exclude_percent, :string
    attribute :user_relation, :string
    attribute :share, :string
    attribute :shop_ids_and, :boolean
    attribute :shop_ids, :integer_array
  end
end

読み込めるところだったらどこでもいいのですが、今回はapp/models/searches/に作ってみました。型に関してですが、booleanなんかは飛んでくるのは'1''0'なんですけど、form_withがモデルを想定してるのでtruefalseでもうまく値を戻してくれます。もちろんここは:stringでも期待通りの動きをします。:integerになってるところも:stringでも大丈夫ですね。まあ、なんとなく実際の型に合わせといたほうが落ち着きがいいかなと思いこうしました。

問題はinteger_arrayになってるところですね。これは後述するカスタムタイプです。ここはフォームを作るところで下記のように生成しています。

<%= form.collection_check_boxes(:shop_ids, Shop.alived.ordered, :id, :name, include_hidden: false) do |cb|%>
  <div class="form-check form-check-inline">
    <%= cb.check_box class: 'form-check-input' %>
    <%= cb.label class: 'form-check-label' do%>
      <%= cb.text %> 
    <%end%>
  </div>
<%- end -%>

こうするとidはintegerの配列になり、最終的なチェックinclude?で見ていますので文字列の配列だとチェックが戻りません(ここで結構ハマりました)。

カスタムタイプ

# config/initializers/active_model_types.rb

class IntegerArray < ActiveRecord::Type::Value
  def cast_value(value)
    value.map(&:to_i)
  end
end

ActiveModel::Type.register(:integer_array, IntegerArray)

カスタムタイプはフルにモデルで使うなら多分もっと色々設定しないとダメだと思いますが、今回は値を戻すためだけなのでこれだけでOKです。

パラメータからモデル名を削除

で、このモデルをViewで使えるようにして

def index
  @for_search = Searches::SalesType.new(params.permit(
    :cid,
    :itid,
    :price_type,
    :exclude_percent,
    :user_relation,
    :share,
    :shop_ids_and,
    shop_ids: []
  ))
<%= form_with model: @for_search, url: sales_type_index_path, local: true, method: :get do |form| %>

viewでこんな感じしてやれば値は戻るのですが、パラメターの名前にsearches_sales_type[cid]というながったらしいscope名が付きます。まあ、これでもrequireしてやれば動きますが、項目が多かったり、選択肢の多い配列があるとURLが長くなっちゃいます(URLの長さについては色々な調査をおこっなってる方がいらっしゃりますし、SEO的な観点の意見も散見されます。気になるか方は一度調べてみて下さい。)。

そこでsearches_sales_typeを外せないの?と思ってソースを追ってみたらscopeに空文字を渡すとなくなるのがわかりました。

<%= form_with model: @for_search, scope: '', url: sales_type_index_path, local: true, method: :get do |form| %>

ここscope ||= ...となっているのでnilでなければ空文字になり、名前はcidだけになります。値も無事戻りました。

ただ、ドキュメントにはないのでバージョンでどうなるかわかりません。そこは留意しておいてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1