TarekRaafat / autoComplete.js

Simple autocomplete pure vanilla Javascript library.
https://tarekraafat.github.io/autoComplete.js
Apache License 2.0
3.95k stars 239 forks source link

Make current element available in src callback #318

Open Radiergummi opened 2 years ago

Radiergummi commented 2 years ago

Is your feature request related to a problem? Please describe. I'm creating multiple autocomplete elements on the page dynamically. They receive suggestions from a JSON endpoint which is resolved at template rendering time. To pass the proper endpoint to the autocomplete input, I would like to do the following:

<input type="text" class="autocomplete" data-suggestions-endpoint="{{ route('foo.bar') }}">

In the next step, I would configure AutoComplete:

const autoCompleteJS = new autoComplete( {
    selector: '[data-suggestions-endpoint]',
    data: {
        src: query => fetch( url ).then(r => r.json())
    }
} );

Alas, there's no way to get access to the data property.

Thoroughly Describe the solution you'd like It would be great if the current element were passed to the query callback as the second parameter:

const autoCompleteJS = new autoComplete( {
    selector: '[data-suggestions-endpoint]',
    data: {
        src: (query, element) => fetch( element.dataset.suggestionsEndpoint )
                                     .then(r => r.json())
    }
} );

Please provide a few use cases for this feature

  1. Configuring dynamic endpoints, as above
  2. Bridging between server-side rendered templates, and frontend applications - that way, autocomplete could be configured using declarative attributes on the input instead of inline JS
  3. Checking the current state of the input element; you might want to abort searching if the user leaves the input field (I'll admit this one is a little convoluted)

Please Describe alternatives you've considered Obviously, binding every element by itself:

const instances = Array.from( document.querySelectorAll(
    '[data-suggestions-endpoint]'
) ).map(element => new autoComplete( {
    selector: () => element,
    data: {
        src: (query) => fetch( element.dataset.suggestionsEndpoint )
                                     .then(r => r.json())
    }
} ) );

This works, but has the drawback of creating several instances instead of a single one.

Additional context

Brainshaker95 commented 2 years ago

@TarekRaafat What do you think about this issue? If this is something we want for the project I would gladly provide a PR for that. I definitely see the use case and I also had a case like this already where this feature would have been useful.

jonnsn commented 1 year ago

+1

I tried the alternative @Radiergummi (thanks for that!) proposed, but could not get it to work.

Uncaught (in promise) TypeError: element is undefined

in line src: (query, element) => fetch( element.dataset.suggestionsEndpoint )

Radiergummi commented 1 year ago

I tried the alternative @Radiergummi (thanks for that!) proposed, but could not get it to work.

Uncaught (in promise) TypeError: element is undefined

Sorry, that was copy pasted sloppily! If you remove the second parameter element from the callback, it will use the instance from the parent scope, and work.