【Rails7】ransackとStimulusでインスタント検索機能を実装する方法

Railsで検索機能を実装するならransackというgemが有名どころですね。

ransackを導入すれば簡単に検索機能を実装することができます。

ただ、今回は普通の検索機能ではなく、以下のように入力したらすぐに検索結果を返してくれるインスタント検索機能を実装してみました。

今回インスタント検索機能を実装するにあたり、Rails7.0から標準搭載となった(Turboと相性の良い)StimulusというJavaScriptのライブラリを用いています。

目次

開発環境

  • Ruby 3.1.2
  • Ruby on Rails 7.0.3
  • Bootstrap 5.1.3
  • M1 Macbook Air 2020
  • mac OS Monterey (ver. 12.4)
  • ターミナル bash (Rosetta 2 使用

ransackで検索機能を実装する流れ

まずは、ransackで以下のような手動の検索機能を実装する流れについてみていきます。

その後にStimulusでインスタント検索を実装していくという流れで説明していきます。

gemの導入

gem "ransack"

上記をGemfileに記述し、$ bundle installします。

コントローラーに検索結果を返すメソッドを記述

検索フォームを導入するページのコントローラーに、ransackでの検索結果を返すメソッドを記述します。

当記事では、Postモデルのshowページに検索フォームを導入したいので、postsコントローラーのshowメソッドに以下のように記述します。

class PostsController < ApplicationController
  ・・・

  def show
    # params[:q]には検索フォームで指定した検索条件が入る
    @search = @post.items.ransack(params[:q])
    # デフォルトのソートを created_at 降順にする
    @search.sorts = "created_at DESC" if @search.sorts.empty?
    # @search.result で検索結果となる@itemsを取得する
    @items = @search.result
  end

  ・・・
end

viewファイルに検索フォームを設置する

ransackの検索フォームの設置には、search_form_for(ransackで用意されているメソッド)を用います。

Railsのform_withメソッドと使い方がほぼ同じなので使いやすいですね。

<%= search_form_for @search, url: post_path(@post) do |f| %>
  <div class="row g3 mb-3">
    <div class="col-4 col-xl-2">
      <%= f.label :item_number_start, "番号", class: "form-label fw-bold" %>
      <%= f.search_field :item_number_start, class: "form-control" %>
    </div>
    <div class="col-4 col-xl-2">
      <%= f.label :name_cont, "景品名", class: "form-label fw-bold" %>
      <%= f.search_field :name_cont, class: "form-control" %>
    </div>
    <div class="col-4 col-xl-2">
      <%= f.label :item_state_eq, "在庫", class: "form-label fw-bold" %>
      <%= f.select :item_state_eq, [['あり ◯', "exist" ], ['なし x', "taken"]], {include_blank: '選択なし'} ,class: 'form-select' %>
    </div>
  </div>
  <div>
    <%= button_tag(icon_with_text("search","検索"), class: "btn btn-primary") %>
    <%= link_to "リセット", post_path(@post), class: "btn btn-outline-secondary" %>
  </div>
<% end %>

ここで注目したいのが、各カラム名の後に付いている_start_cont_eqといった文字列です。

これらはransackで用意されたメソッドで、それぞれ以下の意味があります。

  • _start … 前方一致のとき
  • _cont … の部分が一致するとき
  • _eq … 完全に一致するとき

つまり、上記のコードでいう:name_contとすることで、検索ワードが:name(アイテム名)と部分一致したら検索結果として返してくれるということです。

基本的にカラム名に上記のメソッドを付け加えるだけで検索条件を指定できるので便利ですね。

ransackにはこの他にも便利なメソッドがたくさんあります。

詳しくは、以下の記事がとても参考になります↓

Pikawaka
ransackを使って検索機能がついたアプリを作ろう! Railsの検索機能を簡単に作成できるgemのransackの使い方を解説しています。gemの導入方法から実際の使い方まで、この記事を読めばransackの使い方をマスターすることがで...

これで検索フォームが利用できるようになりました。

インスタント検索機能の実装

さて、ここからはいよいよStimulusを使ってインスタントサーチを実装していきます。

Stimulusコントローラーの作成

まず、以下のコマンドでStimulusコントローラーを作成します。

(今回はsearchという名前のコントローラーを作成します)

$ rails g stimulus search

コマンドを実行すると、app/javascript/controllersディレクトリ内にsearch_controller.jsファイルが自動生成されます。

search_controller.jsの中身は以下のようになっています。

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="search"
export default class extends Controller {
  connect() {
  }
}

また、同一ディレクトリ内にあるindex.jsには以下のようなコードが追加されます。

// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

import HelloController from "./hello_controller"
application.register("hello", HelloController)

// searchコントローラー作成時に自動追記される(これがないとStimulusを利用できない)
import SearchController from "./search_controller"
application.register("search", SearchController)

作成したStimulusコントローラーにsubmitアクション(JavaScript)を記述

Stimulusコントローラーの中身を以下のように変更します。

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="search"
export default class extends Controller {
  // コントローラーに紐づく要素(=フォーム)をsubmitするアクション
  submit() {
    // セットされているTimeoutをクリアする
    clearTimeout(this.timeout)

    // Timeoutをセットする
    // 200ms後にリクエストを実行する
    // 連続で実行されるとTimeoutはクリアされるため、最後の処理だけしか実行されない
    this.timeout = setTimeout( ()=> {
      this.element.requestSubmit()
    }, 200)
  }
}

requestSubmit()はフォームをサーバーに送信するJavaScriptのメソッドです。

https://www.webdesignleaves.com/pr/jquery/javaascript_04.php#h4_index_14

上記のコード(アクション)では、検索フォームに何かしらの入力(リクエスト)をしてから200ms後にrequestSubmit()を実行するようにしています。

検索フォーム(search_form_for)にdata-controllerdata-actionを指定

search_controller.jsに記述したアクションを検索フォームに適用させるため、以下のようにdata-controllerdata-actionを指定します。

<%= search_form_for @search,
      url: post_path(@post),
      html: {
        data: {
          controller: "search",
          action: "input->search#submit"
          }
       } do |f| %>

  ・・・

<% end %>

これでインスタントサーチが利用できるようになるはずです。

検索ボタンの削除

インスタントサーチが利用できるようになると、検索フォームに設置済みの「検索する」ボタンが不要になるので削除しておきましょう。

<%= search_form_for @search, url: post_path(@post) do |f| %>
  
  ・・・

  <div>
    <!-- 検索ボタンを削除する -->
    <!-- <%= button_tag(icon_with_text("search","検索"), class: "btn btn-primary") %> -->
    <%= link_to "リセット", post_path(@post), class: "btn btn-outline-secondary" %>
  </div>
<% end %>

これでスッキリですね。

以下のようにインスタントサーチが機能すれば完成です。

参考資料

Pikawaka
ransackを使って検索機能がついたアプリを作ろう! Railsの検索機能を簡単に作成できるgemのransackの使い方を解説しています。gemの導入方法から実際の使い方まで、この記事を読めばransackの使い方をマスターすることがで...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次