awesomplete icon indicating copy to clipboard operation
awesomplete copied to clipboard

Save extra info about each list item

Open jandramila opened this issue 8 years ago • 6 comments

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.

locationexample

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.

jandramila avatar Sep 22 '16 17:09 jandramila

LoL i never saw this #16938 my bad

jandramila avatar Sep 22 '16 17:09 jandramila

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.

vlazar avatar Sep 30 '16 11:09 vlazar

@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 avatar Feb 14 '17 13:02 El4a

@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.

jandramila avatar Feb 14 '17 13:02 jandramila

Hi! Here is my workaround without changing the awesomeplete itself.

  1. 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;
            }
});
  1. 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.
}); 

zahardev avatar Mar 25 '19 17:03 zahardev

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.

mikemix avatar May 14 '19 11:05 mikemix