Reputation: 508
I am trying to introduce auto-completion to the search bar via search results that I procure from an Elastic search server. At first, I was confused between the tons of examples there are for Bootstrap-typeahead js, and the newer, standalone version. Now I have with me two ways of going about it, and neither are working entirely.
The first way involves binding a keyup
event handler to the search bar. This handler collects queries as I type, sends requests to the elastic server each time I type (doesn't touch the database), and obtains results in JSON. It puts the stringified JSON in localStorage
(I tried without stringifying, and I was facing some issues - it becomes a string "[object Object]"
instead of an array [object Object]
as it was otherwise becoming).
In the source
function of the typeahead code block, I parse it back into a JSON object and then do some processing to get a list of product names for the suggestion box.
Example JSON -:
[{'productName': 'FirstProduct'}, {'productName': 'FirstProd'}]
JS Code First Way -:
$(document).ready(function() {
$('#search-text').keyup(function() {
localStorage.removeItem('autocomplete_data')
$.getJSON($SCRIPT_ROOT + '/search-auto-complete', {
q: $('#search-text').val()
}, function(data, status, xhr) {
console.log("Data " + JSON.stringify(data.results));
localStorage.setItem('autocomplete_data', JSON.stringify(data.results));
});
});
$("#search-text").typeahead({
hint: true,
minLength: 3,
highlight: true
},
{
name: 'products',
displayKey: 'value',
source: function(query, process) {
products = [];
data = JSON.parse(localStorage.getItem('autocomplete_data'));
for (var index = 0; index < data.length; index++) {
for (var key in data[index]) {
products.push(data[index][key]);
}
}
console.log("product names: " + products)
process(products);
}
});
The issue with this is that while I am getting results and two suggestion rows appear as I type the word 'First', the suggestions are undefined
and not the product names as they should be. This is close to being a solution but I am not sure if this is the right way to go about what I want to achieve (results as I type).
The second way involves using the Bloodhound suggestion engine as given in the typeahead github examples. The problem with this approach is that the URL is generated when the DOM is loaded (because of ready
), and at that point there is nothing in the search bar, so $.('#search-text').val()
returns an empty string and $('.typeahead').typeahead('val')
returns undefined
. I confirmed this by putting a dummy value="dummy"
in the HTML, and it sends this value to the server when I type something and the text satisfies minimum length.
Also, it sends the request only once to the server, not generating results I type. This is probably by design.
JS Code Second Way -:
$(document).ready(function() {
var products = new Bloodhound({
datumTokenizer: function(datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: $SCRIPT_ROOT + '/search-auto-complete' + '?q=' + $('#search-text').val(),
filter: function(products) {
// Map the remote source JSON array to a JavaScript object array
return $.map(products.results, function(product) {
return {
value: product.productName
};
});
}
}
});
products.initialize();
$('#search-bar .typeahead').typeahead({
hint: true,
minLength: 5,
highlight: true
}, {
displayKey: 'value',
source: products.ttAdapter()
});
});
Adding the HTML of the search box below for reference. I also had to add some CSS to keep the search box from becoming less than half its width in the second approach - this I found on another Stackoverflow thread where it was explained that the newer typeahead does not work well with Bootstrap.
HTML -:
<div class="input-group" id="search-bar">
<input id="search-text" type="text" class="form-control typeahead" data-provide="typeahead" name="q" itemprop="query-input" required value="dummy">
<span class="input-group-btn">
<button class="btn btn-default" type="submit"><i class="fa fa-search"></i></button>
</span>
</div>
<!-- /input-group -->
Any suggestions for either approach would be appreciated!
Upvotes: 1
Views: 409
Reputation: 508
I've managed to get it right with the second approach.
The only change that I made was to write the url bit as follows -:
url: $SCRIPT_ROOT + '/search-auto-complete?q=%QUERY'
and that did the trick! The suggestions box is now populated correctly.
Props to Dave for showing me the example. Had to read it very carefully to see what I was missing.
Upvotes: 1