The easiest way to add the typeahead component to your app (will be added to the root module)
Basic array
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-basic',
templateUrl: './basic.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadBasicComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
With animation
You can enable animation via isAnimated input or config option
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[isAnimated]="true"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-animated',
templateUrl: './animated.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadAnimatedComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Adaptive position
You can enable adaptive position via adaptivePosition input or config option
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[adaptivePosition]="true"
class="form-control">import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-adaptive-position',
templateUrl: './adaptive-position.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadAdaptivePositionComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Item template
Model:
<ng-template #customItemTemplate let-model="item" let-index="index">
<h5>This is: {{model | json}} Index: {{ index }}</h5>
</ng-template>
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadItemTemplate]="customItemTemplate"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-item-template',
templateUrl: './item-template.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadItemTemplateComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
List template
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[optionsListTemplate]="customListTemplate"
class="form-control">
<ng-template #customListTemplate let-matches="matches" let-query="query" let-typeaheadTemplateMethods>
<ul class="custom-list-group">
@for (match of matches; track match) {
<li class="custom-list-group-item"
[class.active]="typeaheadTemplateMethods.isActive(match)"
(click)="typeaheadTemplateMethods.selectMatch(match, $event)"
(mouseenter)="typeaheadTemplateMethods.selectActive(match)">
{{ match.item }}
</li>
}
</ul>
</ng-template>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-templates',
templateUrl: './list-template.html',
styles: [`
.custom-list-group {
display: flex;
flex-direction: column;
width: 300px;
padding-left: 0;
margin: 0;
list-style: none;
}
.custom-list-group-item {
position: relative;
display: block;
padding: .75rem 1.25rem;
background-color: #fff;
}
.custom-list-group-item.active {
z-index: 2;
color: #fff;
background-color: #FF4461;
border-color: #FF4461;
}
`],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadListTemplateComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Option field
Model:
<pre class="card card-block card-header mb-3">Model: {{customSelected | json}}</pre>
<input [(ngModel)]="customSelected"
[typeahead]="statesComplex"
typeaheadOptionField="name"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { DataSourceType } from '../interfaces/typeahead.interfaces';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-field',
templateUrl: './field.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadFieldComponent {
customSelected?: string;
statesComplex: DataSourceType[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{ id: 3, name: 'Arizona', region: 'West' },
{ id: 4, name: 'Arkansas', region: 'South' },
{ id: 5, name: 'California', region: 'West' },
{ id: 6, name: 'Colorado', region: 'West' },
{ id: 7, name: 'Connecticut', region: 'Northeast' },
{ id: 8, name: 'Delaware', region: 'South' },
{ id: 9, name: 'Florida', region: 'South' },
{ id: 10, name: 'Georgia', region: 'South' },
{ id: 11, name: 'Hawaii', region: 'West' },
{ id: 12, name: 'Idaho', region: 'West' },
{ id: 13, name: 'Illinois', region: 'Midwest' },
{ id: 14, name: 'Indiana', region: 'Midwest' },
{ id: 15, name: 'Iowa', region: 'Midwest' },
{ id: 16, name: 'Kansas', region: 'Midwest' },
{ id: 17, name: 'Kentucky', region: 'South' },
{ id: 18, name: 'Louisiana', region: 'South' },
{ id: 19, name: 'Maine', region: 'Northeast' },
{ id: 21, name: 'Maryland', region: 'South' },
{ id: 22, name: 'Massachusetts', region: 'Northeast' },
{ id: 23, name: 'Michigan', region: 'Midwest' },
{ id: 24, name: 'Minnesota', region: 'Midwest' },
{ id: 25, name: 'Mississippi', region: 'South' },
{ id: 26, name: 'Missouri', region: 'Midwest' },
{ id: 27, name: 'Montana', region: 'West' },
{ id: 28, name: 'Nebraska', region: 'Midwest' },
{ id: 29, name: 'Nevada', region: 'West' },
{ id: 30, name: 'New Hampshire', region: 'Northeast' },
{ id: 31, name: 'New Jersey', region: 'Northeast' },
{ id: 32, name: 'New Mexico', region: 'West' },
{ id: 33, name: 'New York', region: 'Northeast' },
{ id: 34, name: 'North Dakota', region: 'Midwest' },
{ id: 35, name: 'North Carolina', region: 'South' },
{ id: 36, name: 'Ohio', region: 'Midwest' },
{ id: 37, name: 'Oklahoma', region: 'South' },
{ id: 38, name: 'Oregon', region: 'West' },
{ id: 39, name: 'Pennsylvania', region: 'Northeast' },
{ id: 40, name: 'Rhode Island', region: 'Northeast' },
{ id: 41, name: 'South Carolina', region: 'South' },
{ id: 42, name: 'South Dakota', region: 'Midwest' },
{ id: 43, name: 'Tennessee', region: 'South' },
{ id: 44, name: 'Texas', region: 'South' },
{ id: 45, name: 'Utah', region: 'West' },
{ id: 46, name: 'Vermont', region: 'Northeast' },
{ id: 47, name: 'Virginia', region: 'South' },
{ id: 48, name: 'Washington', region: 'South' },
{ id: 49, name: 'West Virginia', region: 'South' },
{ id: 50, name: 'Wisconsin', region: 'Midwest' },
{ id: 51, name: 'Wyoming', region: 'West' }
];
}
Async data
Model:
<pre class="card card-block card-header">Model: {{ asyncSelected | json }}</pre>
<input [(ngModel)]="asyncSelected"
[typeahead]="dataSource"
[typeaheadAsync]="true"
typeaheadOptionField="name"
(typeaheadLoading)="changeTypeaheadLoading($event)"
placeholder="Locations loaded via observable"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Observable, of, Subscriber } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { DataSourceType } from '../interfaces/typeahead.interfaces';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-async',
templateUrl: './async.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadAsyncComponent {
asyncSelected?: string;
dataSource: Observable<DataSourceType[]>;
typeaheadLoading?: boolean;
statesComplex: DataSourceType[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{ id: 3, name: 'Arizona', region: 'West' },
{ id: 4, name: 'Arkansas', region: 'South' },
{ id: 5, name: 'California', region: 'West' },
{ id: 6, name: 'Colorado', region: 'West' },
{ id: 7, name: 'Connecticut', region: 'Northeast' },
{ id: 8, name: 'Delaware', region: 'South' },
{ id: 9, name: 'Florida', region: 'South' },
{ id: 10, name: 'Georgia', region: 'South' },
{ id: 11, name: 'Hawaii', region: 'West' },
{ id: 12, name: 'Idaho', region: 'West' },
{ id: 13, name: 'Illinois', region: 'Midwest' },
{ id: 14, name: 'Indiana', region: 'Midwest' },
{ id: 15, name: 'Iowa', region: 'Midwest' },
{ id: 16, name: 'Kansas', region: 'Midwest' },
{ id: 17, name: 'Kentucky', region: 'South' },
{ id: 18, name: 'Louisiana', region: 'South' },
{ id: 19, name: 'Maine', region: 'Northeast' },
{ id: 21, name: 'Maryland', region: 'South' },
{ id: 22, name: 'Massachusetts', region: 'Northeast' },
{ id: 23, name: 'Michigan', region: 'Midwest' },
{ id: 24, name: 'Minnesota', region: 'Midwest' },
{ id: 25, name: 'Mississippi', region: 'South' },
{ id: 26, name: 'Missouri', region: 'Midwest' },
{ id: 27, name: 'Montana', region: 'West' },
{ id: 28, name: 'Nebraska', region: 'Midwest' },
{ id: 29, name: 'Nevada', region: 'West' },
{ id: 30, name: 'New Hampshire', region: 'Northeast' },
{ id: 31, name: 'New Jersey', region: 'Northeast' },
{ id: 32, name: 'New Mexico', region: 'West' },
{ id: 33, name: 'New York', region: 'Northeast' },
{ id: 34, name: 'North Dakota', region: 'Midwest' },
{ id: 35, name: 'North Carolina', region: 'South' },
{ id: 36, name: 'Ohio', region: 'Midwest' },
{ id: 37, name: 'Oklahoma', region: 'South' },
{ id: 38, name: 'Oregon', region: 'West' },
{ id: 39, name: 'Pennsylvania', region: 'Northeast' },
{ id: 40, name: 'Rhode Island', region: 'Northeast' },
{ id: 41, name: 'South Carolina', region: 'South' },
{ id: 42, name: 'South Dakota', region: 'Midwest' },
{ id: 43, name: 'Tennessee', region: 'South' },
{ id: 44, name: 'Texas', region: 'South' },
{ id: 45, name: 'Utah', region: 'West' },
{ id: 46, name: 'Vermont', region: 'Northeast' },
{ id: 47, name: 'Virginia', region: 'South' },
{ id: 48, name: 'Washington', region: 'South' },
{ id: 49, name: 'West Virginia', region: 'South' },
{ id: 50, name: 'Wisconsin', region: 'Midwest' },
{ id: 51, name: 'Wyoming', region: 'West' }
];
constructor() {
this.dataSource = new Observable((observer: Subscriber<string>) => {
// Runs on every search
observer.next(this.asyncSelected);
})
.pipe(
mergeMap((token: string) => this.getStatesAsObservable(token))
);
}
getStatesAsObservable(token: string): Observable<DataSourceType[]> {
const query = new RegExp(token, 'i');
return of(
this.statesComplex.filter((state: DataSourceType) => {
return query.test(state.name);
})
);
}
changeTypeaheadLoading(e: boolean): void {
this.typeaheadLoading = e;
}
}
Async using http request
Use http request to search for data. If you need to handle http error, do this inside tap operator.
Enter search value several times (10-15), and after a few success responses API should return an error
(GitHub limit for requests)
Model:
<pre class="card card-block card-header">Model: {{ search | json }}</pre>
<input [(ngModel)]="search"
typeaheadOptionField="login"
[typeahead]="suggestions$"
[typeaheadAsync]="true"
class="form-control"
placeholder="Enter GitHub username">
@if (errorMessage) {
<div class="alert alert-danger" role="alert">
{{ errorMessage }}
</div>
}
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { noop, Observable, Observer, of } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
interface GitHubUserSearchResponse {
total_count: number;
incomplete_results: boolean;
items: GitHubUser[];
}
interface GitHubUser {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
received_events_url: string;
type: string;
score: number;
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-async-http',
templateUrl: './async-http-request.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadAsyncHttpRequestComponent implements OnInit {
search?: string;
suggestions$?: Observable<GitHubUser[]>;
errorMessage?: string;
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.suggestions$ = new Observable((observer: Observer<string | undefined>) => {
observer.next(this.search);
}).pipe(
switchMap((query: string) => {
if (query) {
// using github public api to get users by name
return this.http.get<GitHubUserSearchResponse>(
'https://api.github.com/search/users', {
params: { q: query }
}).pipe(
map((data: GitHubUserSearchResponse) => data && data.items || []),
tap(() => noop, err => {
// in case of http error
this.errorMessage = err && err.message || 'Something goes wrong';
})
);
}
return of([]);
})
);
}
}
Cancel on focus lost
Set config property cancelRequestOnFocusLost to true if you want to cancel async request on focus lost event
Model:
<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>
<input [(ngModel)]="asyncSelected"
[typeahead]="dataSource"
(typeaheadLoading)="changeTypeaheadLoading($event)"
(typeaheadOnSelect)="typeaheadOnSelect($event)"
[typeaheadOptionsLimit]="7"
typeaheadOptionField="name"
placeholder="Locations loaded with timeout"
class="form-control">
@if (typeaheadLoading) {
<div>Loading</div>
}
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Observable, Observer, of } from 'rxjs';
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
import { mergeMap, delay } from 'rxjs/operators';
import { TypeaheadConfig } from 'ngx-bootstrap/typeahead';
import { DataSourceType } from '../interfaces/typeahead.interfaces';
export function getTypeaheadConfig(): TypeaheadConfig {
return Object.assign(new TypeaheadConfig(), { cancelRequestOnFocusLost: true });
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-cancel-on-focus-lost',
templateUrl: './cancel-on-focus-lost.html',
providers: [{ provide: TypeaheadConfig, useFactory: getTypeaheadConfig }],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadCancelRequestOnFocusLostComponent {
asyncSelected?: string;
typeaheadLoading?: boolean;
dataSource: Observable<DataSourceType[]>;
statesComplex: DataSourceType[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{ id: 3, name: 'Arizona', region: 'West' },
{ id: 4, name: 'Arkansas', region: 'South' },
{ id: 5, name: 'California', region: 'West' },
{ id: 6, name: 'Colorado', region: 'West' },
{ id: 7, name: 'Connecticut', region: 'Northeast' },
{ id: 8, name: 'Delaware', region: 'South' },
{ id: 9, name: 'Florida', region: 'South' },
{ id: 10, name: 'Georgia', region: 'South' },
{ id: 11, name: 'Hawaii', region: 'West' },
{ id: 12, name: 'Idaho', region: 'West' },
{ id: 13, name: 'Illinois', region: 'Midwest' },
{ id: 14, name: 'Indiana', region: 'Midwest' },
{ id: 15, name: 'Iowa', region: 'Midwest' },
{ id: 16, name: 'Kansas', region: 'Midwest' },
{ id: 17, name: 'Kentucky', region: 'South' },
{ id: 18, name: 'Louisiana', region: 'South' },
{ id: 19, name: 'Maine', region: 'Northeast' },
{ id: 21, name: 'Maryland', region: 'South' },
{ id: 22, name: 'Massachusetts', region: 'Northeast' },
{ id: 23, name: 'Michigan', region: 'Midwest' },
{ id: 24, name: 'Minnesota', region: 'Midwest' },
{ id: 25, name: 'Mississippi', region: 'South' },
{ id: 26, name: 'Missouri', region: 'Midwest' },
{ id: 27, name: 'Montana', region: 'West' },
{ id: 28, name: 'Nebraska', region: 'Midwest' },
{ id: 29, name: 'Nevada', region: 'West' },
{ id: 30, name: 'New Hampshire', region: 'Northeast' },
{ id: 31, name: 'New Jersey', region: 'Northeast' },
{ id: 32, name: 'New Mexico', region: 'West' },
{ id: 33, name: 'New York', region: 'Northeast' },
{ id: 34, name: 'North Dakota', region: 'Midwest' },
{ id: 35, name: 'North Carolina', region: 'South' },
{ id: 36, name: 'Ohio', region: 'Midwest' },
{ id: 37, name: 'Oklahoma', region: 'South' },
{ id: 38, name: 'Oregon', region: 'West' },
{ id: 39, name: 'Pennsylvania', region: 'Northeast' },
{ id: 40, name: 'Rhode Island', region: 'Northeast' },
{ id: 41, name: 'South Carolina', region: 'South' },
{ id: 42, name: 'South Dakota', region: 'Midwest' },
{ id: 43, name: 'Tennessee', region: 'South' },
{ id: 44, name: 'Texas', region: 'South' },
{ id: 45, name: 'Utah', region: 'West' },
{ id: 46, name: 'Vermont', region: 'Northeast' },
{ id: 47, name: 'Virginia', region: 'South' },
{ id: 48, name: 'Washington', region: 'South' },
{ id: 49, name: 'West Virginia', region: 'South' },
{ id: 50, name: 'Wisconsin', region: 'Midwest' },
{ id: 51, name: 'Wyoming', region: 'West' }
];
constructor() {
this.dataSource = new Observable((observer: Observer<string | undefined>) => {
// Runs on every search
observer.next(this.asyncSelected);
}).pipe(
mergeMap((token: string) => this.getStatesAsObservable(token)),
delay(1000)
);
}
getStatesAsObservable(token: string): Observable<DataSourceType[]> {
const query = new RegExp(token, 'i');
return of(
this.statesComplex.filter((state: DataSourceType) => {
return query.test(state.name);
})
);
}
changeTypeaheadLoading(e: boolean): void {
this.typeaheadLoading = e;
}
typeaheadOnSelect(e: TypeaheadMatch): void {
console.log('Selected value: ', e.value);
}
}
With delay
Use typeaheadWaitMs to set minimal waiting time after last character typed
before typeahead kicks-in. In example a search begins with delay in 1 second
Model:
<pre class="card card-block card-header">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadWaitMs]="1000"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-delay',
templateUrl: './delay.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadDelayComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Template-driven forms
Typeahead can be used in template-driven forms. Keep in mind that value of ngModel is
string
Model: {
"address": "312 Sundown Lane",
"state": null
}<pre class="card card-block card-header">Model: {{model | json}}</pre>
<form>
<div class="form-group mb-3">
<label for="address">Address</label>
<input type="text" class="form-control" id="address" required
[(ngModel)]="model.address" name="address">
</div>
<div class="form-group mb-3">
<label for="state">State</label>
<input id="state" class="form-control" name="state"
[(ngModel)]="model.state" [typeahead]="states">
</div>
</form>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-form',
templateUrl: './form.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadFormComponent {
model = {
address: '312 Sundown Lane',
state: null
};
states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Reactive forms
Typeahead can be used in reactive forms
Model: null
<pre class="card card-block card-header">Model: {{myForm.value.state | json}}</pre>
<form [formGroup]="myForm">
<input formControlName="state"
[typeahead]="states"
[typeaheadOptionsLimit]="7"
[typeaheadMinLength]="0"
placeholder="Typeahead inside a form"
class="form-control">
</form>
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-reactive-form',
templateUrl: './reactive-form.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadReactiveFormComponent {
stateCtrl = new UntypedFormControl();
myForm = new UntypedFormGroup({
state: this.stateCtrl
});
states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Grouping results
Model:
<pre class="card card-block card-header mb-3">Model: {{groupSelected | json}}</pre>
<input [(ngModel)]="groupSelected"
[typeahead]="statesComplex"
typeaheadOptionField="name"
typeaheadGroupField="region"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { DataSourceType } from '../interfaces/typeahead.interfaces';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-grouping',
templateUrl: './grouping.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadGroupingComponent {
groupSelected?: string;
statesComplex: DataSourceType[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{ id: 3, name: 'Arizona', region: 'West' },
{ id: 4, name: 'Arkansas', region: 'South' },
{ id: 5, name: 'California', region: 'West' },
{ id: 6, name: 'Colorado', region: 'West' },
{ id: 7, name: 'Connecticut', region: 'Northeast' },
{ id: 8, name: 'Delaware', region: 'South' },
{ id: 9, name: 'Florida', region: 'South' },
{ id: 10, name: 'Georgia', region: 'South' },
{ id: 11, name: 'Hawaii', region: 'West' },
{ id: 12, name: 'Idaho', region: 'West' },
{ id: 13, name: 'Illinois', region: 'Midwest' },
{ id: 14, name: 'Indiana', region: 'Midwest' },
{ id: 15, name: 'Iowa', region: 'Midwest' },
{ id: 16, name: 'Kansas', region: 'Midwest' },
{ id: 17, name: 'Kentucky', region: 'South' },
{ id: 18, name: 'Louisiana', region: 'South' },
{ id: 19, name: 'Maine', region: 'Northeast' },
{ id: 21, name: 'Maryland', region: 'South' },
{ id: 22, name: 'Massachusetts', region: 'Northeast' },
{ id: 23, name: 'Michigan', region: 'Midwest' },
{ id: 24, name: 'Minnesota', region: 'Midwest' },
{ id: 25, name: 'Mississippi', region: 'South' },
{ id: 26, name: 'Missouri', region: 'Midwest' },
{ id: 27, name: 'Montana', region: 'West' },
{ id: 28, name: 'Nebraska', region: 'Midwest' },
{ id: 29, name: 'Nevada', region: 'West' },
{ id: 30, name: 'New Hampshire', region: 'Northeast' },
{ id: 31, name: 'New Jersey', region: 'Northeast' },
{ id: 32, name: 'New Mexico', region: 'West' },
{ id: 33, name: 'New York', region: 'Northeast' },
{ id: 34, name: 'North Dakota', region: 'Midwest' },
{ id: 35, name: 'North Carolina', region: 'South' },
{ id: 36, name: 'Ohio', region: 'Midwest' },
{ id: 37, name: 'Oklahoma', region: 'South' },
{ id: 38, name: 'Oregon', region: 'West' },
{ id: 39, name: 'Pennsylvania', region: 'Northeast' },
{ id: 40, name: 'Rhode Island', region: 'Northeast' },
{ id: 41, name: 'South Carolina', region: 'South' },
{ id: 42, name: 'South Dakota', region: 'Midwest' },
{ id: 43, name: 'Tennessee', region: 'South' },
{ id: 44, name: 'Texas', region: 'South' },
{ id: 45, name: 'Utah', region: 'West' },
{ id: 46, name: 'Vermont', region: 'Northeast' },
{ id: 47, name: 'Virginia', region: 'South' },
{ id: 48, name: 'Washington', region: 'South' },
{ id: 49, name: 'West Virginia', region: 'South' },
{ id: 50, name: 'Wisconsin', region: 'Midwest' },
{ id: 51, name: 'Wyoming', region: 'West' }
];
}
Ignore spaces and order
After setting typeaheadSingleWords input property to true
order of typed symbols and spaces between them will be ignored. For example, "zona ari"
will match with "Arizona"
typeaheadSingleWords: true Model:
<button type="button" class="btn btn-primary"
(click)="typeaheadSingleWords = !typeaheadSingleWords">Toggle typeaheadSingleWords
</button>
<pre class="card card-block card-header">
typeaheadSingleWords: {{typeaheadSingleWords}}
Model: {{selected | json}}
</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadSingleWords]="typeaheadSingleWords"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-single-world',
templateUrl: './single-world.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadSingleWorldComponent {
typeaheadSingleWords = true;
selected?: string;
states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Phrase delimiters
Set the word delimiter by typeaheadPhraseDelimiters to match exact phrase.
This is demo with delimeters "&" and ","
Model:
<pre class="card card-block card-header">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadSingleWords]="true"
typeaheadPhraseDelimiters="&,"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-phrase-delimiters',
templateUrl: './phrase-delimiters.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadPhraseDelimitersComponent {
selected?: string;
states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Dropup
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[dropup]="true"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-dropup',
templateUrl: './dropup.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadDropupComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
On blur
Returns an option on which user lost a focus. To reproduce start typing the name of the state, then focus on one of the options with mouse or arrow keys and click outside of the typeahead
Model:
Option on blur:
<pre class="card card-block card-header">Model: {{selected | json}}</pre>
<pre class="card card-block card-header">Option on blur: {{optionOnBlur | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
(typeaheadOnBlur)="typeaheadOnBlur($event)"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { TypeaheadMatch, TypeaheadConfig } from 'ngx-bootstrap/typeahead';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-on-blur',
templateUrl: './on-blur.html',
providers: [{ provide: TypeaheadConfig, useValue: { selectItemOnBlur: true, hideResultsOnBlur: true } }],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadOnBlurComponent {
selected?: string;
optionOnBlur?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
typeaheadOnBlur(event?: TypeaheadMatch<string>): void {
this.optionOnBlur = event?.item;
}
}
Append to body
container is an input property specifying the element the typeahead should be appended to.
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
container="body"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-container',
templateUrl: './container.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadContainerComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
No result
Used to display the state when no matches were found. To see message "No Results Found" enter the value that doesn't match anything from the list
Model:
<pre class="card card-block card-header">Model: {{selected | json}}</pre>
@if (noResult) {
<div class="alert alert-danger">No Results Found</div>
}
<input [(ngModel)]="selected"
[typeahead]="states"
(typeaheadNoResults)="typeaheadNoResults($event)"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-no-result',
templateUrl: './no-result.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadNoResultComponent {
selected?: string;
noResult = false;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
typeaheadNoResults(event: boolean): void {
this.noResult = event;
}
}
Scrollable
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadScrollable]="true"
[typeaheadOptionsInScrollableView]="5"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-scrollable',
templateUrl: './scrollable.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadScrollableComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Latinize
Use typeaheadLatinize property for matching latin symbols. If it is set
to true the word súper would match super and vice versa.
Model:
<pre class="card card-block card-header">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="frenchWords"
[typeaheadLatinize]="true"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-latinize',
templateUrl: './latinize.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadLatinizeComponent {
selected?: string;
frenchWords: string[] = [
'popularisé',
'français',
'intéressé',
'générateur',
'répandue',
'répétition',
'súper'
];
}
On select / On preview
typeaheadOnSelect event is fired when an option was selected.
Returns an object with this option.
typeaheadOnPreview event is fired when an option was highlighted.
Returns an object with this option.
Model:
Selected option:
<pre class="card card-block card-header mb-3">Model: {{selectedValue | json}}</pre>
<pre class="card card-block card-header mb-3">Selected option: {{selectedOption | json}}</pre>
<div>
<input [(ngModel)]="selectedValue"
[typeahead]="states"
typeaheadOptionField="name"
(typeaheadOnSelect)="onSelect($event)"
(typeaheadOnPreview)="onPreview($event)"
class="form-control">
<div style="float:right;width:160px;"
class="card card-block card-header mb-3">
Preview region:
@if (previewOption) {
<span>{{previewOption?.region}}</span>
} @else {
<span>N/A</span>
}
</div>
</div>
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
import { DataSourceType } from '../interfaces/typeahead.interfaces';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-on-select',
templateUrl: './on-select.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadOnSelectComponent {
selectedValue?: string;
selectedOption?: DataSourceType;
previewOption?: DataSourceType;
states: DataSourceType[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{ id: 3, name: 'Arizona', region: 'West' },
{ id: 4, name: 'Arkansas', region: 'South' },
{ id: 5, name: 'California', region: 'West' },
{ id: 6, name: 'Colorado', region: 'West' },
{ id: 7, name: 'Connecticut', region: 'Northeast' },
{ id: 8, name: 'Delaware', region: 'South' },
{ id: 9, name: 'Florida', region: 'South' },
{ id: 10, name: 'Georgia', region: 'South' },
{ id: 11, name: 'Hawaii', region: 'West' },
{ id: 12, name: 'Idaho', region: 'West' },
{ id: 13, name: 'Illinois', region: 'Midwest' },
{ id: 14, name: 'Indiana', region: 'Midwest' },
{ id: 15, name: 'Iowa', region: 'Midwest' },
{ id: 16, name: 'Kansas', region: 'Midwest' },
{ id: 17, name: 'Kentucky', region: 'South' },
{ id: 18, name: 'Louisiana', region: 'South' },
{ id: 19, name: 'Maine', region: 'Northeast' },
{ id: 21, name: 'Maryland', region: 'South' },
{ id: 22, name: 'Massachusetts', region: 'Northeast' },
{ id: 23, name: 'Michigan', region: 'Midwest' },
{ id: 24, name: 'Minnesota', region: 'Midwest' },
{ id: 25, name: 'Mississippi', region: 'South' },
{ id: 26, name: 'Missouri', region: 'Midwest' },
{ id: 27, name: 'Montana', region: 'West' },
{ id: 28, name: 'Nebraska', region: 'Midwest' },
{ id: 29, name: 'Nevada', region: 'West' },
{ id: 30, name: 'New Hampshire', region: 'Northeast' },
{ id: 31, name: 'New Jersey', region: 'Northeast' },
{ id: 32, name: 'New Mexico', region: 'West' },
{ id: 33, name: 'New York', region: 'Northeast' },
{ id: 34, name: 'North Dakota', region: 'Midwest' },
{ id: 35, name: 'North Carolina', region: 'South' },
{ id: 36, name: 'Ohio', region: 'Midwest' },
{ id: 37, name: 'Oklahoma', region: 'South' },
{ id: 38, name: 'Oregon', region: 'West' },
{ id: 39, name: 'Pennsylvania', region: 'Northeast' },
{ id: 40, name: 'Rhode Island', region: 'Northeast' },
{ id: 41, name: 'South Carolina', region: 'South' },
{ id: 42, name: 'South Dakota', region: 'Midwest' },
{ id: 43, name: 'Tennessee', region: 'South' },
{ id: 44, name: 'Texas', region: 'South' },
{ id: 45, name: 'Utah', region: 'West' },
{ id: 46, name: 'Vermont', region: 'Northeast' },
{ id: 47, name: 'Virginia', region: 'South' },
{ id: 48, name: 'Washington', region: 'South' },
{ id: 49, name: 'West Virginia', region: 'South' },
{ id: 50, name: 'Wisconsin', region: 'Midwest' },
{ id: 51, name: 'Wyoming', region: 'West' }
];
onSelect(event: TypeaheadMatch<DataSourceType>): void {
this.selectedOption = event.item;
}
onPreview(event: TypeaheadMatch<DataSourceType> | undefined): void {
if (event) {
this.previewOption = event.item;
} else {
this.previewOption = undefined;
}
}
}
Min length
Minimal number of characters that needs to be entered before typeahead kicks in. When set to 0, typeahead shows on focus with full list of options.
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadMinLength]="0"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-min-length',
templateUrl: './min-length.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadMinLengthComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Show results on blur
Use input property typeaheadHideResultsOnBlur or config property hideResultsOnBlur
to prevent hiding typeahead's results until a user doesn't choose an item
typeaheadHideResultsOnBlur: false Model:
<button type="button" class="btn btn-primary mb-3"
(click)="typeaheadHideResultsOnBlur = !typeaheadHideResultsOnBlur">Toggle typeaheadHideResultsOnBlur
</button>
<pre class="card card-block card-header mb-3">
typeaheadHideResultsOnBlur: {{typeaheadHideResultsOnBlur}}
Model: {{selected | json}}
</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadHideResultsOnBlur]="typeaheadHideResultsOnBlur"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-show-on-blur',
templateUrl: './show-on-blur.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadShowOnBlurComponent {
typeaheadHideResultsOnBlur = false;
selected?: string;
states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Configuring defaults
<input [(ngModel)]="selected"
[typeahead]="states"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { TypeaheadConfig } from 'ngx-bootstrap/typeahead';
// such override allows to keep some initial values
export function getTypeaheadConfig(): TypeaheadConfig {
return Object.assign(new TypeaheadConfig(), { hideResultsOnBlur: false });
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-config',
templateUrl: './config.html',
providers: [{ provide: TypeaheadConfig, useFactory: getTypeaheadConfig }],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadConfigComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Is first item active
Use input property typeaheadIsFirstItemActive or config property isFirstItemActive to make the first item active/inactive
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadIsFirstItemActive]="false"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-first-item-active',
templateUrl: './first-item-active.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadFirstItemActiveComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Selected first item
Use typeaheadSelectFirstItem property to make the first item in options list unselectable by tab and enter.
Model:
<pre class="card card-block card-header mb-3">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadSelectFirstItem]="false"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-selected-first-item',
templateUrl: './selected-first-item.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemotypeaheadSelectFirstItemComponent {
selected?: string;
states: string[] = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Order results
Use typeaheadOrderBy property to order your result by a certain field and in certain direction
Source - array of string. Order direction - descending
Source - array of string. Order direction - ascending
Source - array of objects. Order direction - ascending, sort by city, group by state
Source - Observable of array of string. Order direction - descending
<div class="mb-3">
<h6>Source - <strong>array of string</strong>. Order direction - <strong>descending</strong></h6>
<input [(ngModel)]="selected1"
[typeahead]="states"
[typeaheadOrderBy]="sortConfig1"
class="form-control">
</div>
<div class="mb-3">
<h6>Source - <strong>array of string</strong>. Order direction - <strong>ascending</strong></h6>
<input [(ngModel)]="selected2"
[typeahead]="states"
[typeaheadOrderBy]="sortConfig2"
class="form-control">
</div>
<div class="mb-3">
<h6>
Source - <strong>array of objects</strong>. Order direction - <strong>ascending</strong>,
sort by <strong>city</strong>, group by <strong>state</strong>
</h6>
<input [(ngModel)]="selected3"
[typeahead]="cities"
typeaheadOptionField="city"
typeaheadGroupField="state"
[typeaheadItemTemplate]="customItemTemplate"
[typeaheadOrderBy]="sortConfig3"
class="form-control">
<ng-template #customItemTemplate let-model="item">
<span><strong>{{model.city}}</strong> - {{model.code}}</span>
</ng-template>
</div>
<div class="mb-3">
<h6>Source - <strong>Observable of array of string</strong>. Order direction - <strong>descending</strong></h6>
<input [(ngModel)]="selected4"
[typeahead]="states$"
[typeaheadAsync]="true"
[typeaheadOrderBy]="sortConfig1"
class="form-control">
</div>
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { TypeaheadOrder } from 'ngx-bootstrap/typeahead';
import { Observable, of, Subscriber } from 'rxjs';
import { switchMap } from 'rxjs/operators';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-ordering',
templateUrl: './ordering.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadOrderingComponent implements OnInit {
selected1?: string;
selected2?: string;
selected3?: string;
selected4?: string;
sortConfig1: TypeaheadOrder = {
direction: 'desc'
};
sortConfig2: TypeaheadOrder = {
direction: 'asc'
};
sortConfig3: TypeaheadOrder = {
direction: 'asc',
field: 'city'
};
states$?: Observable<string[]>;
states: string[] = [
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Alaska',
'Alabama',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
cities = [{
city: 'Norton',
state: 'Virginia',
code: '61523'
}, {
city: 'Grundy',
state: 'Virginia',
code: '77054'
}, {
city: 'Coeburn',
state: 'Virginia',
code: '01665'
}, {
city: 'Phoenix',
state: 'Arizona',
code: '29128'
}, {
city: 'Tucson',
state: 'Arizona',
code: '32084'
}, {
city: 'Mesa',
state: 'Arizona',
code: '21465'
}, {
city: 'Independence',
state: 'Missouri',
code: '26887'
}, {
city: 'Kansas City',
state: 'Missouri',
code: '79286'
}, {
city: 'Springfield',
state: 'Missouri',
code: '92325'
}, {
city: 'St. Louis',
state: 'Missouri',
code: '64891'
}];
ngOnInit(): void {
this.states$ = new Observable((observer: Subscriber<string>) => {
// Runs on every search
observer.next(this.selected4);
})
.pipe(
switchMap((token: string) => {
const query = new RegExp(token, 'i');
return of(
this.states.filter((state: string) => query.test(state))
);
})
);
}
}
Multiple search
Set typeaheadMultipleSearch input property to true
and provide the multiple search delimiter by typeaheadMultipleSearchDelimiters
to be able to search typeahead again after using one of the provided delimiters. Default delimiter
is "," if typeaheadMultipleSearchDelimiters is not used.
After picking a first value from typeahead
dropdown, type "," or "|" and then next value can be searched.
This is demo with delimeters "," and "|"
Model:
<pre class="card card-block card-header">Model: {{selected | json}}</pre>
<input [(ngModel)]="selected"
[typeahead]="states"
[typeaheadMultipleSearch]="true"
typeaheadMultipleSearchDelimiters=",|"
class="form-control">
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-typeahead-multiple-search',
templateUrl: './multiple-search.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTypeaheadMultipleSearchComponent {
selected?: string;
states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Dakota',
'North Carolina',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
];
}
Installation
ng add ngx-bootstrap --component typeahead
### Standalone component usage
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TypeaheadModule } from 'ngx-bootstrap/typeahead';
@Component({
standalone: true,
imports: [
BrowserAnimationsModule,
TypeaheadModule,
...
]
})
export class AppComponent(){}
### Module usage
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TypeaheadModule } from 'ngx-bootstrap/typeahead';
@NgModule({
imports: [
BrowserAnimationsModule,
TypeaheadModule,
...
]
})
export class AppModule(){}
TypeaheadConfig
Default values provider for typeahead
Properties
adaptivePosition | Type: booleanDefault value: falsesets use adaptive position |
cancelRequestOnFocusLost | Type: booleanDefault value: falseif true, typeahead will cancel async request on blur |
hideResultsOnBlur | Type: booleanDefault value: trueused to hide results on blur |
isAnimated | Type: booleanDefault value: falseturn on/off animation |
isFirstItemActive | Type: booleanDefault value: trueused to active/inactive the first item in typeahead container |
minLength | Type: numberDefault value: 1used to choose set minimal no of characters that needs to be entered before typeahead kicks-in |
selectFirstItem | Type: booleanDefault value: trueused to choose the first item in typeahead container |
selectItemOnBlur | Type: booleanDefault value: falseused to choose item on blur event |
TypeaheadOptionListContext
A context for the optionsListTemplate
input template in case you want to override default one
Properties
$implicit | Type: TypeaheadTemplateMethodsTypeahead template methods |
itemTemplate | Type: TemplateRef<TypeaheadOptionItemContext>Item template |
matches | Type: TypeaheadMatch<any>[]All matches |
query | Type: string | string[]Search query |
TypeaheadOptionItemContext
A context for the typeaheadItemTemplate
input template in case you want to override default one
Properties
index | Type: numberItem index |
item | Type: unknownItem |
match | Type: TypeaheadMatch<any>Typeahead match |
query | Type: string | string[]Search query |
TypeaheadTemplateMethods
Methods for optionsListTemplate context
Methods
selectMatch | selectMatch( value: TypeaheadMatch<any> , e: Event | undefined) => void Function to select an option by click event |
selectActive | selectActive( value: TypeaheadMatch<any>) => void Function to select an option by mouseenter event |
isActive | isActive( value: TypeaheadMatch<any>) => boolean Function to check if an option is active |
Basic array
Model:
With animation
Model:
Adaptive position
Model:
Item template
Model:
List template
Model:
Option field
Model:
Async data
Model:
Async using http request
Model:
Cancel on focus lost
Model:
With delay
Model:
Template-driven forms
Model: {
"address": "312 Sundown Lane",
"state": null
}Reactive forms
Model: null
Grouping results
Model:
Ignore spaces and order
typeaheadSingleWords: true Model:
Phrase delimiters
Model:
Dropup
Model:
On blur
Model:
Option on blur:
Append to body
Model:
No result
Model:
Scrollable
Model:
Latinize
Model:
On select / On preview
Model:
Selected option:
Min length
Model:
Show results on blur
typeaheadHideResultsOnBlur: false Model:
Configuring defaults
Is first item active
Model:
Selected first item
Model:
Order results
Source - array of string. Order direction - descending
Source - array of string. Order direction - ascending
Source - array of objects. Order direction - ascending, sort by city, group by state
Source - Observable of array of string. Order direction - descending
Multiple search
Model:
- Basic array
- With animation
- Adaptive position
- Item template
- List template
- Option field
- Async data
- Async using http request
- Cancel on focus lost
- With delay
- Template-driven forms
- Reactive forms
- Grouping results
- Ignore spaces and order
- Phrase delimiters
- Dropup
- On blur
- Append to body
- No result
- Scrollable
- Latinize
- On select / On preview
- Min length
- Show results on blur
- Configuring defaults
- Is first item active
- Selected first item
- Order results
- Multiple search
