はじめに
ユーザー情報をインクリメタルサーチで検索しながらUIに優れた検索フォームを実装したいなと思い、その実装方法についてアウトプットしておきます。
今後はVue.jsをキャッチアップし実装し直す予定ですが、基本的な流れは変わらないだろうと思い、一旦実装しました。
目的
jQueryでリッチな検索フォームを実装する。
Rails5.2に載せる。
(前提、環境)
mac
macOS Mojave 10.14.5
ruby 2.5.1
rails 5.2.3
jQueryを導入
こちらはとても簡単なのですが、一応本題とは外れるので今度別途記事書きます。
一応やること
# Gemfile
(省略)
gem "jquery-rails"
# app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
// 上2行を追記
//= require_tree .
gem ”gem ‘jquery-ui-rails’ をインストール
# Gemfile
(省略)
gem "jquery-ui-rails"
# app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require jquery-ui/widgets/autocomplete
// 上1行を追記
//= require_tree .
初めの考え方
HTML5のautocompleteをうまく使えないかと考えていました。
色々調べ、jQueryのautocompleteというライブラリを使うことに決定。
公式
参考にした記事
autocomplete関数の使い方
以下が抽象的な使い方です。
オプションはもちろんこれだけではないので、公式を見てみてください!
$('レシーバー').autocomplete({
sources: データの塊 or function(request, response){
~~処理~~
response( データの塊 );
},
autoFocus: true or false,
delay: 何ミリ秒後に発火させたいか,
minlength: 最少文字数を定める,
select: 選んだ後に行う処理
});
参考記事
今回やりたかったこと
入力時にRailsでデータベースから値を検索して引っ張ってきたものを返す。
性的なデータを用意しておくのではなく、動的に動きそうな値の集合体を用意し、検索区結果に反映させる。
悩んだこと
たくさんありすぎて死にそうでしたw
(正直めちゃくちゃデバッグした。)
他の記事を見る限りだとめちゃくちゃ簡単そうに書いてあったので、すっかり騙されました(勝手に騙されている)笑
結果4時間くらい?かかってようやく実装できるというなんとも不甲斐ないことに・・・
①sourcesの値にデータを入れたいが、読みこむことができる値は1つだけ
ここは本当に公式を見逃して後悔したところなのですが、sourcesに入れておけるデータは
①{label: "test", value: 1}
②{name: "test"}
=>{label: "test", name: "test"}という形に変化する
というように、基本的にはレスポンスに渡せる値は1つでラベルを貼り付けることができるだけのようでした。
本来はデータベースから引っ張ってきたid(今回はUsersテーブルのid)と名前のどちらも渡したかったので苦労しました。。。とほほ
※ずっとjson形式のデータをsources内に渡していて、なんでレスポンスに入らないかわからなくて関数を外で定義して呼び出して無理やりなんとかできないか試したりしていました。
テストコード
$("#search_form").autocomplete({
source: [{ label: "test", value: 1 }, { label: "kumatest", value: 2 }, { label: "kumatest", value: 3 }],
autoFocus: true,
delay: 300,
minLength: 1
});
結果
https://gyazo.com/34594647b2dadbb6e5633723cb823ea8
②選んだプルダウンに出てくるもの(label)とinputタグに入る値(value)が最終的に関数が評価されたタイミングで決定する。
select(値が決まった後の処理を追記できる)ことを知り、labelとvalueでそれぞれidと名前を渡したらいけると思ったのですが、
『最終的にautocomplete関数が評価されたタイミングで決定する。』
のでいくらselectで関数を発火させて見た目を変更しても最終的に戻ってしまうので、ここでも吐きそうなくらいデバッグしていました笑
結論
『localStrageにjsonデータの一部を格納し、決定したタイミングでlocalStrageから値を引っ張ってくる。』
ブラウザにデータを預けておけば良いと思いつきました笑
今回は渡したいデータも結局は検索したいユーザーの名前とIDだけだったのでセキュリティ的に問題なし。
(IDだけでも値を渡せればRails側で検索できるため。)
具体的な方法
localStrageを使って値を取り出しています。
HTML5から使える機能ですが、今度別記事でまとめようと思います。
$("#search_form").autocomplete({
source: function (request, response) {
// 仕込みinputの初期化
var details = document.getElementById("details")
var details_child = details.childNodes[0];
if (details_child){
details.removeChild(details_child);
}
//
var suggests = [];
$.ajax({
url: 送りたいコントローラー/アクションのパス,
dataType: "json",
data: { input: request.term },
})
.done(function(users){
users.forEach( function(user){
user_profile = { label: user.name, value: user.name }
// 以下localStrageへの保存方法
var setjson_user = JSON.stringify(user);
localStorage.setItem(user.name, setjson_user);
//
//
//
// 検索結果をHTMLに返す
suggests.push( user_profile);
return response(suggests);
//
//
});
})
},
autoFocus: true,
delay: 300,
minLength: 1,
select: function(e, ui){
// uiのなかのitemにデータの塊が入っている。
if (ui.item) {
// 仕込み用のinputタグ生成。user_idを渡す。
var params = document.createElement("input");
params.setAttribute("type", "hidden");
params.setAttribute("name", "user[:id]");
params.setAttribute("class", "search_user_id");
params.setAttribute("data-name", ui.item.label);
// localStrageから取得。jsonで扱えるようにするt前にparseメソッドで変換する。
var getjson_user = localStorage.getItem(ui.item.value);
var user_details = JSON.parse(getjson_user);
//
params.value = user_details.id;
document.getElementById("details").appendChild(params);
//
//
//
//
}
}
});
}
終わりに
書いているときはautocompleteの機能に振り回されて死にそうでしたが、どうやって実装したらいいか考えるのはとても楽しいですね。
プログラミングの醍醐味が詰まっているなぁと感じました笑