awesomplete
awesomplete copied to clipboard
Save extra info about each list item
So i´ll first describe my use case: Im using awesomeplete with google places API, so basically, im populating the _list collection for the awesomplete on the location input with an async call to google´s API, for each item i have plenty of information such as geometry and a google_place_id which identifies the given place.
I't would be nice if i would be able to retrieve that data from the autocomplete selection, so whenever the awesomplete-selectcomplete event is fired, the data attached to the given item is available.
I will mostly sure add this and use a customized version of the library but i can add this to a PR if anybody likes the idea.
LoL i never saw this #16938 my bad
Currently we support a separate label
and value
for each suggestion (the original array of strings, objects or arrays). See docs http://leaverou.github.io/awesomplete/#basic-usage
Additional properties are not directly possible due to API backward compatibility shim we use right now https://github.com/LeaVerou/awesomplete/blob/b757022a728a9f825c545f2235b99c5e98a85874/awesomplete.js#L302-L315. See explanation here https://github.com/LeaVerou/awesomplete/issues/16888#issuecomment-218536654
But this would be a really nice feature to have.
@jandramila so did you work out a customized solution? I'm facing the same problem right now so I'd love to hear your solution (I can't work it out :( )
@El4a Yeah, i achieved it, but it only works when you programmatically set list items, i just added extraData to these lines:
...
// Private functions
function Suggestion(data) {
var o = Array.isArray(data)
? { label: data[0], value: data[1] , extraData: data[2]}
: typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data };
this.label = o.label || o.value;
this.value = o.value;
this.extraData = o.extraData;
}
Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", {
get: function() { return this.label.length; }
});
Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () {
return "" + this.label;
};
...
I´m using my own awesomplete customized version on a react app, you can refer the list via jQuery selector or whatever, in my case, i use a component ref:
autocompletesReference.location._list = predictions.map( (prediction) => {
return {
label: prediction.description,
value: prediction.description,
extraData: {
googlePlaceId: prediction.place_id
}
}
});
So by doing this, you wont be able to set items list on html or with inline data via attributes, but is also easy to handle the _list property via JS, so it should not be much of a problem.
If you have any question just ask.
Hi! Here is my workaround without changing the awesomeplete itself.
- In the item we'll store the entire object with all needed data, not just string value (see data() function). And to show the string value, we'll use replace() function:
this.autocompleter = new Awesomplete(this.searchInput,{
list: [],
autoFirst: true,
data: function (item, input) {
return {value: item, label: item.description};
},
replace: function(suggestion){
this.input.value = suggestion.value.description;
}
});
- Then I use 'awesomeplete-selectcomplete' event to work with selected element data:
$(this.searchInput).on('awesomplete-selectcomplete', function(ev){
console.log(ev.originalEvent.text.value); //here you'll have the entire object with all data you need.
});
Well I think I found a way to make this work. At least in my case.
A typical response from my backend looks like follows:
[
{id: 1, name: "New York", about: "The great city of New York"},
{id: 2, name: "Chicago", about: "Famous Chicago"},
…
]
First of all, I save entire response after fetching it. I use item index as the value. I set the label as well so Awesomplete filtering can work, but it doesn't matter as I do server-side filtering.
let results = [];
// I act upon input keydown event
// throttling, ignoring special keys skipped for brevity
$.getJSON(url, {term: e.target.value}, response => {
// save response and index by the id
response.forEach(item => results[item.id] = item);
// use the id as value
awesomplete.list = response.map(item => ({value: item.id, label: item.name}));
awesomplete.evaluate();
});
I build list items by myself now based on my response:
item: item => {
// get current result using the id
const result = results[item.value];
const span = document.createElement('span');
span.innerText = result.about;
const node = document.createElement('li');
node.innerText = result.name;
node.appendChild(span);
return node;
},
I attach to the awesomplete-select
event so I can override the value which will appear in my input. I store selected response item:
let result = null;
input.addEventListener('awesomplete-select', event => {
result = results[event.text.value];
event.text.value = event.text.label;
});
Et voilà:
input.addEventListener('awesomplete-select', () => {
console.log(result);
});
Full code for reference published on Gist.