Profile
Back to NewsBack
GitHub Trending 10 min
Reader Mode
ng2-ui/auto-complete: Angular Auto Complete component and directive

ng2-ui/auto-complete: Angular Auto Complete component and directive

14 hours ago

@ngui/auto-complete

CI</a> npm version</a> npm downloads</a> Angular</a> types</a> license</a>

A versatile Angular autocomplete library that works as both a directive (attached to any input) and a standalone component.

Live Demo


Installation

npm install @ngui/auto-complete @angular/cdk

The 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-formatter or 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, but NguiAutoCompleteModule is 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 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 (), 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) | — | 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 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 set dir="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 is generic. Bind a typed [source] (a typed array or a function returning Observable) and Angular infers the item type — [(value)], (valueSelected) (NguiAutoCompleteSelection) 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.


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 ; 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
pnpm install

Build library in watch mode, then in a second terminal start the demo app

pnpm run build-lib:watch pnpm start
This repo uses pnpm. Enable it with corepack enable (ships with Node).

Available scripts

| Script | Description | |--------|-------------| | pnpm start | Serve the demo app on port 4200 | | pnpm test | Run unit tests (Vitest + jsdom) | | pnpm run lint | Lint all TypeScript and HTML | | pnpm run build-lib:watch | Build library in watch mode (for demo development) | | pnpm run build-lib:prod | Production library build | | pnpm run build-docs | Build demo app for GitHub Pages deployment | | pnpm run cypress:open | Open Cypress e2e test runner | | pnpm 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).

pnpm 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 public

Contributing — 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.

License

MIT

Chat with me