railsでサジェスト機能を実装してみた


railsでサジェスト機能を実装した時のメモ🍣

autocomplete系のgemが軒並みメンテされていなかったので、jquery-uiで実装していきます。

動作環境

rails 5.2.2.1
jquery-ui-rails 6.0.1

やること

company属性を持つUserというモデルを想定し、
User作成画面でcompanyのテキストフィールドに入力した際、
既にDBに存在するデータをもとに候補をサジェストする機能を実装します😎

こういうやつ↓

Gemfile

Gemfileに下記を追加しbundle install

Gemfile
gem 'jquery-ui-rails'
bundle install

application.js

下記を追加

/assets/javascripts/application.js
//= require jquery-ui/widgets/autocomplete

application.scss

下記追加

/assets/stylesheets/application.scss
 @import "jquery-ui/autocomplete";
 @import "jquery-ui/theme";
 @import "jquery-ui/menu";

筆者環境だと、theme、menuを外すと表示が崩れました🤔

routes.rb

サジェストの配列を返すルートを追加します。

routes.rb
  resources :users do
    get '/autocomplete_company/:company', on: :collection, action: :autocomplete_company
  end

モデル

前方一致検索のscopeを追加します。

user.rb
  # 前方一致検索
  scope :by_company_like, lambda { |company|
    where('company LIKE :value', { value: "#{sanitize_sql_like(company)}%"})
  }

コントローラー

サジェストの候補を返すaction追加
受け取ったパラメータをもとにサジェストしたい文字列の配列を返すようにします。

user_controller.rb
  def autocomplete_company
    # params[:company]の値でUser.companyを前方一致検索、company列だけ取り出し、nilと空文字を取り除いた配列
    companies = User.by_company_like(autocomplete_params[:company]).pluck(:company).reject(&:blank?)
    render json: companies
    # レスポンスの例: ["てすと1会社","てすと2会社","てすと3会社"]
  end

  private

    def autocomplete_params
      params.permit(:company)
    end

ビュー

autocompleteのsourceに
サジェスト候補の配列を取得する関数をセット。

_form.html.slim
= form_with(model: @user, local: true) do |form|
 .form-group
  = form.label :company
  = form.text_field :company
  / id='user_company'で生成される

javascript:
  $(function() {
    const dataList = function(request, response) {
      $.ajax({
        url: '/users/autocomplete_company/' + request.term,
        dataType: 'json',
        type: 'GET',
        cache: true,
        success: function(data) {
          response(data);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
          response(['']);
        }
      });
    }

    // #user_companyの部分は必要に応じてidなり指定してください
    $('#user_company').autocomplete({
      source: dataList,
      autoFocus: true, // 自動的に先頭の項目にフォーカスするか
      delay: 300, // 入力してからサジェストが動くまでの時間(ms)
      minLength: 2 // 2文字入力しないとサジェストが動かない
    })
  });

テキストフィールドの内容に応じて、動的に内容を変える必要がなければ
dataListにサジェストしたい文字列の配列を直接入れればOKです!

javascript:
  $(function() {
    const dataList = ["こうほ1","こうほ2"];
  });

まとめ

jquery-uiを使って、サジェスト機能を実装しました。
今回は説明のため、Viewに直接javascriptを書いてますが
別ファイルにしたり、ヘルパーを切り出したりすると良いと思われます☺️

間違っているところがあればご指摘ください!

autocompleteのドキュメント