インクリメンタルサーチを実装する!
インクリメンタルサーチとは・・逐次検索です!完成イメージ
あるワード「コロナ」を入れると、、そのワードがタイトル、コンテンツ内に含まれてる記事が出てきます。 さらに、「コロナ」から「コロナウイルス」にうち進めると... さらに絞り込まれていきます。 ちなみに下に出てくる記事のタイトルは、リンクにしてるので 押したらそのまま、記事に飛べます。頑張って実装したのでまとめていきます
まずは通常の検索機能を実装します
以下の記事を参考に実装しました。
検索対象をタイトルとコンテンツにしたかったので、
モデルファイルはこんな感じにしてあります。
def self.search(search)
return Post.all unless search
Post.where('title LIKE(?)', "%#{search}%").or(Post.where('content LIKE(?)', "%#{search}%"))
end
さて、通常の検索機能がしっかり動くことを確認したら、
インクリメンタルサーチを実装していきます!!
Gem等の準備
gem 'jquery-rails'
gem入れたらbundle install
application.jsに
//= require jquery
入れます。
//= 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 .
検索窓の編集
<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">
ここは、
インクリメンタルサーチの検索候補が出てくるところです!
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使ってるので、フォルダとファイルは以下の状態です。jsファイルは手動で作成したような気がします。。
javascript書いてると、すごく、警告マークみたいなのが出てきます
気にせず進めます・・・
// ターボリンクの個別無効化
$(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){
// idがresultの子要素liを削除する
$('#result_posts').find('li').remove();
// dateをuserという変数に代入し以下を繰り返し処理する
$(data).each(function(i,post) {
$('#result_posts').append(`<li class="post"><a href="/posts/${post.id}">${post.title}</a></li>`)
});
})
});
});
$(document).on('turbolinks:load', function(){
なんかjQueryがうまく動かないなーというときはこれが原因らしいので、
このように無効化します。個別に無効化するのがベストです。
var input = $.trim($(this)
で、前後の空白を取り除きます。
仮に、var str = ' foo '; console.log(str.trim());
であれば出力はfooになります。
this
は、.search_input_postsを呼び出しています。
val());
は、HTML要素の値を取得、変更したりしてくれます。
ユーザーがフォームに入力した内容や選択した項目を
jQuery側から把握できるようにしています。
('#result_posts').find('li').remove()は、
検索窓の<ul id="result_posts"></ul>
ここを指しています。
liタグはulタグの中に入ってる検索結果です。
ここを書かないと何も書いてない状態で、全記事のタイトルが、出てきてしまうかと思います。
なので一旦削除します。
$(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歩ずつ進んでいきます