入力フォームにサジェスト機能を追加したい
googleの検索窓のように、フォームに文字入力した場合、検索結果をサーバーから取得し、結果一覧をサジェストとしてフォームに表示するような実装を行いたい。
すでにフロント側でサジェストとして表示する一覧のデータを持っているなら良いのですが、文字を1文字入力するたびに、サーバーにGETリクエストを投げるのはすごいイヤなわけです。外部APIと連携していれば、Rate Limitに達する可能性もあります。
かといって、エンターキーやボタンを押した際にサジェストを表示するのも、サジェストの意味あるの?という気がします。
そこで、最後の文字入力から1秒経過した場合に、サーバーにGETリクエストを投げる、という実装をしてみたいと思います。文字を入力した後、1秒以内に文字がタイプされた場合は、またタイマーがリセットされる、という仕組みです。
コード
AngularJSを用いて書いてみました。実際の実行例はこちらから(http://jsfiddle.net/ADukg/8059/)
文字を入力して少し経つと、「サジェストの結果」に文字が出てきます。
また、連続して文字を入力した場合は、サジェストの結果に反映されません。
<div ng-controller="MyCtrl">
<form name="timeForm">
<input type="text" name="firstName" ng-model="formString">
<p>
サジェストの結果: {{showString}}
</p>
</form>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
// 処理待ち用のキュー
$scope.searchQueue = [];
$scope.showString = '';
// 文字入力があるたびに、waitAndGo関数を実行
$scope.$watch('formString', function() {
waitAndGo($scope.formString);
});
// 一定時間文字入力が無い場合は処理を実行
function waitAndGo(searchQuery){
// 処理待ちのイベントが無いか確認し、ある場合は全て実行をキャンセルする
if($scope.searchQueue.length > 0) {
$scope.searchQueue.forEach(function(elem){
clearTimeout(elem);
$scope.searchQueue.shift();
});
}
// 1秒間request関数の実行を待つ。setTimeOutのIDを取得し、処理待ち用のキューに格納する
var eventId = setTimeout(request, 1000, searchQuery);
$scope.searchQueue.push(eventId);
// サーバーへのリクエスト処理。ここでは便宜的にscopeへ代入します。
function request(query) {
$scope.showString = query;
$scope.$digest();
}
}
}
文字が入力されるたびに、
- 実行待ちのイベントは無いか確認
- あれば全て削除
- 入力された文字列でリクエストを実行するイベントを1秒間遅延させ、実行待ちのキューの中に登録
を、文字入力時ごとに、繰り返すことにより、ある程度まとまった文字列でGETリクエストを送信することが可能です。