@ngui/auto-complete
v22.0.0
Published
Angular Input Autocomplete
Readme
@ngui/auto-complete
A versatile Angular autocomplete library that works as both a directive (attached to any input) and a standalone component.
Installation
npm install @ngui/auto-complete @angular/cdkThe directive positions its dropdown with the CDK Overlay, so
@angular/cdk is a peer dependency and your app must include the CDK overlay styles once:
/* styles.scss — skip this if you already import an Angular Material theme (it bundles them) */
@import '@angular/cdk/overlay-prebuilt.css';Styling the dropdown: because the directive renders its dropdown in an overlay at the document root, any custom dropdown styles (e.g. classes used by a
list-formatteror template) must be global, not scoped to an ancestor of the input. See Theming to restyle it with CSS variables.
Setup
Standalone (Angular 21+)
The component and directive are standalone. Import them directly into your standalone component
(or NgModule) imports:
import { NguiAutoCompleteComponent, NguiAutoCompleteDirective } from '@ngui/auto-complete';
@Component({
// ...
imports: [FormsModule, NguiAutoCompleteComponent, NguiAutoCompleteDirective],
})
export class MyComponent {}NgModule (Angular 20 and below)
NguiAutoCompleteModule was removed in v21 — import the standalone component/directive shown above
instead. If your app still relies on the NgModule, install the matching major and import the module as
before:
- Angular 20 →
npm install @ngui/auto-complete@20(standalone, butNguiAutoCompleteModuleis still re-exported for back-compat). - Angular 19 and older →
npm install @ngui/auto-complete@19.
import { NguiAutoCompleteModule } from '@ngui/auto-complete';
@NgModule({
imports: [BrowserModule, FormsModule, NguiAutoCompleteModule],
})
export class AppModule {}Usage
As a Directive
Attach to any element containing an input. The directive is a ControlValueAccessor, so it binds with
[(ngModel)] like any form control:
<!-- Directly on an input -->
<input ngui-auto-complete [(ngModel)]="myValue" [source]="myArray" />
<!-- On a wrapping div: put the value binding on the host element -->
<div ngui-auto-complete [(ngModel)]="myValue" [source]="myArray">
<input />
</div>Reactive forms
Because the directive is a ControlValueAccessor, [formControl] and formControlName work directly:
<input ngui-auto-complete [formControl]="cityControl" [source]="cities" />
<form [formGroup]="form">
<input ngui-auto-complete formControlName="city" [source]="cities" />
</form>As a Component
Use <ngui-auto-complete> directly, control its visibility with @if:
<input [(ngModel)]="myValue" (focus)="show = true" (blur)="show = false" />
@if (show) {
<ngui-auto-complete
[source]="myArray"
[show-input-tag]="false"
[show-dropdown-on-init]="true"
[(value)]="myValue">
</ngui-auto-complete>
}Remote / Observable Source
<input ngui-auto-complete
[(ngModel)]="address"
[source]="searchFn"
path-to-data="results"
list-formatter="formatted_address"
min-chars="2" />searchFn = (keyword: string): Observable<any> => {
return this.http.get(`https://api.example.com/search?q=${keyword}`);
};Custom dropdown templates
Pass Angular ng-templates for custom rendering: itemTemplate (each row — receives the item as
$implicit and the row index as index), headerTemplate (a header row), and loadingTemplate (shown
while remote data loads). They work on both the component and the directive:
<input ngui-auto-complete [(ngModel)]="myValue" [source]="myArray"
[itemTemplate]="row" [headerTemplate]="head" />
<ng-template #head>Suggestions</ng-template>
<ng-template #row let-item let-i="index">
<strong>{{ i + 1 }}.</strong> {{ item.name }} — <em>{{ item.country }}</em>
</ng-template>API Reference
One reference for both surfaces. The Applies to column says whether each option works on the directive
([ngui-auto-complete]), the component (<ngui-auto-complete>), or both. Every option keeps its
kebab-case attribute name; numeric/boolean inputs accept both the attribute form (min-chars="2") and the
bound form ([min-chars]="2").
Value & forms
How the selected value is read and written.
| Option | Type | Default | Applies to | Description |
|--------|------|---------|------------|-------------|
| [(ngModel)] / [formControl] / formControlName | T | — | Directive | The directive is a ControlValueAccessor, so the value flows through Angular forms. (ngModelChange) / the control's valueChanges fire on every accepted value |
| [(value)] | T | — | Component | Two-way bindable selected value on the standalone component |
| select-value-of | string | — | Directive | Commit this property's value on selection instead of the whole object |
Data & filtering
Where suggestions come from and how the keyword is matched.
| Option | Type | Default | Applies to | Description |
|--------|------|---------|------------|-------------|
| source | T[] \| string \| ((keyword) => Observable<T[]>) | — | Both | Required (input.required — omitting [source] is a compile-time error). Local array, URL string, or a function returning an Observable |
| path-to-data | string | — | Both | Dot-path to the array in an HTTP response, e.g. data.results |
| min-chars | number | 0 | Both | Minimum characters before fetching/filtering |
| max-num-list | number | unlimited | Both | Maximum number of suggestions to show |
| match-formatted | boolean | false | Both | Match the keyword against formatted values instead of the raw data |
| ignore-accents | boolean | true | Both | Treat accented characters as their base characters when matching |
Display & formatting
How rows and the selected value are rendered.
| Option | Type | Default | Applies to | Description |
|--------|------|---------|------------|-------------|
| display-with | string \| ((item) => string) | item's value | Directive | Text shown in the input after selecting an object — a property name (display-with="name") or a function ([display-with]="fn") |
| list-formatter | string \| ((item) => string) | — | Both | Format each dropdown row. String pattern (key) name or a function |
| itemTemplate | TemplateRef | — | Both | ng-template for each dropdown row (context: $implicit = item, index = row index). Takes precedence over list-formatter |
| headerTemplate | TemplateRef | — | Both | ng-template for a non-selectable header row |
| loadingTemplate | TemplateRef | — | Both | ng-template shown while remote data loads (falls back to loading-text) |
| loading-text | string | 'Loading' | Both | Text shown while fetching remote data |
| blank-option-text | string | — | Both | Adds an empty first option with this label |
| no-match-found-text | string | — | Both | Text shown when nothing matches. Set to "" to suppress the row entirely |
| placeholder | string | — | Component | Placeholder for the component's internal input |
| auto-complete-placeholder | string | — | Directive | Placeholder for the dropdown's (normally hidden) internal input |
Behavior
Interaction and selection behavior.
| Option | Type | Default | Applies to | Description |
|--------|------|---------|------------|-------------|
| accept-user-input | boolean | true | Both | Allow values that are not in the list |
| auto-select-first-item | boolean | false | Both | Pre-highlight the first suggestion |
| select-on-blur | boolean | false | Both | Select the highlighted item on blur |
| tab-to-select | boolean | true | Both | Select the highlighted item on the Tab key |
| re-focus-after-select | boolean | true | Both | Return focus to the input after a selection |
| autocomplete | boolean | false | Both | When false, sets the native autocomplete="off" on the input |
| open-on-focus | boolean | true | Directive | Open the dropdown when the input gains focus |
| close-on-focusout | boolean | true | Directive | Close the dropdown on focusout |
| show-input-tag | boolean | true | Component | Render an <input> inside the component |
| show-dropdown-on-init | boolean | false | Component | Open the dropdown as soon as the component appears |
Layout & positioning
| Option | Type | Default | Applies to | Description |
|--------|------|---------|------------|-------------|
| open-direction | 'auto' \| 'up' \| 'down' | 'auto' | Both | Preferred side. For the directive (CDK overlay) up/down set the preference and the overlay still flips when there isn't room; for the component, up renders above via CSS and auto/down keep it below |
| z-index | number | 1 | Directive | z-index of the dropdown overlay. Rarely needed — the CDK overlay already renders above page content; only useful to order overlapping overlays |
RTL: there is no RTL input — the directive's overlay follows the input's computed direction, and the component's drop-up anchors via logical CSS (
inset-inline-start). Just setdir="rtl"on the element or an ancestor (or the document direction).
Events
Both surfaces emit the same two outputs.
| Output | Payload | Applies to | Description |
|--------|---------|------------|-------------|
| (valueSelected) | NguiAutoCompleteSelection | Both | Fires when a value is committed. Use fromSource to tell a list pick from a typed value |
| (noMatchFound) | void | Both | Fires when the filtered list is empty and the min-chars threshold is met — use it to show an "Add new…" affordance |
The (valueSelected) payload:
interface NguiAutoCompleteSelection<T = any> {
value: T; // the committed value (same as [(ngModel)] / [(value)])
item: T; // the full picked object (or the typed text)
index: number; // row in the shown list; -1 when typed (fromSource = false)
fromSource: boolean; // true = picked from [source]; false = typed by the user
}Type inference
NguiAutoCompleteComponent<T = any> is generic. Bind a typed [source] (a typed array or a function
returning Observable<T[]>) and Angular infers the item type — [(value)], (valueSelected)
(NguiAutoCompleteSelection<T>) and the itemTemplate context are then all typed with no extra
annotation:
<ngui-auto-complete [source]="cities" [(value)]="city" (valueSelected)="onPick($event)"></ngui-auto-complete>cities: City[] = [/* … */];
city?: City;
onPick(e: NguiAutoCompleteSelection<City>) { /* e.item is City */ }It defaults to any, so existing templates are unaffected. The directive ([ngui-auto-complete]) stays
loosely typed — Angular can't infer a generic for an attribute directive in templates, so its
(valueSelected) payload is NguiAutoCompleteSelection<any>.
Theming
Restyle the dropdown by overriding these CSS variables. Each has a sensible default, so set only what you
need. Set them on :root — the directive's dropdown renders in an overlay at the document root, so
variables on an ancestor of the input won't reach it.
| Variable | Default | Controls |
|----------|---------|----------|
| --ngui-ac-background | #fff | Dropdown background |
| --ngui-ac-color | inherit | Text color |
| --ngui-ac-border | 1px solid rgba(0,0,0,.12) | Dropdown border |
| --ngui-ac-border-radius | 4px | Corner radius |
| --ngui-ac-shadow | 0 4px 12px rgba(0,0,0,.15) | Elevation shadow |
| --ngui-ac-max-height | 256px | Height cap (none to remove) |
| --ngui-ac-z-index | 10 | Stacking order of the floating list (standalone <ngui-auto-complete>; the directive's overlay uses the z-index input instead) |
| --ngui-ac-item-padding | 6px 12px | Row padding (density) |
| --ngui-ac-item-border | 1px solid rgba(0,0,0,.06) | Row divider |
| --ngui-ac-hover-background | rgba(0,0,0,.06) | Row hover background |
| --ngui-ac-selected-background | rgba(0,0,0,.1) | Highlighted row background |
/* e.g. a dark dropdown */
:root {
--ngui-ac-background: #2b2b2b;
--ngui-ac-color: #eee;
--ngui-ac-item-border: 1px solid rgba(255, 255, 255, 0.08);
--ngui-ac-hover-background: rgba(255, 255, 255, 0.08);
--ngui-ac-selected-background: rgba(255, 255, 255, 0.16);
}Angular Version Compatibility
This library follows Angular's versioning: @ngui/[email protected] supports Angular N.
Install the version matching your Angular major (e.g. Angular 22 → npm install @ngui/auto-complete@22).
Development
Quick start
git clone https://github.com/ng2-ui/auto-complete.git
cd auto-complete
npm install
# Build library in watch mode, then in a second terminal start the demo app
npm run build-lib:watch
npm startAvailable scripts
| Script | Description |
|--------|-------------|
| npm start | Serve the demo app on port 4200 |
| npm test | Run unit tests (Karma/Jasmine) |
| npm run lint | Lint all TypeScript and HTML |
| npm run build-lib:watch | Build library in watch mode (for demo development) |
| npm run build-lib:prod | Production library build |
| npm run build-docs | Build demo app for GitHub Pages deployment |
| npm run cypress:open | Open Cypress e2e test runner |
| npm run cypress:run | Run Cypress e2e tests headlessly |
Publish a new version
# 1. Update version in projects/auto-complete/package.json
# 2. Build the library. This also copies README/CHANGELOG/MIGRATION/LICENSE
# into dist/ for you (via the copy-lib script).
npm run build-lib:prod
# 3. Move into the built output — do NOT run npm publish from the project root
# (the root package.json is private and will fail)
cd dist
npm publish --access publicContributing — help wanted!
This library is maintained by a small team with limited time — every contribution genuinely helps keep it alive and improving. If you use it and find it useful, here are easy ways to give back:
- Found a bug? Open an issue with a clear description and a minimal reproduction
- Have an idea? Check the open issues first, then open a new one if needed
- Want to fix something? Pull requests are always welcome — small focused PRs are easiest to review
- Using it at work? A GitHub star goes a long way for visibility
Issues and pull requests: github.com/ng2-ui/auto-complete.
Changelog
See CHANGELOG.md for the full history of changes.
