awesomplete icon indicating copy to clipboard operation
awesomplete copied to clipboard

Value being used as label on selectcomplete

Open jfi opened this issue 6 years ago • 11 comments

I have a JSON endpoint that's returning data in the following format:

[{
   id: 1,
   name: "Bob"
}]

And I'm using the following code:

window.addEventListener("awesomplete-selectcomplete", function(e) {
	window.location.href = '/my-path/' + e.text.value;
}, false);

var ajax = new XMLHttpRequest();
ajax.open("GET", "/data.json", true);
ajax.onload = function() {
	new Awesomplete(document.querySelector("#search"), {
		list: JSON.parse(ajax.responseText).map(function(i) { return i; }),
		data: function (item, input) {
			return { label: item.name, value: item.id };
		}
	});
}
ajax.send();

When I select an option, the value (id) is showing up in the textbox rather than the label (name). I'd still like it to use the label, just to pass the id through as the value to the listener. Is there some way to do this?

jfi avatar Nov 21 '17 19:11 jfi

Did u find a solution for this?

GSLabIt avatar Jan 09 '18 09:01 GSLabIt

@joezsweet No, I changed libraries instead. :)

jfi avatar Jan 09 '18 09:01 jfi

I see ..what r u using?

GSLabIt avatar Jan 09 '18 09:01 GSLabIt

@vlazar added the separate text/value functionality to Awesomplete, so ideally, he would be the one to help (hence why I haven't responded to this), but he hasn't been around lately and I guess I should start assuming he's not coming back. :(

To me, this looks like a bug. If we're using a label in the dropdown, that's what should be inserted in the input, not the id. I suspect he was following what native <datalist> does, but it seems kind of counter-intuitive in this case. Also, it seems to be the easiest way to ensure that it's the id that gets submitted, not the label.

I can dig in and fix this, but I see a few possible solutions, all with their own drawbacks:

  • The id could be added to an attribute, so that it's accessible in code that way. This is the easiest and quickest solution but it would require any script reading the value of that input to be aware of it. Same with form submissions.
  • I could add a listener for the submit event on this.input.form and possibly hijack the .value getter/setter. Seems super hacky, so I'd rather avoid this.
  • Add a second hidden input with the same name and id as the value. This way both values get submitted and getting them via JS is still possible (form.elements.foo returns a ...RadioNodeList with both values)
  • Add the id in the input and use some clever CSS to show the label instead.

Let me know which solution would work best for your use cases, or if you can think of a better one! @jfi how did the library you switched to handle this?

LeaVerou avatar Jan 13 '18 15:01 LeaVerou

@vlazar not sure what's best thing to do ..i'd say keep it simple ...

GSLabIt avatar Jan 13 '18 22:01 GSLabIt

@jfi @joezsweet @LeaVerou If I understood correctly what needs to be done, no need for fixes to Awesomplete.

When I select an option, the value (id) is showing up in the textbox rather than the label (name). I'd still like it to use the label, just to pass the id through as the value to the listener. Is there some way to do this?

What's inserted into the input is controlled by the replace method. By default it inserts value, you'll need to provide a custom method which will insert label instead:

replace: function(suggestion) {
  this.input.value = suggestion.label;
},

See demo here: https://jsfiddle.net/1agrns7n/

vlazar avatar Jan 20 '18 16:01 vlazar

@LeaVerou

@vlazar added the separate text/value functionality to Awesomplete, so ideally, he would be the one to help (hence why I haven't responded to this), but he hasn't been around lately and I guess I should start assuming he's not coming back. :(

Being busy for quite some time now :( I don't know how you manage to do open source and have normal life :)

vlazar avatar Jan 20 '18 17:01 vlazar

Hey, good to see you around again @vlazar!!

Being busy for quite some time now :( I don't know how you manage to do open source and have normal life :)

I don’t, really. I always feel I’m falling behind on my open source stuff, even those that are part of my job. It's tough. :( I hope it all goes well with life, and you can find even a little time for open source :)

What's inserted into the input is controlled by the replace method. By default it inserts value, you'll need to provide a custom method which will insert label instead:

Yay! Though if they need to submit the form and submit the id instead of the label, that may be a problem.

LeaVerou avatar Jan 21 '18 19:01 LeaVerou

Thank you @vlazar, I think @LeaVerou is right - what I could do with is the user only ever seeing the value, but the backend seeing the ID. I'll certainly use what you've suggested for now unless you have any other ideas - it's a nicer user solution to reverse-engineer the label on the backend than them seeing the "flash" of the ID on selection, although not as nice from a development point of view.

jfi avatar Jan 21 '18 19:01 jfi

@jfi Why reverse-engineer label? Have you checked https://jsfiddle.net/1agrns7n/ demo?

Both awesomplete-selectcomplete event and replace method have access to suggestion item value and label. You can easily have say visible input for inserted label which you can ignore on backend or don't send, and additional hidden field for selected value. Just assign this hidden's filed value in replace method.

vlazar avatar Jan 22 '18 06:01 vlazar

I feel like the current default behavior for this is very counter intuitive. A combobox is supposed to mimic the behavior of a select with added features. Ideally, a user who doesn't use the added features should not even realize it isn't a normal select. A select uses the value as an underlying id of sorts that you can get and send to the server. As such, the value in Awesomplete should be underlying while the label is put in the input box, just like a select.

The difficulty is Awesomplete is an autocomplete first and a combobox second, meaning there is no current functionality to track a selected index. Ideally, when you select an item in the list, or just fully type out an item so that it matches a value in the list, there should be an underlying index and possible related value stored within the Awesomplete. If the input does not match anything in the list, the index should be -1 or false. This might represent a significant change though, so maybe there's an easier way to do it.

For example, we could add an id field or maybe we allow users to attach whatever they want in each data object instead of just label and value. Then, they can use awesomplete-selectcomplete to pop it in a hidden object. The two problems with this approach though are:

  1. If the user types an entire option without selecting it, it should still get the id on blur. This means there would need to be a function to call to see if the current value in the input matches any labels in the autocomplete list (which there probably should be anyway, for cases such as when you want to enforce that the final value matches something in the list). The function would need to return the data object so you could get the id value of the matched element.

  2. This represents a sizeable amount of extending for what seems like it should be built in if Awesomplete is to support combobox functionality. You would have to use awesomplete-selectcomplete to store the value and attach another onBlur event to check if the typed value is in the list. As @jfi mentioned, this is behavior that is expected, which means many people might turn away from Awesomplete without even realizing this functionality could be implemented. An extended combobox example in the docs could help.

dillydadally avatar Feb 09 '18 19:02 dillydadally