suggestrapでフォーム入力時のサジェスト機能を実装する

![49056240-4c7db100-f23e-11e8-9a2e-863f59ad84a7.gif](https://qiita-image-store.s3.amazonaws.com/0/47583/bec073f9-8afc-5e51-7fcf-0a3b4c20a3e9.gif)
[suggestrap](https://github.com/mrhorin/suggestrap)はユーザがフォームに値を入力した時に、その入力された値を元にサジェストを自動表示してくれるJavaScriptライブラリです。サジェストは「オートコンプリート」とも言いますが、ここでは「サジェスト」で統一します。
表示するサジェストの一覧データを取得する方法には、[JSONサーバからサジェストを取得する方法](#jsonサーバからサジェストを取得する方法)と、予め用意したJavaScriptの配列内に格納された値を利用して[JSONサーバなしでサジェストを取得する方法](#jsonサーバなしでサジェストを取得する方法)の2通りあります。
ここでは記事冒頭のgifアニメのような、検索フォームにユーザ名を入力すると、その入力された値にマッチするユーザ名を検索候補として表示するケースを想定して使い方を説明します。


## 目次

1. [suggestrapのインストール](#suggestrapのインストール)
1. [JSONサーバからサジェストを取得する方法](#jsonサーバからサジェストを取得する方法)
 1. JSONサーバ
 1. index.html
 1. index.js
1. [JSONサーバなしでサジェストを取得する方法](#jsonサーバなしでサジェストを取得する方法)
1. [コンストラクタ](#コンストラクタ)
1. [デザインのカスタマイズ](#デザインのカスタマイズ)

## suggestrapのインストール
ダウンロードはGitHubの[mrhorin/suggestrap](https://github.com/mrhorin/suggestrap)から。
npmでインストールする場合は`npm install suggestrap`

## JSONサーバからサジェストを取得する方法
ユーザがフォームに値を入力すると、suggestrapはその入力された値をリクエストパラメータにして、別途用意したJSONサーバにJSONファイルをリクエストし、返ってきたJSONファイルをパースしてサジェストを表示します。その為、自身で別途JSONサーバを用意する必要があります。JSONサーバなしで動かしたい場合は[JSONサーバなしでサジェストを取得する方法](#jsonサーバなしでサジェストを取得する方法)を参照してください。

### JSONサーバ
JSONサーバの構築方法は各自任意の方法で構いませんが、ここではRuby on Railsで構築されたWebサービスを例に説明します。Railsのインストール方法やモデルとコントローラの作成方法やルーティング方法についてはここでは割愛します。

```ruby:users_controller.rb
class UsersController < ApplicationController

  # GET /json/users/:query(.:format)
  def suggestion
    @users = User.where('name LIKE ?', "%#{params[:query]}%").limit(5)
    render json: @users
  end

end
```
Userモデルでnameキーがリクエストパラメータ`params[:query]`の値で部分一致するレコードを最大５件取得しています。ここで注意すべき点は、JSONサーバのレスポンスが遅いと、そのぶんsuggestrapがサジェストを表示するのも遅くなってしまうことです。登録されているレコードの件数が多い時はindexを追加するなどしてレスポンスが遅くならないようにして下さい。

```json:users.json
[
  {"id":1, "age":32, "name":"Jack"},
  {"id":2, "age":41, "name":"Jackie"},
  {"id":3, "age":41, "name":"Jake"},
  {"id":4, "age":20, "name":"James"},
  {"id":5, "age":28, "name":"Jane"}
]
```  
返ってくるJSONのフォーマットは上記の例のような、オブジェクトを持つ深さ1の配列でなければいけません。

### index.html

```html:index.html
<input id="user" type="text">

<script src="./node_modules/suggestrap/lib/suggestrap.js"></script>
<script src="./index.js"></script>
```
サジェストを表示したいテキストフォームにidを指定して下さい。

### index.js
```javascript:index.js
  var req = {
    target: "user",
    url: "https://example.com/json/users/?query=%QUERY",
    key: "name"
  };
  var suggestrap = new Suggestrap(req);
```
ユーザが`index.html`の`<input id="user" type="text">`に値を入力すると、suggestrapはコンストラクタの第一引数の`url`キーで指定したJSONリクエスト用URLの`https://example.com/json/users/?query=%QUERY`の`%QUERY`の部分をユーザが入力した値に置き換えます。例えばユーザが入力した値が`ja`だった場合は`https://example.com/json/users/?query=ja`というURLが生成されます。その後suggestrapはそのURLでJSONをリクエストし、返ってきたJSONオブジェクト内のキーの中で、`key`キーで指定したJSONオブジェクトの`"name"`キーの値をサジェストとして表示します。


```javascript:index.js
import Suggestrap from 'suggestrap'

const req = {
  target: "user",
  url: "https://example.com/json/users/?query=%USERNAME",
  key: "name"
}
const option = {
  minlength: 3,
  wildcard: "%USERNAME",
  delay: 200
}

let suggestion = new Suggestrap(req, option)
```
こちらはES6の場合の例です。こちらの例では第二引数でいくつかのオプション項目も指定しています。尚、この例のようにimportでsuggestrapを読み込む場合はindex.html内でsuggestrap.jsを読み込む必要はありません。

## JSONサーバなしでサジェストを取得する方法

JSONサーバなしでサジェストを表示したい場合は、suggestrapのコンストラクタの第一引数の`values`キーに、下記の例のようなサジェスト候補のハッシュの一覧を持つ配列を指定します。ユーザがテキストフォームに値を入力すると、suggestrapはその値と部分一致するオブジェクトを`values`キーで指定したサジェスト候補の一覧から抽出してサジェストを表示します。また、`values`キーを指定する場合は`url`キーは指定する必要はありません。もし`url`キーと`values`キーを同時に指定した場合は`values`キーの値が優先されます。
この例はES6で記述されています。index.htmlについては、[JSONサーバからサジェストを取得する方法](#jsonサーバからサジェストを取得する方法)の内容と重複するのでここでは割愛します。


```javascript:index.js
import Suggestrap from 'suggestrap'

const req = {
  target: "user",
  key: "name",
  values: [
    { name: "Andy" },
    { name: "Billy" },
    { name: "Catherine" },
    { name: "David" },
    { name: "Emily"},
    { name: "Henry"},
    { name: "Jack"},
    { name: "Kevin"},
    { name: "Mike"}
  ]
}

let suggestion = new Suggestrap(req)
```

## デザインのカスタマイズ
CSSをカスタマイズしたい時は自身でCSSをオーバーライドして下さい。suggestrapによって生成されるサジェストのエレメントのIDは、コンストラクタの第二引数の`id`キーで指定します。デフォルトの場合のidは`suggestrap`です。

```css:style.css
/* Example */
ul#suggestrap {
  background: #fff;
  border-radius: 3px;
  box-shadow: -2px 2px 7px rgba(0,0,0,0.3);
}
ul#suggestrap li {
  color: #333;
  padding: 1px 6px;
}
ul#suggestrap li.suggestrap-active,
ul#suggestrap li:hover {
  background: #4b89bf;
  color: #fff;
}
```

## コンストラクタ
第一引数は必須です。下記のキーを持つハッシュを渡して下さい。

- **target**: ターゲットとなるサジェストを表示したいフォームのIDを文字列で指定します。
- **url**: JSONサーバにサジェスト用のJSONをリクエストするURLを文字列で指定します。このURLは前述の[JSONサーバからサジェストを取得する方法](#jsonサーバからサジェストを取得する方法)の例のようにワイルドカードを含んでいる必要があります。suggestrapは、このURLのワイルドカード部分を、ターゲットのフォームに入力された値に置き換えた後に、JSONサーバへJSONをリクエストします。また、このワイルドカードの文字列のデフォルト値は`%QUERY`ですが、これは第二引数の`wildcard`キーで任意の文字列を指定することができます。
- **key**: サジェストで表示したいオブジェクトのキー名を文字列で指定します。
- **values**: このキーの指定は任意です。もしJSONサーバなしで、フロントサイドのみでサジェストを表示したい場合は、このキーにサジェスト候補のハッシュの一覧を持つ配列を指定します。その場合、`url`キーは指定する必要はありません。もし`url`キーと`values`キーを同時に指定した場合は`values`キーの値が優先されます。加えて、この`values`キーにはJSONリクエスト用のURLを指定することもできます。その場合の挙動は、`url`キーでURLを指定した場合と全く同じです。このキーの具体的な使い方については、[JSONサーバなしでサジェストを取得する方法](#jsonサーバなしでサジェストを取得する方法)を参照して下さい。

第二引数は任意のオプションです。指定したいキーを持つハッシュを渡して下さい。デフォルト値のままで良いキーは指定する必要はありません。

- **minlength**: ターゲットのフォームに入力された値の文字数が指定した値に達すると、suggestrapはサジェストを表示します。値は数値で指定します。初期値は`2`です。
- **wildcard**: JSONリクエストURLのワイルドカードを文字列で指定します。 初期値は`'%QUERY'`です。
- **delay**: サジェストを表示するまでの遅延時間をミリ秒単位で数値で指定します。ターゲットのフォームに入力された値の文字数が`minlength`に達した時、指定したミリ秒後にサジェストが表示されます。 初期値は`400`です。
- **count**: 表示するサジェストの件数を数値で指定します。初期値は`5`です。
- **id**: suggestrapが生成するサジェストのエレメントIDを文字列で指定します。もし指定したエレメントIDと同名のIDが既に存在していた場合は`suggestrap_2`のように、アンダースコアと数字の組み合わせの接尾辞を末尾に付与します。初期値は`'suggestrap'`です。