Autocomplete to loop through search terms with ajax callback results - jquery-ui

I'm setting up an autocomplete form in which I need that every keyword/term is matched.
I'm using an ajax callback to get my results list and I tried many workarounds to convert json results to a autocomplete-capable array but I couldn't get it to work.
$(this).autocomplete({
source: function (request, response) {
$.ajax({
url: 'autocomplete.php',
data: request.term,
dataType: "json",
success: function (data) {
var dataArray = [];
for(var i in data)
dataArray.push(data[i]);
var matchArray = dataArray.slice();
var srchTerms = $.trim (request.term).split (/\s+/);
$.each (srchTerms, function (J, term) {
var regX = new RegExp (term, "i");
matchArray = $.map (matchArray, function (item) {
return regX.test (item) ? item : null;
} );
} );
response(matchArray);
},
error: function () {
response([]);
}
});
},
autoFocus: true
});
I think the code itself is working because if I push a normal javascript array it works just fine but as long as I get results from ajax and convert it to a javascript array, it doesn't work anymore.
What I trying to get is that if a given results is equal to "example book title", it should pop up even if my keywords are "example title" or "title book example" etc...

Based on your description, if the user enters multiple words, you want it to show the results for each of the searches for each of those words. For example, if the user enters "hot air", results would include all the results for "hot" and all the results for "air".
Consider the following example.
$(function() {
var availableTags = [
"win the day",
"win the heart of",
"win the heart of someone"
];
$("#tags").autocomplete({
source: function(req, resp) {
// Trim Query and split by " " (Space)
var srchTerms = req.term.trim().split(" ");
// Prepare result Arrays
var results = [],
uniqueResults = [];
// Iterate each search term and combine the results into one array
$.each(srchTerms, function(i, t) {
results = results.concat($.ui.autocomplete.filter(availableTags, t));
});
// Remove duplicates by iterating each result item
$.each(results, function(i, r) {
if ($.inArray(r, uniqueResults) === -1) uniqueResults.push(r);
});
// Send back to Autocomplete
resp(uniqueResults);
}
});
});
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="demo">
<div class="ui-widget">
<label for="tags">Multi-word search: </label>
<input id="tags">
</div>
</div>
If the user enters "win" or "win ", it will still hit the source data. If the user enters "win heart", it will filter the source data for both terms individually, combining all the results into one array. There will likely be duplicates. In the steps, we can filter those duplicates out and send the resulting array back.
In your code, you will want send each search term to PHP, which should return an Array or an Array of Objects. In the same way you'll collect all the results into one array and then filter.
Alternatively, you can do all the work on the PHP Side, send the entire chunk to PHP and have it perform the search and filtering (potentially faster) and return all the results.
Update
If all you want to do is get the array via ajax, review the following:
function (request, response) {
$.getJSON("autocomplete.php", {term: request.term}, function(data){
var matchArray = [];
$.each(data, function(key, val){
matchArray.push(val);
});
var srchTerms = $.trim(request.term).split(/\s+/);
$.each (srchTerms, function (J, term) {
var regX = new RegExp (term, "i");
matchArray = $.map (matchArray, function (item) {
return (regX.test(item) ? item : null);
});
});
response (matchArray);
});
}
This assumes that PHP will return JSON Data that is an array of results. Since you have not provided an example of the results nor a snippet of your PHP, it's hard to help further.
Hope this helps.

Related

Select2 Dropdown autoselect if only 1 option available

I have a select2 dropdown box using remote datasource.
What I would like to do is if/when there is only one option returned by the search, auto select it. ie, the user doesn;t have to click on the option to make the selection.
$("#searchInfo_Entity_Key").select2({
ajax: {
url: "/Adjustment/GetEntity",
dataType: 'json',
delay: 250,
data: function (params) {
return {
term: params.term, // search term
};
},
processResults: function (data) {
return {
results: data
};
},
results: function (data) {
return { results: data };
},
},
initSelection: function (element, callback) {
var data = [];
callback(data);
},
minimumInputLength: 2,
allowClear: true,
placeholder: "Select an entity"
});
This is a custom matcher, that wraps around the default matcher and counts how many matches there are. If there is only one match, then it will select that match, change it, and close the dropdown box.
(Not tested with remote data.)
$(function() {
var matchCount = 0; // Track how many matches there are
var matched; // Track the ID of the last match
$('select').select2({
placeholder: 'Product Search',
allowClear: true,
matcher: function(params, data) {
// Wrap the default matcher that we have just overridden.
var match = $.fn.select2.defaults.defaults.matcher(params, data);
// If this is the first option that we are testing against,
// reset the matchCount to zero.
var first = $(data.element).closest('select').find('option').first().val();
if (first == data.id) matchCount = 0;
// If there was a match against this option, record it
if (match) {
matchCount = matchCount + 1;
matched = data.id;
}
// If this is the last option that we are testing against,
// now is a good time to check how many matches we had.
var last = $(data.element).closest('select').find('option').last().val();
if (last == data.id && matchCount == 1) {
// There was only one match, change the value to the
// matched option and close the dropdown box.
$(data.element).closest('select')
.val(matched).trigger('change')
.select2('close');
return null;
}
// Return the default match as normal. Null if no match.
return match;
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet"/>
<select style="width: 20em">
<option value=""></option>
<option value="100">Product One</option>
<option value="200">Product Two</option>
<option value="300">Product Three</option>
<option value="400">Product Four</option>
<option value="500">Product Five</option>
</select>

Auto-complete doesn't work as expected

I tried to implement this in MVC 5 with jquery ui 1.10.2
#{
ViewBag.Title = "Home Page";
Layout = null;
}
<p>
Enter country name #Html.TextBox("Country")
<input type="submit" id="GetCustomers" value="Submit" />
</p>
<span id="rData"></span>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery-ui.js"></script>
#Styles.Render("~/Content/themes/base/css")
<script type="text/javascript">
$(document).ready(function () {
$("#Country").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Home/AutoCompleteCountry",
type: "POST",
dataType: "json",
data: { term: request.term },
success: function(data) {
response($.map(data, function(item) {
return { label: item.Country, value: item.Country };
}));
}
});
}
});
})
</script>
the server side is
...
[HttpPost]
public JsonResult AutoCompleteCountry(string term)
{
// just something to return..
var list = new List<string>() { "option1", "option2", "option3"};
var result = (from r in list
select r);
return Json(result, JsonRequestBehavior.AllowGet);
}
}
I have two issues
1. it open up drop down autocomplete with 3 dots but without the actual strings.
2. It has this annoying message of "3 results were found" - I'd like to eliminate it..
DO you have any idea how to face those two issues or neater way to implement it in MVC5?
The 3 bullet points and "3 results were found" is because you are missing the jQuery UI css file. That file will format a drop down that will look a lot better. You can customize how the dropdown looks with additional css.
Also, you are seeing 3 empty results because your JS is referencing item.Country ...
return { label: item.Country, value: item.Country };
But your server code is just sending 3 strings.
new List<string>() { "option1", "option2", "option3"};
To fix, change your JS to just reference the item (the string) ...
return { label: item, value: item};
OR, change your server code to send more complex objects
new List<Object>() { new { Country = "option1" }, new { Country = "option2" }, new { Country = "option3" } };
use return data in place of return { label: item.Country, value: item.Country };

select2 unable to search if data source is remote

I am using select2 select box to populate and show some server data. Most of it works fine as I am able to get the results and populate the combo box. But when I type in the search box, the selection doesn't narrow down the the closest match.
I found the problem was because the backend URL doesn't support searching based on the string provided, while select2 keeps making multiple search request to backend, based on user entered text. The legacy backend code returns results in one shot, which is populated into the combobox the first time.
My question is, how do I get the the select box to focus to the closest matching result, without making multiple Ajax calls. I want the search to happen in the results which are already fetched.
Thanx to anyone helping me out on this.
My ajax call is like below, if this helps...
select2: {
placeholder: 'Select Next Plan Id',
allowClear: true,
id: function (item) {
return item.id;
},
ajax: {
type: 'GET',
dataType: "jsonp",
url: "http://172.16.7.248:8480/app/gui?action=1&type=11",
data: function (term, page) {
return { search: term };
},
results: function (data, page) {
return { results: data.aaData };
},
success : function(data, status, xhr) {
var html = "<option value=''>None</option>";
$.each(data.aaData, function(i, item) {
html += "<option data=" + JSON.stringify(item.id) + " value=" + item.id + "'>" + item.id + "</option>";
});
$('#nextplanid').html(html);
self.prop('data-loaded', 'true');
},
error : function(data, status, xhr) {
}
},
formatResult: function (item) {
return item.id;
},
formatSelection: function (item) {
return item.id;
},
initSelection: function (element, callback) {
return $.get('/getText', { query: element.val() }, function (data) {
callback(data);
});
}
},

remove <strong> from text box

I have implemented autocomplete using Jquery. I have also implemented highlighting the matching text. I am using <strong> tag in the high light function. When I go through the autocomplete dropdown one by one using keyboard arrows, the text where I am currently on, is displayed in the text box. When it displays, it displays with the <strong> tag. Any suggestions to remove the tag? I have given my code below.
<input type="text" id="institution-list"/>
<script type="text/javascript" language="javascript">
$(function () {
$("#institution-list").autocomplete({
source: function (request, response) {
$.ajax({
url: "/home/findinstitutions", type: "POST", dataType: "json",
data: { searchText: request.term, maxResults: 10 },
success: function (data) {
response($.map(data, function (item) {
return { label: highlight(item.InstitutionName, request.term),
id: item.InstitutionId
};
}));
}
});
},
minLength: 3
})
.data("autocomplete")._renderItem = function (ul, item) {
return $("<li></li>")
.data("item.autocomplete", item)
.append($("<a></a>").html(item.label))
.appendTo(ul);
};
});
function highlight(s, t) {
var matcher = new RegExp("(" + $.ui.autocomplete.escapeRegex(t) + ")", "i");
return s.replace(matcher, "<strong>$1</strong>");
}
</script>
I think that the problem is that you're taking the label of your recently found data and render it as HTML, instead of plain text. Thus, instead of Berkeley, your autocomplete is showing <strong>Ber</strong>keley.
Try to parse it and remove any HTML tag before displaying it:
function sanitize(text){
var regex = /(<([^>]+)>)/ig;
return text.replace(regex, "");
}
.data("autocomplete")._renderItem = function (ul, item) {
return $("<li></li>")
.data("item.autocomplete", item)
.append($("<a></a>").html(sanitize(item.label)))
.appendTo(ul);
};
The regular expression was extracted from here: Remove HTML Tags in Javascript with Regex
Find below the solution I found for my problem
Existing code:
response($.map(data, function (item) {
return { label: highlight(item.InstitutionName, request.term),
id: item.InstitutionId
};
Solution:
response($.map(data, function (item) {
return { label: highlight(item.InstitutionName, request.term),
value: item.InstitutionName,
id: item.InstitutionId
};
The original code returned the label (which had embedded html tags) and no value. Since there was no value, the textbox used the label to display. Now, I explicitly assign the value of the text box with my text (without html tags) and that fixes my problem.
Here is the snapshot of how it appears now.

Jquery UI autocomplete select

I need some help with the code below.
$("#auto_cp").autocomplete({
minLength: 3,
//source
source: function(req, add) {
$.getJSON("friends.php?callback=?", req, function(data) {
var suggestions = [];
$.each(data, function(i, val) {
suggestions.push(val.name);
});
add(suggestions);
});
},
//select
select: function(e, ui) {
alert(ui.item.value);
}
});​
using FireBug, i'm getting this in my console :
jQuery171003666625335785867_1337116004522([{"name":"97300
Cayenne","zzz":"203"},{"name":"97311
Roura","zzz":"201"},{"name":"97312 Saint
Elie","zzz":"388"},{"name":"97320 Saint Laurent du
Maroni","zzz":"391"},{"name":"97351
Matoury","zzz":"52"},{"name":"97354 Remire MontJoly
Cayenne","zzz":"69"},{"name":"97355 Macouria Tonate","zzz":"449"}])
Everything is working very fine, but I don't know how to get the value of 'zzz' on select item.
I tried
alert(ui.item.zzz);
But it doesn't work.
The autocomplete widget expects a data source in array format with either:
Objects containing a label property, a value property, or both
Simple string values
You are currently building up the second (an array of string values), which works fine, but you can also slightly tweak your data as you iterate over it and also supply the other properties in the object:
$("#auto_cp").autocomplete({
minLength: 3,
//source
source: function(req, add) {
$.getJSON("friends.php?callback=?", req, function(data) {
var suggestions = [];
$.each(data, function(i, val) {
suggestions.push({
label: val.name,
zzz: val.zzz
});
});
add(suggestions);
});
},
//select
select: function(e, ui) {
alert(ui.item.zzz);
}
});​
Now, since the array you're supplying the widget contains objects with a name property, you should get autocomplete functionality and also gain access to the zzz property.
Here's a working example (without the AJAX call): http://jsfiddle.net/LY42X/
You're source function is only populating the name. If you want everything from that data structure, do this:
$("#auto_cp").autocomplete({
minLength: 3,
//source
source: function(req, add) {
$.getJSON("friends.php?callback=?", req, function(data) {
var suggestions = [];
$.each(data, function(i, val) {
suggestions.push(val); //not val.name
});
add(suggestions);
});
},
//select
select: function(e, ui) {
alert(ui.item.value.zzz);
}
});​
This seems to be an array of objects... what your may be missing is the "[0]" or in general "[index]".
Please check this: jqueryui.com/demos/autocomplete/#event-select

Resources