Personal website for Trevor Eyre
trevoreyre / autocomplete Goto Github PK
View Code? Open in Web Editor NEWAccessible autocomplete component for vanilla JavaScript and Vue.
Home Page: https://autocomplete.trevoreyre.com
License: MIT License
Accessible autocomplete component for vanilla JavaScript and Vue.
Home Page: https://autocomplete.trevoreyre.com
License: MIT License
Hello,
is it possible to send html in response? If yes, how ?
Thanks
I've tried both importing the Vue component, and just linking to the browser bundle. Either way I get the same issue
The component simply renders as <!---->
and nothing is displayed at all.
And clicking anywhere on the page gives a console error from the handleDocumentClick
method
TypeError: this.$refs.root is undefined
For styling purposes, it would be easier to have an option to keep the results list open. What do you suggest to accomplish that?
I tired to use same method I use for some of my components:
import Autocomplete from '@trevoreyre/autocomplete-vue';
function createAutocomplete(el, options) { // el is just empty div
let component = new Autocomplete({
propsData: options
});
component.$mount(el);
return component;
}
But have an error: "TypeError: WEBPACK_IMPORTED_MODULE_1__trevoreyre_autocomplete_vue.a is not a constructor".
Is there a way to create component dynamically (by demand) and mount to DOM element? It's preferable to have it as root Vue instance.
Hi, I' like to brig your attention on this issue:
As you can test in the example
https://autocomplete.trevoreyre.com/#/vue-component?id=submit
if you type the name of a country (also completely e.g Cambodia) and pres enter key you will receive an undefined in the "result" argument of function handleSubmit.
This is quite boring when reading from a barcode for example.
Thanks for attention.
GT
I'm using Autocomplete from '@trevoreyre/autocomplete-vue' with version 2.1.0.
In my Vue component, I search for a user, then change same values and submit the changed user to the backend. If this succeeds I want to reset all input fields, including the autocomplete. How can this be done in a way, that also the internal state of autocomplete is empty...
Instead of a single Autocomplete
component, It would be better to separate the component into its individual parts, for easier overriding and styling.
<Autocomplete>
<AutocompleteInput />
<AutocompleteResults>
<AutocompleteResult>Result 1</AutocompleteResult>
<AutocompleteResult>Result 2</AutocompleteResult>
</AutocompleteResults>
</Autocomplete>
Autocomplete executes the search
function on focus. If this function returns an initial set of results on focus, and the autoSelect
prop is set to true, it could potentially be trouble.
When the input value is Korean, pressing the Keydown button will call search().
So the result is reloaded and keydown doesn't work.
Perhaps in my opinion, other than English, the languages ββare the same.
The main benefit will be enabling deploy previews. Plus, all my other sites are on Netlify, so it's nice to be consistent.
I'm newb, and i added some codes to your code for vue component, succeed adding placeholder atributes, but not with the required. How do i add that required feature? Thanks
Hello,
First of all thanks for your component it is working great for me!
I have a small problem related to an edgy use case I have; I need to clear the input and its results and then focus to the input programmatically.
I am using Vue. I managed to do it with
this.$refs.autocomplete.value = '';
this.showResults = false;
this.$refs.autocomplete.$el.firstElementChild.children[0].focus();
I think when I focus it is triggering a search because sometimes it will show me results from what used to be the value.
This is what I mean: you can see the old result while the input is empty:
I also use an async answer coming from axios if it helps.
I think it is related to this: #15 "Autocomplete executes the search function on focus". I really wish I could disable this somehow.
My hacky approach that does not seem to always work is to have a showResults
boolean in my Vue data, setting it to false before focusing and doing this in my search function:
search: function(input) {
if (input === '') {
return [];
}
return axios.get(...))
.then(response => {
if (! this.showResults) {
this.showResults = true;
return [];
}
return response.data;
});
},
Possibly add an options
prop, which is the full list of potential options that can be chosen from, automatically filtered by the component, in addition to a results
prop, which is the list of filtered results that should be shown.
Hello, I really liked the vuejs component, however I can not get the onblur to trigger an event/function like the onsubmit.
Is there any way in onblur to run the onsubmit or the same function set for it?
Can you provide an example of how to render a button next to the autocomplete? This seems like an common UI pattern.
I guess the root div wrapper cannot be inline-block correct?
I was able to modify the example with default slot (full control), but does one need to write all this in order to render an inline button?
Is there an simpler approach?
For this so common use case, would you consider adding a slot for rendering some content directly after or directly before the input element?
I'm very new to Vue, and I cannot find a way to clear the autocomplete value
after a submit request has been handled.
Any advice?
It would be a nice Prop as well.
Hi, im not sure why is this happening. it shows
client.js?06a0:83 ReferenceError: countries is not defined
at VueComponent.search (sidebar.vue?480e:27)
at AutocompleteCore.search (autocomplete.esm.js?95bf:300)
at AutocompleteCore.eval (autocomplete.esm.js?95bf:238)
at AutocompleteCore.eval (autocomplete.esm.js?95bf:127)
at VueComponent.handleInput (autocomplete.esm.js?95bf:536)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at HTMLInputElement.invoker (vue.runtime.esm.js?2b0e:2179)
at HTMLInputElement.original._wrapper (vue.runtime.esm.js?2b0e:6917)
vue__WEBPACK_IMPORTED_MODULE_22__.default.config.errorHandler @ client.js?06a0:83
globalHandleError @ vue.runtime.esm.js?2b0e:1870
handleError @ vue.runtime.esm.js?2b0e:1839
invokeWithErrorHandling @ vue.runtime.esm.js?2b0e:1862
invoker @ vue.runtime.esm.js?2b0e:2179
original._wrapper @ vue.runtime.esm.js?2b0e:6917
the code I've written is exactly the same from the vuecomponent pages
<div id="autocomplete">
<autocomplete :search="search"></autocomplete>
</div>
<script>
export default {
methods: {
search(input) {
if (input.length < 1) { return [] }
return countries.filter(country => {
return country.toLowerCase()
.startsWith(input.toLowerCase())
})
}}
}
</script>
I cannot delete the text of the input. How to do ?
Thanks !
Hey Trevor,
In our project, we don't have support for Webpack, Node, ES6 modules (import/export).
I was able to integrate Vue but all my Vue components have separate Vue DOM templates.
Your autocomplete-vue package provides a single file Vue component, so my question is:
Should I use the javascript package and provide a Vue wrapper myself, or would you think it's easier to refactor you single file component into multiple files (js, html, css) ?
I have a :keyup function that pushes the input value to an array when enter is pressed. That part works fine. I'm trying to clear the input at the end of the function with
input.value='';
This works in the console, but not in the :keyup function. Is this a bug or something I'm doing wrong?
Hi there,
I'm trying to use this autocomplete vue in a traditional php form (laravel) and I need to setup the name of the input in order to the value to get passed to the form action.
There is any way of doing this? or maybe workaround?
Thank you in advance!
I couldn't find an option to clear the selected value in the input, can any one guide me on doing the same?
I need to restrict API calls while writing suggestion. Can I fetch a call to debounce?
Hi,
In vue.js can I bind the initial value / default value so that when my page initially loads it has the data in the autocomplete i grab from a database?
Cheers
Mark
Hey, I was wondering if there is some kind of strict mode. Let's say we have a list of countries and the user starts typing germa and then hits the space bar. In this case we have germa instead of germany in the input field.
In the end I want to have the country id in a hidden field. Thats working if the user behaves as I want...but only then :)
Thanks
In Chrome the native browser autocomplete pops up over the fetched results. This causes arrow down to select the native autocomplete suggestion instead of navigating over the results from this library.
Image should be self-explanatory:
This doesn't happen in any of your examples cause the inputs dont have any names assigned.
Chrome: Version 77.0.3865.75 (Official Build) (64-bit)
Does not happen on Firefox.
Uppon further debugging this is caused when the input
has name
that browser is able to cache in local storage.
See reproduced issue here on example: https://codepen.io/versedi-ts/pen/eYYwqmx
Type something, press enter, refresh page and focus the input or type something in it.
I'm getting a "TypeError: results.forEach is not a function" in autocomplete.cjs.js:445:13
when I try setting up a asynchronous API call to https://restcountries.eu/rest/v2/name/
The error occurs when no match is found, and I think it's because restcountries.eu
returns a different JSON structure when there's no valid match.
Example call where matches are found.
https://restcountries.eu/rest/v2/name/den
Example call where NO MATCH is found.
https://restcountries.eu/rest/v2/name/qzx
I've tried to add debouncing to the search function but the component will not update after the results array is updated. I also tried to call the handleUpdate and handleShow methods directly which will show the results appropriately but clicking on any result fails to populate the event with the result object. Is there any way to update the control after a debounced API call completes? Thanks.
It seems like getResultValue
must return a string, while it could be interesting that it is able to return HTML to allow subformatting (eg. highlighting).
I guess this should be optionable, but for instance there could be another attribute :get-result-value-html="getResultValue"
which would <span v-html="getResultValueHtml(result)"></span>
instead of {{getResultValue(result)}}
.
The best solution would be to being able to return a component, but I have the impression that it is not possible with vue, is it?
I have the following modified code from docs example: https://codepen.io/trevoreyre/pen/vbqpxv
I just wrapped in in a form, since this are my requirements:
<div id="app">
<form action="www.test.com/search" method="GET">
<autocomplete
:search="search"
placeholder="Search Wikipedia"
aria-label="Search Wikipedia"
:get-result-value="getResultValue"
@submit="handleSubmit"
></autocomplete>
</form>
</div>
my problem is that handleSubmit
is called but the default form
action is faster, causing the page to reload and do the server GET.
There is any way to preventDefault on the form? or another ideas?
Thank you!
Normally when using the arrow keys to scroll through the results, if you reach the end of the container it will automatically scroll to keep the next selection in view, as seen here:
However when using the default slot to define our own markup, the results container is not automatically scrolled, as seen here:
These examples were taken directly from the documentation page in Chrome 80.
Currently there's no index.d.ts
nor package @types/autocomplete-js
available.
Types can be defined inside library itself in index.d.ts or added to the DefinitelyTyped repository here: https://github.com/DefinitelyTyped/DefinitelyTyped
I never created typings myself, but from what I know it would be something like this for DefinitelyTyped
:
// Type definitions for autocomplete-js
// Project: https://github.com/trevoreyre/autocomplete/, https://autocomplete.trevoreyre.com/
// Definitions by: versedi <https://github.com/versedi>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
export as namespace Autocomplete;
// --------------------------------------------------------------------------------------
// Options Interfaces
// --------------------------------------------------------------------------------------
export interface AutocompleteOptions {
/**
* The search function to be executed on user input. Can be a synchronous function or a Promise.
* @param input
*/
search(input: string): Function|Promise<string>,
/**
* Controls whether first result should be highlighted after input
* Optional
*/
autoSelect?: boolean,
/**
* Sets the value of the calling component's input element
*/
setValue? (): Function,
/**
* Sets attributes on the calling component's input element
*/
setAttribute? (): Function,
/**
* Fired when the results list is updated. Receives results (Array), and selectedIndex (Number) as arguments.
*/
onUpdate? (): Function,
/**
* Fired when user submits result. Receives result as argument.
* @param result
*/
onSubmit(result: object): void,
/**
*
* @param result
* @param props
*/
renderResult(result: object, props: Props): string
/**
* Fired when the results list is shown
*/
onShow? (): Function,
/**
* Fired when the results list is hidden
*/
onHide? (): Function,
/**
* Fired if search is a Promise and hasn't resolved yet
*/
onLoading? (): Function,
/**
* Fired after asynchronous search function resolves
*/
onLoaded? (): Function,
}
export interface EventHandlers {
handleInput: Function,
handleKeyDown: Function,
handleBlur: Function,
handleResultMouseDown: Function,
handleResultClick: Function,
}
// --------------------------------------------------------------------------------------
// Autocomplete
// --------------------------------------------------------------------------------------
declare global {
interface Autocomplete<TElement = HTMLElement> {
}
}
The global declaration would probaly require some tweaking.
For self-alone index.ts that anyone can put into his typeRoots
in tsconfig.json
:
declare module '@trevoreyre/autocomplete' {
export default class Autocomplete<TElement = HTMLElement> {
constructor(inputSelector: string, options?: AutocompleteOptions);
}
/**
* * Creates a props object with overridden toString function. toString returns an attributes
string in the format: `key1="value1" key2="value2"` for easy use in an HTML string.
*/
export class Props {
/**
* @param index
* @param selectedIndex
* @param baseClass
*/
constructor(index: string, selectedIndex: string, baseClass: string);
toString(): string
}
export interface AutocompleteOptions {
/**
* The search function to be executed on user input. Can be a synchronous function or a Promise.
* @param input
*/
search(input: string): Function|Promise<string>,
/**
* Controls whether first result should be highlighted after input
* Optional
*/
autoSelect?: boolean,
/**
* Sets the value of the calling component's input element
*/
setValue? (): Function,
/**
* Sets attributes on the calling component's input element
*/
setAttribute? (): Function,
/**
* Fired when the results list is updated. Receives results (Array), and selectedIndex (Number) as arguments.
*/
onUpdate? (): Function,
/**
* Fired when user submits result. Receives result as argument.
* @param result
*/
onSubmit(result: object): void,
/**
*
* @param result
* @param props
*/
renderResult(result: object, props: Props): string
/**
* Fired when the results list is shown
*/
onShow? (): Function,
/**
* Fired when the results list is hidden
*/
onHide? (): Function,
/**
* Fired if search is a Promise and hasn't resolved yet
*/
onLoading? (): Function,
/**
* Fired after asynchronous search function resolves
*/
onLoaded? (): Function,
}
export interface EventHandlers {
handleInput: Function,
handleKeyDown: Function,
handleBlur: Function,
handleResultMouseDown: Function,
handleResultClick: Function,
}
}
and autocomplete-js
typings:
declare module '@trevoreyre/autocomplete-js' {
export default class Autocomplete<TElement = HTMLElement> {
constructor(inputSelector: string, options?: AutocompleteJsOptions);
}
/**
* * Creates a props object with overridden toString function. toString returns an attributes
string in the format: `key1="value1" key2="value2"` for easy use in an HTML string.
*/
export class Props {
/**
* @param index
* @param selectedIndex
* @param baseClass
*/
constructor(index: string, selectedIndex: string, baseClass: string);
toString(): string
}
{
/**
* The search function to be executed on user input. Can be a synchronous function or a Promise.
* @param input
*/
search(input: string): Function|Promise<string>,
/**
* Fired when user submits result. Receives result as argument.
* @param result
*/
onSubmit(result: object): void,
baseClass?: string,
/**
* Controls whether first result should be highlighted after input
*/
autoSelect?: boolean,
/**
* For complex search results, this function is executed to get the value to display in the input
* @param result
*/
getResultValue? (result: object): void,
/**
*
* @param result - The result value returned from your search function
* @param props - An object containing generated attributes for the result item, which are expected to be set on your li element. The object has a custom toString function which lets you easily serialize it to a String of HTML attributes in the form attribute1="value1" attribute2="value2". This way, you don't have to worry about generating the proper IDs, classes, and ARIA attributes yourself.
*/
renderResult? (result: object, props: Props): string|Element
}
}
how can I have multiple instances at a time in a v-for loop. Is there a way to pass an index to the submit handler along with the result? I'm struggling to find a way to handle the submit event of dynamically added autocomplete instances.
For example:
<div v-for="(airport, index) in airports">
<autocomplete :search="airportAutocomplete" @submit="handleSubmit(result, index)"></autocomplete>
</div>
...
data() {
return {
airports: [
{
code: '',
},
{
code: '',
},
{
code: '',
}
]
}
},
methods: {
handleSubmit(result, index) {
this.airports[index].code = result.code
}
}
I would like to use this to hit an API (which I am doing successfully) but rate limits aren't infinite and every single keystroke is a call to the api. Is there a way to delay the search function for like at least one or two seconds after typing?
In my case, I need to get the input value on the click of another button.
After inspecting the component, I'm doing that so now:
handleAddNewGuest() {
const name = this.$refs.addAttendeeField.core.value;
}
Another approach would be to always keep a backup of the current value in my parent:
data() {
return {
currentInputValue: ""
}
},
search(input) {
this.currentInputValue = input;
}
Will I always have access to core.value
with newer releases? If there is a more standard approach, then perhaps I missed it?
Thanks!
I can't seem to hook into the native DOM 'change' event with Vue. How do I do this? I've tried @change
and putting a method in there. But it's not working.
i cant able to reset or empty the value in autocomplete field after submitting. could you please let me know how to empty the value.
I have a Vue component that has a data item in it; before using the Autocomplete component, I could do something like this in my component's template:
<input v-model="year">
This creates all the magic two-way bindings for me... but when I'm using the Autocomplete component, it's unclear to me how I could achieve the same results? I'm looking for something like this (p-code):
<Autocomplete v-model="year"
:search="searchYears"
>
</Autocomplete>
I'm going for a two-way binding between my component's data, and the Autocomplete result. Would I need to do something like override inputProps
somehow? Or use named slots to override the default template as per this example? If so, how?
I feel like I'm missing something, because this seems like it'd be a common use-case where we want to use v-model
to set up the bindings for the Autocomplete result.
Reproduce pen: https://codepen.io/versedi-ts/pen/eYYwqmx
The image used in result is requested every time user presses arrow down/arrow up.
Would it be possible to trigger the network request only on first select? Not sure why it's triggered on the select event - the images are already downloaded by browser when autocomplete is opened so that request is completely unnecessary.
Would it be possible to leverage browser caching and use the previously downloaded image? The request URL is the same, not sure why this doesn't happen out of the box.
When using the VueComponent layer, when the AutocompleteCore handles the "Tab" keypress, it doesn't trigger the onSubmit method. The value gets filled to the input but I can't update my object when the submit event is triggered. I'm not sure if this was by design or not, but I would expect Tab and Enter to be handled similarly.
Would be useful to have some option that defines what events should trigger search and display dropdown with results. In my case I'm trying to replicate the behavior of my old jQuery component, that was custom select with search ability. So I wolud like it to dropdown on value change or ArrowUp, ArrowDown, Enter keys.
Currently it's not possible to remove the autocompleter instance.
Hello, is it possible to update the input value from outside with vue js.
I use v-model, but its not seem to work.
Any idea ?
methods like search
onSubmit
should pass in the root node of the component or the input component. When dynamically creating autocompletes, this reference may be necessary.
Also, please create a Core
Vue
label for issues and maybe even a v2-patch
and v3
label
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.