LoginSignup
1
0

More than 1 year has passed since last update.

インクリメンタルサーチを実装 ajax jQuery rails 初心者むけ

Last updated at Posted at 2021-08-20

インクリメンタルサーチを実装する!

インクリメンタルサーチとは・・逐次検索です!

完成イメージ

あるワード「コロナ」を入れると、、そのワードがタイトル、コンテンツ内に含まれてる記事が出てきます。
さらに、「コロナ」から「コロナウイルス」にうち進めると...
さらに絞り込まれていきます。
ちなみに下に出てくる記事のタイトルは、リンクにしてるので
押したらそのまま、記事に飛べます。

頑張って実装したのでまとめていきます:fist:

スクリーンショット 2021-08-20 20.49.34.png
スクリーンショット 2021-08-20 20.49.58.png
スクリーンショット 2021-08-20 20.50.27.png

まずは通常の検索機能を実装します

以下の記事を参考に実装しました。

検索対象をタイトルとコンテンツにしたかったので、
モデルファイルはこんな感じにしてあります。

perl/model/post.rb
def self.search(search)
    return Post.all unless search
    Post.where('title LIKE(?)', "%#{search}%").or(Post.where('content LIKE(?)', "%#{search}%"))
end

さて、通常の検索機能がしっかり動くことを確認したら、
インクリメンタルサーチを実装していきます!!

Gem等の準備

Gemfile
gem 'jquery-rails'

gem入れたらbundle install

application.jsに
//= require jquery入れます。

application.js
//= require jquery
//= require jquery3
//= require popper
//= require bootstrap-sprockets

//= require summernote/summernote-bs4.min
//= require summernote-init


//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .

検索窓の編集

posts/_index.html.erb
 <div style="text-align:center;">
      <h6><i class="fas fa-search">投稿記事を探す</i></h6>
    </div>
      <%= form_with(url: posts_searches_path, local: true, method: :get, class: "search_form_posts") do |f|%>
      <%= f.text_field :posts_keyword, placeholder:"キーワード", class: "search_input_posts"%>
      <%= f.submit "検索", class: "search_btn"%>
      <% end %>
          <ul id="result_posts">
          </ul>

ここで大事なのは、クラス名の指定と、検索ワードを:posts_keywordで受け取るように
してるところでしょうか。
1つのアプリケーションで、部署検索、ユーザー検索のインクリメンタルサーチを
行ったのでごっちゃにならないようにしています。
<ul id="result_posts">ここは、
インクリメンタルサーチの検索候補が出てくるところです!

controllers/posts/searches_controller.rb
class Posts::SearchesController < ApplicationController
  def index
    posts = Post.search(params[:posts_keyword])
    @posts = posts.page(params[:page]).per(25)
    @value = params[:posts_keyword]
    @tag_list = Tag.order('id DESC').limit(20)
    respond_to do |format|
      format.json { render 'posts/index', json: @posts }
      format.html
    end
  end
end

posts = Post.search(params[:posts_keyword])
ここは、検索ワードを受け取る部分です。
Post.searchとなっているように、ここで検索ワードをpostモデルに渡して、
検索をかけています。
@posts = posts.page(params[:page]).per(25)
その検索結果が@postsに入ります。
@value = params[:posts_keyword]
ここは検索結果の表示画面で、
「〜〜の検索結果です。」と表示したいので定義してます。
そして、問題は以下ですね・・・

respond_to do |format|
      format.json { render 'posts/index', json: @posts }
      format.html
    end

json形式のデータを受け取ったら、
@postsをデータとして返し、render indexします という記述です。
ちなみに、 { render 'posts/index', json: @posts }書かないと、
何も起こらなくなってしまいますので忘れずに。

json形式って何??

Javascriptと相性が良い、ファイルの書き方のルールだそうです!
Javascript Object Notationの略です。

ちなみに、json.jbuilderを作成しているインクリメンタルサーチの
記事がありますが、今回の場合なくてもできます。
(あれを使うともっと高度なインクリメンタルサーチを作れるのでしょうか・・)

Javascriptを書いていく

namespace使ってるので、フォルダとファイルは以下の状態です。

スクリーンショット 2021-08-20 22.12.49.png

jsファイルは手動で作成したような気がします。。

javascript書いてると、すごく、警告マークみたいなのが出てきます:frowning2:
気にせず進めます・・・

スクリーンショット 2021-08-20 22.15.38.png

posts/search.js
// ターボリンクの個別無効化
$(document).on('turbolinks:load', function(){
// このアプリ(document)で、searc_inputで入力されるごとに、eという引数受け取る
  $(document).on('keyup', '.search_input_posts', function(e){
// キャンセル可能なイベントをキャンセル
    e.preventDefault();
// 入力された言葉を取得し、前後の余白をtrimで取り除き、inputに代入する
    var input = $.trim($(this).val());

// 非同期通信・行う場所・メソッド等、jsonで渡す
    $.ajax({
      url: '/posts/searches',
      type: 'GET',
// コントローラーに渡す値を入力された値にする
      data: ('posts_keyword=' + input),
      processData: false,
      contentType: false,
      dataType: 'json'
    })
// 通信が成功した時の処理が以下
    .done(function(data){
// idresultの子要素liを削除する
    $('#result_posts').find('li').remove();
// dateuserという変数に代入し以下を繰り返し処理する
    $(data).each(function(i,post) {
    $('#result_posts').append(`<li class="post"><a href="/posts/${post.id}">${post.title}</a></li>`)

});
    })
  });
});

:point_up:$(document).on('turbolinks:load', function(){
なんかjQueryがうまく動かないなーというときはこれが原因らしいので、
このように無効化します。個別に無効化するのがベストです。

:v:var input = $.trim($(this)で、前後の空白を取り除きます。
仮に、var str = ' foo ';
console.log(str.trim());
であれば出力はfooになります。

:point_up:thisは、.search_input_postsを呼び出しています。

:v:val());は、HTML要素の値を取得、変更したりしてくれます。
ユーザーがフォームに入力した内容や選択した項目を
jQuery側から把握できるようにしています。

:point_up:('#result_posts').find('li').remove()は、
検索窓の<ul id="result_posts"></ul>ここを指しています。
liタグはulタグの中に入ってる検索結果です。
ここを書かないと何も書いてない状態で、全記事のタイトルが、出てきてしまうかと思います。
なので一旦削除します。

:v:$(data).each(function(i,post) {
ここで、データを取り出して、postという変数に代入し、それ以降の作業を繰り返し行なっています。
(data).each(function(i,post)はjQueryのeachメソッドです。

$('#result_posts').append(`<li class="post"><a href="/posts/${post.id}">${post.title}</a></li>`)
append(content)で、要素である('#result_posts')にcontentsを追記していきます。

参考にした記事

難しい・・・

難しいですが、、頑張って1歩ずつ進んでいきます:frowning2:

1
0
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
0