问题
I have an input field for locations with jquery-ui-autocomplete.
<script type="text/javascript">
$(document).ready(function(){
var location_input=$('input[id="location-autocomplete"]');
var cityNames = [
{ value: 'Mallorca' },
{ value: 'Berlin' },
{ value: 'London' },
// many other elements
];
location_input.autocomplete({
source: cityNames,
minLength: 2
});
} );
// keeps same width as box
jQuery.ui.autocomplete.prototype._resizeMenu = function () {
var ul = this.menu.element;
ul.outerWidth(this.element.outerWidth());
}
</script>
However, I am not happy with the case when the same location can have different names.
For example, let's say that the user wants to look for Mallorca. He could write: Mallorca, Majorca, Palma de Mallorca, PMI or Palma.
My first idea was to use the label
property
var cityNames = [
{ value: 'Mallorca', label: 'Palma de Mallorca' },
{ value: 'Mallorca', label: 'Mallorca' },
{ value: 'Mallorca', label: 'Majorca' },
// etc
];
However, this can be very confusing. Writing Ma the autocomplete would show Mallorca, Palma de Mallorca and Majorca, even if they are the same place.
I would like that when typing Ma the user only gets one suggestion. Similarly, no matter if the user types Maj, Mal or Pal, he should just get only one suggestion term for Mallorca.
Ideally, an additional property called input
working as the input keyword to be searched would have been perfect. Then, having an array with the properties value
and input
for each would have done the trick. Unfortunately, I did not find such a thing in the docs.
As you can see, I am using as source type an array
. For what I read, maybe the function
type would allow me to do something like this, but it is not clear to me how, as I am not very familiar with js and did not find any clear example about it.
How could I achieve that in a simple way?
回答1:
You are almost there. You can use the response
function (see in API) to remove the alternate spellings from the results. Also put the best spelling ("Mallorca" in this case) last. Check this out, I hope the comments are enough to get the logic. Try to type "Ma", or "Maj" and select the only option. In both cases it will display "Mallorca"
$( function() {
var availableTags = [
{ value: 'Mallorca', label: 'Palma de Mallorca' },
{ value: 'Mallorca', label: 'Majorca' },
{ value: 'Mallorca', label: 'Mallorca' },
{ value: 'Madrid', label: 'Madrid' }
];
$( "#tags" ).autocomplete({
source: availableTags,
response: function( event, ui ) {
var added = [];//Keep here the unique labels that are already in the list
for(var i=ui.content.length-1;i>=0;i--){//Start a loop, itterate all items backwards
var cur = ui.content[i].value;//Value of the item
if($.inArray(cur,added)==-1){//If not already loaded
added.push(cur);//Put the new item in the list
}else{
ui.content.splice(i,1);//If already loaded remove it from the results
}
}
}
});
} );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="ui-widget">
<label for="tags">Tags: </label>
<input id="tags">
</div>
回答2:
After some additional research and many tries, I found a way to do it. This is specific for Django, so any other more generic proposal is of course more than welcome.
The solution is based on this tutorial, although with some modifications.
First of all, import jQuery and jQueryUI in the template:
<link rel="stylesheet" href="http://code.jquery.com/ui/1.8.18/themes/base/jquery-ui.css" type="text/css" media="all" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
Afterwards, in the template itself, you need to assign an id to the input tag. Note that this id is how jquery will identify in which form to run the autocomplete.
<div class="ui-widget">
<label for="cities">City: </label>
<input id="autocomplete-cities">
</div>
The javascript code is as follows:
<script type="text/javascript">
$(document).ready(function(){
var location_input=$('input[id="autocomplete-city"]');
location_input.autocomplete({
source: "/api/get_city_names/",
minLength: 2
});
} );
// keeps same width as box
jQuery.ui.autocomplete.prototype._resizeMenu = function () {
var ul = this.menu.element;
ul.outerWidth(this.element.outerWidth());
}
</script>
And urls.py
file needs to be modified accordingly:
# urls.py
import yourapp.views
urlpatterns = [
...,
url(r'^api/get_city_names/', yourapp.views.get_city_names),
...,
]
And finally create the django view. The name of the function has to be the same as the one written in urls.py
and the one written in the source
of the javascript.
#views.py
import json
def get_city_names(request):
#what was in the question an array is now a python list of dicts.
#it can also be in some other file and just imported.
all_city_names = [
{ good_name: 'Mallorca', input_name: 'Palma de Mallorca' },
{ good_name: 'Mallorca', input_name: 'Mallorca' },
{ good_name: 'Mallorca', input_name: 'Majorca' },
# etc
]
if request.is_ajax():
q = request.GET.get('term', '')
city_names = [c['good_name'] for c in all_city_names if q in c["input_name"]]
city_names = set(city_names) #removing duplicates
results = []
for cn in city_names:
cn_json = {'value': cn}
results.append(cn_json)
data = json.dumps(results)
else:
data = 'fail'
mimetype = 'application/json'
return HttpResponse(data, mimetype)
And the autocomplete should work.
来源:https://stackoverflow.com/questions/42224605/how-to-suggest-similar-words-in-autocomplete