Autocomplete
Typeahead search component with a dropdown result list. Extends GuiCommons and is used for searching records with server-side or client-side filtering. The component provides a text input, a search button, a clear button, and a dropdown menu that displays matching results as the user types.
Import: @io-oidis-usercontrols/.../Autocomplete.js (also exports AutocompleteItem, IAutocompleteHandlers, IAutocompleteQuery, IAutocompleteLocalization)
The localization property is protected and embedded into HTML at construction time via innerHtml(). You cannot set it from the controller after creation — always use a subclass with constructor overrides. See Localization below.
Basic Usage
Declare the control in the panel, bind it in the template, and configure handlers in the controller.
// Panel
public readonly userSearch : Autocomplete;
// Constructor
this.userSearch = new Autocomplete();
<!-- innerHtml() -->
<div data-oidis-bind="${this.userSearch}"></div>
// Controller — bind in beforeLoad() or resolver()
$instance.userSearch.Bind({
fetchHandler: async ($query : any) : Promise<IAutocompleteData[]> => {
return (await this.client.SearchUsers($query)).data;
},
onSearch: async ($query : IAutocompleteQuery) : Promise<void> => {
this.selectedUser = null;
await this.refreshResults($query);
}
});
Unlike simple controls (TextField, Button) that use = undefined, Autocomplete must be created with new Autocomplete() in the panel constructor because it is a composite component that initializes internal sub-components (searchField, search, clear, etc.) during construction.
Bind Handlers
The Bind() method accepts an IAutocompleteHandlers object that configures the component behavior.
| Handler | Signature | Purpose |
|---|---|---|
fetchHandler | ($query : any) => Promise<IAutocompleteData[]> | Fetch results from server. Receives the query (text by default, or custom object via setSearchToQueryFormatter). Required. |
dataToItemMapper | ($data : IAutocompleteData, $item : AutocompleteItem) => Promise<void> | Map each result to an AutocompleteItem. Default: sets Data, Text, and Value from the IAutocompleteData fields. |
itemCreateFormatter | ($item : AutocompleteItem, $index : number) => Promise<void> | Custom setup for each dropdown item. Default: binds click to select the item, set the value, and close the dropdown. |
onSearch | ($query : IAutocompleteQuery) => Promise<void> | Fired when the search button is clicked. Receives {query, id}. Required. |
onSelect | ($item : AutocompleteItem) => void | Fired when keyboard navigation highlights an item. Default: sets Active(true) and scrolls the item into view. |
onClear | () => Promise<void> | Fired when the clear button is clicked (after the component resets). Default: calls onSearch(null) when ReloadOnClear is enabled. |
The default dataToItemMapper and itemCreateFormatter handle standard use cases. Override them only when you need custom item rendering (icons, badges, multi-line content) or non-standard selection behavior.
IAutocompleteData
The data structure returned by fetchHandler and used internally by the dropdown items.
interface IAutocompleteData {
text : string; // Display text for the item
value? : any; // Associated data object (typically the record)
}
IAutocompleteQuery
The query object passed to onSearch and used internally when SearchOnSelect triggers a search after item selection.
interface IAutocompleteQuery {
query? : string; // Search text from the input field
id? : string; // Associated identifier (from selected item's data)
}
Configuration
Getter/setter properties for controlling component behavior. Set these after construction, before or after Bind().
| Property | Type | Default | Purpose |
|---|---|---|---|
Trigger | number | 3 | Minimum characters before triggering fetchHandler. Must be greater than 0. |
ReloadOnClear | boolean | false | When cleared, trigger onSearch(null) via the onClear handler. |
HintOnFocus | boolean | false | When the search field receives focus, trigger a fetch immediately. |
SearchOnSelect | boolean | false | When an item is selected from the dropdown, trigger onSearch with the current query and selected item id. |
SearchButtonVisible | boolean | true | Show or hide the search button. Note: hiding is blocked when SearchOnSelect is false (the search button remains as the only way to trigger search). |
// Example: configure for immediate search on focus with low trigger threshold
$instance.userSearch.Trigger = 1;
$instance.userSearch.HintOnFocus = true;
$instance.userSearch.SearchOnSelect = true;
$instance.userSearch.SearchButtonVisible = false;
Value and Data
| Method / Property | Purpose |
|---|---|
Value (get/set) | Get or set the search text. The setter strips HTML tags for safety. |
Data($value?) | Get or set the associated data object (typically the selected record). Delegates to the internal autocomplete component. |
Clear() | Full reset: clears the input value, removes all result items, clears associated data, re-enables text editing, and hides the clear button. |
Show() | Opens the dropdown using the Bootstrap Dropdown API and focuses the search field. |
Hide() | Closes the dropdown. |
// Read current search text
const searchText : string = $instance.userSearch.Value;
// Set value programmatically
$instance.userSearch.Value = "John Doe";
// Read associated data (set when an item is selected)
const selectedUser : User = <User>$instance.userSearch.Data();
// Full reset
$instance.userSearch.Clear();
Custom Formatters
Two protected methods control how the search text is transformed into a query and how results are transformed into display text. Override them via public setters.
Search-to-Query Formatter
Transforms the raw search text into a query object passed to fetchHandler. Default: passes the text string as-is.
$instance.userSearch.setSearchToQueryFormatter(($value : string) : any => {
return { query: $value, type: "user" };
});
Result-to-Value Formatter
Transforms an IAutocompleteData result into the display text shown in the input field when an item is selected. Default: returns $data.text.
$instance.userSearch.setResultToValueFormatter(($value : IAutocompleteData) : string => {
return `${$value.value.FirstName} ${$value.value.LastName}`;
});
Keyboard Navigation
The component supports full keyboard navigation when the dropdown is open:
| Key | Action |
|---|---|
| Arrow Down | Move highlight to the next item (wraps to first) |
| Arrow Up | Move highlight to the previous item (stops at first) |
| Enter | Select the highlighted item, or re-fetch if data is already set and no item is highlighted |
| Escape | Close the dropdown and clear the selection state |
Navigation state is tracked via an internal currentIndex. The onSelect handler is called for each highlighted item (default: sets Active(true) and scrolls the dropdown).
Localization
The localization property controls the search button text and the input placeholder. Since it is embedded into HTML during innerHtml(), it must be set in the constructor via subclassing.
interface IAutocompleteLocalization {
searchBtnText : string; // Search button label (default: "Search")
placeholderHint : string; // Input placeholder (default: "Looking for something?")
}
export class MyAutocomplete extends Autocomplete {
constructor() {
super();
this.localization = {
searchBtnText : "Find",
placeholderHint : "Type to search..."
};
}
}
localization is protected and embedded into HTML at construction time via innerHtml(). You cannot set it from the controller after the component is created — the HTML is already rendered with the default values. Always use a subclass with constructor overrides. This follows the same pattern as DropDownMultiselect.
Subclassing
The following protected properties and methods are available for override in subclasses:
| Member | Type | Purpose |
|---|---|---|
localization | IAutocompleteLocalization | Localization strings for button and placeholder |
searchButtonOrder | string | CSS class for search icon alignment (default: "", use "text-end" for right-aligned) |
autocompleteSelector | Button (readonly) | Internal invisible button that triggers the Bootstrap dropdown |
searchField | TextField (readonly) | Internal text input for search queries |
items | AutocompleteItem[] | Array of currently displayed result items |
autocomplete | GuiCommons (readonly) | The dropdown menu container |
search | Button (readonly) | The search button |
clear | Button (readonly) | The clear/reset button |
searchToQuery($value) | Method | Override to transform search text to query (alternative to setSearchToQueryFormatter) |
resultToValue($result) | Method | Override to transform result data to display text (alternative to setResultToValueFormatter) |
getNewItemInstance() | Method | Override to return a custom AutocompleteItem subclass |
fetchData() | Method | Override to customize the entire fetch-and-render cycle |
innerHtml() | Method | Override to customize the component HTML structure |
AutocompleteItem
AutocompleteItem extends DropDownListItem and is the class used for each result in the dropdown. It can be subclassed for custom item rendering, and getNewItemInstance() can be overridden to return the custom subclass.
Complete Example
A full example with fetchHandler, dataToItemMapper, onSearch, onSelect override, and custom formatters.
// Panel
public readonly employeeSearch : Autocomplete;
// Constructor
this.employeeSearch = new Autocomplete();
<!-- innerHtml() -->
<div class="col-6" data-oidis-bind="${this.employeeSearch}"></div>
// Controller — beforeLoad()
$instance.employeeSearch.Trigger = 2;
$instance.employeeSearch.ReloadOnClear = true;
$instance.employeeSearch.setResultToValueFormatter(($value : IAutocompleteData) : string => {
const emp : Employee = <Employee>$value.value;
return `${emp.FirstName} ${emp.LastName} (${emp.Department})`;
});
$instance.employeeSearch.Bind({
fetchHandler: async ($query : any) : Promise<IAutocompleteData[]> => {
const results : Employee[] = (await this.client.SearchEmployees($query)).data;
return results.map(($emp : Employee) : IAutocompleteData => ({
text: `${$emp.FirstName} ${$emp.LastName}`,
value: $emp
}));
},
onSearch: async ($query : IAutocompleteQuery) : Promise<void> => {
this.selectedEmployee = null;
await $instance.table.ShowData(true);
},
itemCreateFormatter: async ($item : AutocompleteItem, $index : number) : Promise<void> => {
$item.getEvents().setOnClick(() : void => {
this.selectedEmployee = <Employee>$item.Data().value;
$instance.employeeSearch.Value = `${this.selectedEmployee.FirstName} ${this.selectedEmployee.LastName}`;
$instance.employeeSearch.Data($item.Data());
$instance.employeeSearch.Hide();
});
},
onClear: async () : Promise<void> => {
this.selectedEmployee = null;
await $instance.table.ShowData(true);
}
});
See also: User Controls | Data Binding | Events and Validation