afcapel / stimulus-autocomplete

Stimulus autocomplete component
MIT License
478 stars 61 forks source link

Suggestion: Allow to use local HTML instead of fetching from the server #116

Open fidalgo opened 2 years ago

fidalgo commented 2 years ago

Hi! First of all, thank you for your work on this!

If we allow using HTML data attributes with data in JSON format instead of fetching from the server, this will allow caching where the data is static while preventing more network requests.

Imagine the case of a country list, where it's a fixed list; we may want just to iterate and render the li elements, but currently, we cannot use the autocomplete.

Do you think this could be a nice addition to this lib?

schmijos commented 2 years ago

I just tried abusing a data URL for this:

<div data-controller="autocomplete" data-autocomplete-url-value="data:text/html,%3Cli%20class%3D%22list-group-item%22%20role%3D%22option%22%20data-autocomplete-value%3D%221%22%3EBlackbird%3C%2Fli%3E%0A%3Cli%20class%3D%22list-group-item%22%20role%3D%22option%22%20data-autocomplete-value%3D%222%22%3EBluebird%3C%2Fli%3E%0A%3Cli%20class%3D%22list-group-item%22%20role%3D%22option%22%20data-autocomplete-value%3D%223%22%3EMockingbird%3C%2Fli%3E%0A">
    <input name="birds" type="text" class="form-control" data-autocomplete-target="input" placeholder="search a bird" autofocus/>
    <input type="hidden" name="bird_id" data-autocomplete-target="hidden"/>
    <ul data-autocomplete-target="results" class="list-group"></ul>
</div>

It works partially 🤣 The query parameters obviously become part of the answer.

https://user-images.githubusercontent.com/245443/191965163-8efcc89e-3aeb-4fcd-85e1-becdcb511e6a.mp4

afcapel commented 2 years ago

@fidalgo if you just want to solve this issue for a particular feature you could override doFetch in the controller to return an HTML with the options you want to show.

<div data-controller="combobox" data-combobox-options-value="[JSON array with options]">
...
</div>
class Combobox extends Autocomplete {
  static values = { options: Array }

  doFetch = async () => {
    const query = this.inputTarget.value.trim().toLowerCase()
    const matchingOptions = this.optionsValue.filter(o => o.toLowerCase.includes(query))
    return matchingOptions.map(o => `<li data-autocomplete-value="${o}">${o}</li>`).join("\n")
  }
}

If however you wan to add the feature to the library, I'd suggest going in the opposite direction: create a base Combobox class that allows filtering a set of static options and then define Autocomplete as a subclass of Combobox, adding the methods to fetch new options from the server in Autocomplete.

I'd be happy to accept patches implementing that, although it seems a substantial change.

ACPK commented 1 year ago

+1