Typeahead component on github

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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-basic',
  templateUrl: './basic.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-animated',
  templateUrl: './animated.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-adaptive-position',
  templateUrl: './adaptive-position.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-item-template',
  templateUrl: './item-template.html',
  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">
    <li class="custom-list-group-item"
        *ngFor="let match of matches"
        [class.active]="typeaheadTemplateMethods.isActive(match)"
        (click)="typeaheadTemplateMethods.selectMatch(match, $event)"
        (mouseenter)="typeaheadTemplateMethods.selectActive(match)">
      {{ match.item }}
    </li>
  </ul>
</ng-template>
import { Component } 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;
    }
  `],
  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 } 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',
  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 } 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',
  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">

<div class="alert alert-danger" role="alert" *ngIf="errorMessage">
  {{ errorMessage }}
</div>

import { Component, OnInit } 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',
  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">
<div *ngIf="typeaheadLoading">Loading</div>
import { Component } 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 }],
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-delay',
  templateUrl: './delay.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-form',
  templateUrl: './form.html',
  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 } 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',
  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 } 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',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-single-world',
  templateUrl: './single-world.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-phrase-delimiters',
  templateUrl: './phrase-delimiters.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-dropup',
  templateUrl: './dropup.html',
  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 } 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 } }],
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-container',
  templateUrl: './container.html',
  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>
<div class="alert alert-danger" *ngIf="noResult">No Results Found</div>

<input [(ngModel)]="selected"
       [typeahead]="states"
       (typeaheadNoResults)="typeaheadNoResults($event)"
       class="form-control">
import { Component } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-no-result',
  templateUrl: './no-result.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-scrollable',
  templateUrl: './scrollable.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-latinize',
  templateUrl: './latinize.html',
  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: 
Preview region: N/A
<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:
        <span *ngIf="previewOption; else noPreviewOption">{{previewOption?.region}}</span>
        <ng-template #noPreviewOption><span>N/A</span></ng-template>
    </div>
</div>
import { Component } 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',
  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>): 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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-min-length',
  templateUrl: './min-length.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-show-on-blur',
  templateUrl: './show-on-blur.html',
  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 } 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 }],
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-first-item-active',
  templateUrl: './first-item-active.html',
  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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-selected-first-item',
  templateUrl: './selected-first-item.html',
  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 } 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',
  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))
          );
        })
      );
  }
}

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 } from '@angular/core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-typeahead-multiple-search',
  templateUrl: './multiple-search.html',
  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(){}

Selector

TypeaheadConfig

Default values provider for typeahead

Properties

adaptivePosition
Type: boolean
Default value: false

sets use adaptive position

cancelRequestOnFocusLost
Type: boolean
Default value: false

if true, typeahead will cancel async request on blur

hideResultsOnBlur
Type: boolean
Default value: true

used to hide results on blur

isAnimated
Type: boolean
Default value: false

turn on/off animation

isFirstItemActive
Type: boolean
Default value: true

used to active/inactive the first item in typeahead container

minLength
Type: number
Default value: 1

used to choose set minimal no of characters that needs to be entered before typeahead kicks-in

selectFirstItem
Type: boolean
Default value: true

used to choose the first item in typeahead container

selectItemOnBlur
Type: boolean
Default value: false

used 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: TypeaheadTemplateMethods

Typeahead 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: number

Item index

item
Type: unknown

Item

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) => 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: 
Preview region: N/A

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: