そもそもインクリメンタルサーチとは
検索窓に文字を入力すると、入力ごとに候補を表示させる検索方法です。
Googleで何か文字を打つと、検索窓の下に、ピョコンと検索候補が出てきますよね。
あれです。
対象読者
インクリメンタルサーチに恐怖を覚えている人。
rails、jqueryの基礎はある程度理解している人。
準備
まず、サンプルとなるアプリケーションを作っていきましょう。
今回は、scaffoldを使用して進めていきます。
ユーザーの名前と年齢を登録できるアプリケーションを作成してみます。
ターミナルで、以下のコマンドを打ち込みます。
rails new scaffold_sample
rails g scaffold user name:string age:integer
すると、MVCで必要なファイルを一気に作成し、ルーティングまで設定してくれます。さらにbundle installまで行ってくれます。
マイグレーションファイルを確認して、データベースを作成しましょう。
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.integer :age
t.timestamps
end
end
end
rake db:migrate
これでデータベースも作成され、準備が整いました。
サーバーを立ち上げ、http://localhost:3000/users に接続し、正しく表示がされているか確認してみましょう。
rails s
画像のように表示されていれば成功です。
では、試しにユーザー登録をしてみます。
ユーザー情報がデータベースにしっかりと登録ができていれば、下の画像のように表示されます。
インクリメンタルサーチを実装する
さて、ここからいよいよインクリメンタルサーチを実装していきます。
検索語句を入力したら、その度にユーザー名が表示されるようなものを作っていきます。
ビューを編集する
まずは、検索窓をビューに作りましょう。
inputタグを使い、フォームを作ります。
<h1>Users</h1>
<!-- ここから追記 -->
<input type=form id="form" placeholder="ユーザー名を入力してください" style="width: 250px;"/>
jsファイルでは、id(もしくはクラス)を指定して実装を進めるので、必ずidを付与しておきます。
jsファイルで、検索語句を取得する
さて、これだけでは何も意味を持たないフォームができただけですね。
このフォームの情報をjsファイルで取得できるようにしましょう。
app/assets/javascriptに、user.js(名前は任意)というファイルを作成します。
その中に、以下のような記述を加えます。
$(document).on('turbolinks:load', function(){ //リロードしなくてもjsが動くようにする
$(document).on('keyup', '#form', function(e){ //このアプリケーション(document)の、formというid('#form')で、キーボードが押され指が離れた瞬間(.on('keyup'...))、eという引数を取って以下のことをしなさい(function(e))
e.preventDefault(); //キャンセル可能なイベントをキャンセル
var input = $.trim($(this).val()); //この要素に入力された語句を取得し($(this).val())、前後の不要な空白を取り除いた($.trim(...);)上でinputという変数に(var input =)代入
});
});
これでユーザーが入力した検索語句を取得することができました。
ajaxを用いてデータを渡す
このデータを、ajax通信を用いて、json形式でコントローラーに渡します。
下記のように追記しましょう。
$(document).on('turbolinks:load', function(){
$(document).on('keyup', '#form', function(e){
e.preventDefault();
var input = $.trim($(this).val());
//ここから追記
$.ajax({ //ajax通信で以下のことを行います
url: '/users/search', //urlを指定
type: 'GET', //メソッドを指定
data: ('keyword=' + input), //コントローラーに渡すデータを'keyword=input(入力された文字のことですね)'にするように指定
processData: false, //おまじない
contentType: false, //おまじない
dataType: 'json' //データ形式を指定
})
});
});
これで、ajax通信でデータをコントローラーに渡すことができました。
コントローラーを編集する
さて、ajaxにてURLは'/users/search'に、メソッドは'GET'を指定しました。
これの通り、ルーティングとコントローラーを設定します。
Rails.application.routes.draw do
resources :users do
collection do
get 'search'
end
end
end
def search
end
# privateの前に追記
このsearchアクションの中でデータを受け取り、検索をかけたいですね。
また、新たにsearchというビューをつくるのではなく、検索した名前はindexに表示させたいので、そのような処理もここに書きましょう。
def search
@users = User.where('name LIKE(?)', "%#{params[:keyword]}%") #paramsとして送られてきたkeyword(入力された語句)で、Userモデルのnameカラムを検索し、その結果を@usersに代入する
respond_to do |format|
format.json { render 'index', json: @users } #json形式のデータを受け取ったら、@usersをデータとして返す そしてindexをrenderで表示する
end
end
以下のように書くとよりすっきりします。
def search
@users = User.where('name LIKE(?)', "%#{params[:keyword]}%")
render json: @users
end
コントローラーで検索された内容をjsで表示できるようにする
さて、今@usersにはフォームに入力された語句と部分一致したユーザーの名前の情報が入っています。
この中から、一人ずつ名前を表示できるようにしたいですね。
ここから、一気に書いていきます。
まずは、ビューに検索結果を表示させる場所を作りましょう。
リストとして表示させたいので、ulタグを用意しておきます。
<input type=form id="form" placeholder="ユーザー名を入力してください" style="width: 250px;"/>
<!-- ここから追記 -->
<ul id="result">
<!-- この中に<li>デデデ大王</li>みたいに検索候補を追加していきます -->
</ul>
次に、ulタグの中に検索結果を表示させるコードを書いていきます。
まず、.doneを書きます。
意味としては、「ajax通信が成功したら」、という意味だと考えて良いでしょう。
そして、dataという形で、コントローラーで作成した@usersがjsに渡されています。
それを、each文で一つずつ取り出し、名前を表示させます。
$(document).on('turbolinks:load', function(){
$(document).on('keyup', '#form', function(e){
e.preventDefault();
var input = $.trim($(this).val());
$.ajax({
url: '/users/search',
type: 'GET',
data: ('keyword=' + input),
processData: false,
contentType: false,
dataType: 'json'
})
//ここから追記
.done(function(data){ //データを受け取ることに成功したら、dataを引数に取って以下のことする(dataには@usersが入っている状態ですね)
$('#result').find('li').remove(); //idがresultの子要素のliを削除する
$(data).each(function(i, user){ //dataをuserという変数に代入して、以下のことを繰り返し行う(単純なeach文ですね)
$('#result').append('<li>' + user.name + '</li>') //resultというidの要素に対して、<li>ユーザーの名前</li>を追加する。
});
})
});
});
これで全ての実装が終わりました!
見た目はしょぼいですが、以下のように表示されていれば成功です!
('#result').find('li').remove();
という記述をしているのは、同じユーザーネームでも、文字を打つたびに検索結果が追加されていってしまうからです。
例えば、'aaaaa'という名前のユーザーがいるとします。
aと入力→
aaaaa
と表示される
aaと入力→
aaaaa
aaaaa
と表示される
aaaと入力→
aaaaa
aaaaa
aaaaa
と表示される
実際に試してみると、そのような挙動をするはずです。
これを回避するために、このような記述が必要なのです。
最後に
最後まで読んでくださってありがとうございます!
インクリメンタルサーチと聞くと、それだけで難しい気がしてしまいますが、実際に行っていることを一つずつ追ってみると、以外と単純だったりします。
文字が入力される
↓
jsで読み取る
↓
コントローラーにjson形式でajax通信でデータを渡す
↓
そのデータを元に、like句を使って検索
↓
検索結果をjsに返す
↓
受け取ったデータをappendで表示する
私自身初心者ですが、わからないときは1から順に片付けていくと良いみたいですね!
参考資料
記事中では全く解説しなかったものについて、解説してくれているサイトたちです。
scaffold(スキャフォールド)について
覚えておくと超便利!Ruby on Railsのscaffoldの使い方【初心者向け】
Turbolinksについて
Turbolinks 5とTurbolinks Classic(2.x)の比較
preventDefault()について
preventDefault()について
ajaxについて
はじめてajaxを使うときに知りたかったこと
jsonとは
RailsでJSON形式のデータを取得し、結果を画面に表示させる
collection doについて
Rails のルーティング
LIKE句について
Railsであいまい検索(LIKE)やAND検索を行う