3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails ブラウザでリアルタイム検索機能の実装(JavaScript)

Last updated at Posted at 2018-04-03

#目的
場所(Places)をデータベースからMVCまで1から作成、ブラウザではplaces#indexにてリアルタイムで出力する検索機能をJavaScriptにて実装

https://www.tam-tam.co.jp/tipsnote/javascript/post11315.html
↑スタート時何処から手付けるやらの時に非常に参考になりました
こちらを基に書き換えたモノですね

まだ未完成で最下段にエラーの詳細を乗せるのでお助けいただける方はぜひお願いしたいです。
ここから最低限のみ記載

##Migration

def change
 create_table :places do |t|
  t.string :name
  t.string :prefecture
  t.string :address
  t.timestamp
 end
end

他に緯度経度、備考等

##Model
バリデーション等好きなように

##Controller

class PlacesController < ApplicationController

def index
  @places = Place.all
end

#View(index.html.erb)メイン


<div class="container">
  <div class="card">
    <div class="card-header"></div>
     <div class="search-area">
        <%= form_for 送り先, url:送り先  do |f| %>
        <div class="form-group">
          <%= f.label :場所 %>
          <%= f.text_field :name, class: "w-100", id: "search-text", placeholder: "検索ワードを入力" %>
        </div>
        <div class='form-group w-100 px-1'>
          <%= f.label :都道府県, class:'w-100' %>
          <%= f.collection_select :prefecture, JpPrefecture::Prefecture.all, :name, :name, { include_blank: true }, class:'w-100', id: "search-address" %>
        </div>
<% end %>
     </div>

     <div class="new-place-link" style="color:blue;">
         <%= link_to "新しい場所", new_places_path, :style => "color:blue; float:right;" %>
     </div>
  </div>

    <div class="search-result">
       <div class="search-result__hit-num"></div>
     </div>

  </div>

<table class='table text-center'>
    <thead>
      <tr>
        <th>場所名</th>
        <th>住所</th>
        <th>今回は場所情報を別コントローラーに持って行きたかった</th>
        </tr>
    </thead>
    <tbody>
    <% @places.each do |p| %>
      <tr class="target-area">
        <td class="name"><%= p.name %></td>
        <td class="address"><%= p.prefecture%><%= p.address%></td>
        <td><%= link_to "こちら", 送り先path %></td>
      </tr>
    <% end %>
    </tbody>
 </table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>

<script>
// 入力の有無を判断する関数
function hasValue(val){
    if (val){
    return true;
    }else{
    return false;
    }
};
// グローバル変数で用意
var placeInput = document.getElementById("search-text");
var addressSelect = document.getElementById("search-address");

//  二つのフォームともに入力があった場合共通のレコードを探す関数定義
doubleInfo = function(){
var searchResult,
    targetPlace,
    targetAddress,
    hitNum;

    searchPlace   =   placeInput.value;
    searchAddress = addressSelect.value;
    searchResult  = [];

    $('.search-result__hit-num').empty();
    $('.target-area').hide();

     $('.target-area').each(function() {
        targetPlace = $(this).children('.name').text();
        targetAddress = $(this).children(".address").text();

      if (targetPlace.indexOf(searchPlace) != -1 && targetAddress.indexOf(searchAddress) != -1) {
        searchResult.push(targetPlace);
        $(this).show();
      }
     });

    // ヒットの件数をページに出力
    hitNum = '<span>検索結果</span>:' + searchResult.length + '件見つかりました。';
    $('.search-result__hit-num').append(hitNum);
};

// 場所名での検索関数定義
searchPlace = function(){
  if (hasValue(placeInput.value) && hasValue(addressSelect.value)){
    doubleInfo();
  }else if (hasValue(addressSelect.value)){
    searchAddress();
  }else{
    var searchResult,
        searchText = $(this).val(), // 検索ボックスに入力された値
        targetPlace,
        hitNum;

    // 検索結果を格納するための配列を用意
    searchResult = [];
    searchResultOfAdrress = [];

    // 検索結果エリアの表示を空にする
    $('.search-result__hit-num').empty();
    $('.target-area').hide();

    // 検索ボックスに値が入ってる場合
    if (searchText != '') {
      $('.target-area').each(function() {
        targetPlace = $(this).children('.name').text();

    // 検索対象となる入力された文字列が存在するかどうかを判断
        if (targetPlace.indexOf(searchText) != -1) {
          // 存在する場合はそのリストのテキストを用意した配列に格納(検索結果件数用)
        searchResult.push(targetPlace);
        $(this).show();
        }
    });

    // ヒットの件数をページに出力
    hitNum = '<span>検索結果</span>:' + searchResult.length + '件見つかりました。';
      $('.search-result__hit-num').append(hitNum);
    }
  }
};

// 住所検索機能の定義 流れはコート検索と同じ
searchAddress = function(){
  if (hasValue(placeInput.value) && hasValue(addressSelect.value)){
    doubleInfo();
  }else if (hasValue(placeInput.value) && !hasValue(addressSelect.value)){
    searchPlace();
  }else{
    var searchResult,
        searchAddress = $(this).val(),
        targetAddress,
        hitNum;
        searchResult = [];

    $('.search-result__hit-num').empty();
    $('.target-area').hide();

    if (searchAddress != '') {
      $('.target-area').each(function() {
        targetAddress = $(this).children(".address").text();
        if (targetAddress.indexOf(searchAddress) != -1) {
           searchResult.push(targetAddress);
           $(this).show();
        }
      });

       hitNum = '<span>検索結果</span>:' + searchResult.length + '件見つかりました。';
          $('.search-result__hit-num').append(hitNum);
    }
  }
};


// 関数発火ゾーン

// if文で片方だけ存在する場合探しに行く関数
// 入力がある度にコート、キーワード検索の実行
$('#search-text').on('input', searchPlace);
// selectされる度に住所検索の実行
$("#search-address").change(searchAddress);
</script>

##解説
(github からのコピーなので行頭の+は無視してもらって)
(フェイクを入れてるので若干ズレがあるかも)

今回のタスクではレコードの数が対して多くないので(現時点)コントローラで全件をインスタンス変数にいれてビューでテーブルにして出力してます。
が、cssでテーブル自体をdisplay:none;にしているのでindexページを出力すると検索フォームのみ。

form_for にする必要性は0だったのですが、都道府県のライブラリを使用する際に単なる
<form>タグ内での使用法に悩み、結果無理に送り先を作って実行するボタンを置かない事で実装しました。
↓都道府県ライブラリ使用法
https://chocoby.jp/blog/2013/02/19/jp-prefecture-gem/
どなたかform_for抜きの単なる<%=collection_select%>にて都道府県ライブラリの実装法を教えて頂けたらうれしいです

関数発火ゾーンにある通りinput側(場所名)は入力があるたびに、
collection_select側(都道府県)はドロップダウンメニューから選ばれた時に
関数が走ります。

最初はそれぞれを独立した機能として作成、後にdoubleInfo関数を作成して共通レコードの出力まで行いました。

参考にした記事にもありますようにIndexOfによって一部一致の場所名も返してくれます。
        例)場所名:タワー 都道府県:入力なし 
showされるレコード→    東京タワー    東京都港区~~~~~~
              横浜ランドマークタワー 神奈川県~~~~~ 
              神戸タワー       兵庫県~~~~~~
              タワーマンション  東京都品川区などなど
 

#解決したいエラー
##二つのフォームに入力をおこなった後、片方の入力を白紙にする場面

例)入力→         場所名:タワー 都道府県: 東京都
showされるレコード→    東京タワー    東京都港区~~~~~~ 
              タワーマンション  東京都品川区~~~~~

発火した関数の一番頭においてあるこの条件分岐ですが、ここで入力した「タワー」をbackspaceで入力無しにすると、東京都に該当するレコードを出力
を行いたいです。

searchPlace = function(){
  if (hasValue(placeInput.value) && hasValue(addressSelect.value)){
+    doubleInfo();
+  }else if (!hasValue(placeInput.value) && hasValue(addressSelect.value)){
+    searchAddress();
+  }else{



searchAddress = function(){
+  if (hasValue(placeInput.value) && hasValue(addressSelect.value)){
+    doubleInfo();
+  }else if (hasValue(placeInput.value) && !hasValue(addressSelect.value)){
+    searchPlace();
+  }else{

デベロッパーツールconsoleにて

Uncaught TypeError: searchAddress is not a function
    at HTMLSelectElement.searchPlace (places:382)
    at HTMLSelectElement.dispatch (jquery.min.js:3)
    at HTMLSelectElement.r.handle (jquery.min.js:3)

Uncaught TypeError: searchPlace is not a function
    at HTMLSelectElement.searchAddress (places:382)
    at HTMLSelectElement.dispatch (jquery.min.js:3)
    at HTMLSelectElement.r.handle (jquery.min.js:3)

逆もまた叱りで、都道府県を白紙に戻した時に「タワー」が場所名にはいったレコードを出力したいのですが上記のエラーを吐きます。
それぞれ条件分岐は通りますがあるはずの関数がないとのこと。
お助け願いたい...。

3
4
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
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?