Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8725

検索機能で検索boxの下に候補を出す方法

$
0
0

0 はじめに

 今回は以下の写真のように、ユーザーが検索する時の助けになるように、boxの下に候補を出す方法を記述します。
 もっといい方法があるとは思いますが、今の私の理解力と技術ではこれがベストです。

バージョンです

 version ruby 2.5.1p57
          Rails 5.2.4.1

1 実装の流れ

a) controllerで候補に出すデータをインスタンス変数に入れる

b) viewにinputタグ(type:hidden)を記述。その中に1の変数を記述

c) 検索boxに入力されるたびに発火させ処理させる

d) 検索boxに入力された値をもとに正規表現を作成

e) インスタンス変数の値をeachで取り出して正規表現とマッチするか調べる

f) マッチしたものだけ関数を使って要素を作り、検索boxにappendする

2 コード・解説

a) controllerで候補に出すデータをインスタンス変数に入れる

jsサイドにデータを渡すためのコードです。
今回は「new.html.haml」 「edit.html.haml」以外に検索機能を実装します。productsコントローラーにbeforeアクションを設定しました。@wordに商品名を入れ、@id_boxにproduct.idを入れます。

products_controller.rb
classProductsController<ApplicationControllerbefore_action:set_product_name,except: [:new,:edit]---省略---defset_product_name@product_for_search=Product.all@wordbox=[]@id_box=[]@product_for_search.eachdo|product|@id_box<<product.id   @word_box<<product.nameendendend

b) viewにinputタグ(type:hidden)を記述。その中に1の変数を記述

 jsが発火した時にここから商品名及び、商品idを取得できるようにします。
 一番下の#result-wordの下にliで候補を表示します

index.html.haml
=form_with(url: products_searches_path,local: true,method: :get,class: "search-form")do|form| .search-group
  = form.text_field :keyword, placeholder: "商品を検索する", class: "main-header__search-box", id: "_products_searches_keyword"
  = form.label :keyword, for: "search-btn", class: "search-label" do
   = form.submit "検索", class: "search-btn", id: "search-btn", style: "display: none"
   = image_tag 'icon-search 1.png',size: "30x25",class: "main-header__search-img"
  %input{name: "search-word-list", type: "hidden", value: @wordbox, class: 'search_word_list' }  ←この行を入れる
  %input{name: "search-id-list", type: "hidden", value: @id_box, class: 'search_id_list' }     ←この行を入れる
 %ul#result-word ←この下に候補用の要素を追加する

c) 検索boxに入力されるたびに発火させ処理させる

jsファイル完全版

search.js
$(document).on('turbolinks:load',function(){varsearchWordList=$('.search_word_list').val();functionappendList(word,number){letitem=$(`
    <li class="list result-list">
      <a href = "/products/${number}" class="search-word-list">
        <p>${word}</p>
    `);$("#result-word").append(item);}functioneditElement(element){if(element!=""){letresult="^"+element;returnresult;}else{letresult="$^";returnresult;}}$("#_products_searches_keyword").on("keyup",function(){letinput=$("#_products_searches_keyword").val();if(input==""){$("#result-word").empty();}else{letinputs=input.split("");letnewInputs=inputs.map(editElement);letreg=RegExp(newInputs.join("|"));$.each(JSON.parse(searchWordList),function(i,word){varsearchIdList=$('.search_id_list').val();searchIdList=JSON.parse(searchIdList)if(word.match(reg)){appendList(word,searchIdList[i]);}});};});});

search.jsの詳細コード

search.js
varsearchWordList=$('.search_word_list').val();⬅️①$("#_products_searches_keyword").on("keyup",function(){ ⬅️②letinput=$("#_products_searches_keyword").val();if(input==""){$("#result-word").empty();}else{letinputs=input.split("");letnewInputs=inputs.map(editElement);letreg=RegExp(newInputs.join("|"));---省略---});};});

① ページが読み込まれた時に商品の名前、idを取得する
② ↓入力されるたびに候補が出て欲しいので、keyupを使って発火させる。
  ↓発火すると検索boxに入れられた値を取得する
  ↓何も入力されていなければ候補用の要素を削除する
  ↓入力された検索ワードが複数ある場合はスペースで区切り、配列(inputs)にする
  ↓配列(inputs)をmapで新しい配列にする d)に飛ぶ
  ↓正規表現用の変数を用意し、先ほど ^ マークを付けた配列を"|"(「または」を意味する)を間に入れて合体させる。

d) 検索boxに入力された値をもとに正規表現を作成

search.js
functioneditElement(element){if(element!=""){letresult="^"+element;returnresult;}else{letresult="$^";returnresult;}}

inputで分けられた配列の中身があるときだけ検索ワードの先頭に ^ マークをつける。「test ␣」のようにスペースが空いている場合は「"^test", "^"」というふうに配列が作成されるので、すべての商品名がヒットしてしまう。「""」の時は ^ マークは付けないようにする。 b)に戻る。

e) インスタンス変数の値をeachで取り出して正規表現とマッチするか調べる

search.js
$.each(JSON.parse(searchWordList),function(i,word){varsearchIdList=$('.search_id_list').val();searchIdList=JSON.parse(searchIdList)if(word.match(reg)){appendList(word,searchIdList[i]);}});

 一行目のJSON.parseはsearchWordListをobjectに変換するためのメソッド
 jsのeachはobjectに対して処理を行うようでこの記述がないとエラーが表示される。(理解仕切れていません)

Uncaught TypeError: Cannot use 'in' operator to search for 'length' in ["aa", "test", "選択してくださいテスト", "test-sample", "服"]

商品のidにも同じように記述する
はじめは商品のidを渡さずに、eachのindexを使ってリンクのURLを作成していたが、本番環境でエラーが出たので、書き換えました。

f) e)のeachでマッチしたものだけ、関数を使って要素を作り、検索boxにappendする

search.js
functionappendList(word,number){letitem=$(`
   <li class="list result-list">
    <a href = "/products/${number}" class="search-word-list">
     <p>${word}</p>
  `);$("#result-word").append(item);}

候補をクリックするとその商品のページに移動するためリンクを作成しています。その時にproduct.idが必要なめviewから取得しました。

 

3 最後に

今回のポイントは
・商品のデータをビューからjsに渡す
・検索ワードを入力している途中でスペースが入った時にすべての商品名がヒットしてしまうので、スペースには ^ マークを付けない
・JSON.parseでobjectに変換する
・検索ワードを正規表現にして該当するものだけヒットするようにする

以上です。
最後までご覧いただきありがとうございました。


Viewing all articles
Browse latest Browse all 8725

Latest Images

Trending Articles

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>