Autocomplete

Angular Bootstrap 5 Autocomplete component

Autocomplete component predicts the words being typed based on the first few letters given by the user. You can search the list using basic scroll and the keyboard arrows.

Note: Read the API tab to find all available options and advanced customization


Basic example

        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete #autocomplete="mdbAutocomplete">
              <mdb-option *ngFor="let option of results | async" [value]="option">
                {{ option }}
              </mdb-option>
              <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
            </mdb-autocomplete>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { Observable, Subject } from 'rxjs';
            import { map, startWith, tap } from 'rxjs/operators';

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              searchText = new Subject<string>();
              results: Observable<string[]>;
              notFound = false;
              data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];

              constructor() {
                this.results = this.searchText.pipe(
                  startWith(''),
                  map((value: string) => this.filter(value)),
                  tap((results: string[]) =>
                    results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                  )
                );
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();
                return this.data.filter((item: string) =>
                  item.toLowerCase().includes(filterValue)
                );
              }
            }
          
        
    

Display value

The displayValue option allow to separate original result value from the value that will be displayed in the result list or input (after selection). Its useful when the data returned by the filter method is an array of objects. You can specify which parameter of the object should be displayed.

        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete #autocomplete="mdbAutocomplete" [displayValue]="displayValue">
              <mdb-option *ngFor="let option of results | async" [value]="option">
                {{ option.title }}
              </mdb-option>
              <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
            </mdb-autocomplete>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { Observable, Subject } from 'rxjs';
            import {
              map,
              startWith,
              tap,
            } from 'rxjs/operators';

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              searchText = new Subject<string>();
              results: Observable<any[]>;
              notFound = false;
              data: any[] = [
                { title: 'One', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit' },
                { title: 'Two', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit' },
                { title: 'Three', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit' },
                { title: 'Four', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit' },
                { title: 'Five', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit' },
              ];

              constructor() {
                this.results = this.searchText.pipe(
                  startWith(''),
                  map((value: any) => {
                    const title = typeof value === 'string' ? value : value.title;
                    return this.filter(title)
                  }),
                  tap((results: string[]) =>
                    results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                  )
                );
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();
                return this.data.filter((item: any) => item.title.toLowerCase().includes(filterValue));
              }

              displayValue(value: any): string {
                return value ? value.title : '';
              }
            }
          
        
    


Threshold

Use threshold option to specify a minimum number of the characters in the input field needed to perform a search operation.

        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete #autocomplete="mdbAutocomplete">
              <mdb-option *ngFor="let option of results | async" [value]="option">
                {{ option }}
              </mdb-option>
              <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
            </mdb-autocomplete>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { Observable, of, Subject } from 'rxjs';
            import {
              map,
              mergeMap
            } from 'rxjs/operators';
            import { combineLatest } from 'rxjs'

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              searchText = new Subject<string>();
              results: Observable<string[]>;
              notFound = false;

              data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];

              constructor() {
                this.results = this.searchText.pipe(
                  mergeMap((value: string) => {
                    const results = this.filter(value);
                      return combineLatest([of(value), of(results)]);
                  }),
                  map(([value, results]) => {
                    if (value.length > 2 && results.length === 0) {
                      this.notFound = true;
                      return results;
                    } else {
                      this.notFound = false;
                    }

                    return results;
                  })
                );
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();

                if (filterValue.length >= 2) {
                  return this.data.filter((item: string) => item.toLowerCase().includes(filterValue));
                } else {
                  return [];
                }
              }
            }
          
        
    

Custom item template

It is possible to customize the appearance of the autocomplete option. You can use the listHeight option to modify the result list height when you want to display more content in the component dropdown.

        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete
              #autocomplete="mdbAutocomplete"
              [displayValue]="displayValue"
              [listHeight]="290"
              [optionHeight]="58">
                <mdb-option *ngFor="let option of results | async" [value]="option">
                  <div class="autocomplete-custom-item-content">
                    <div class="autocomplete-custom-item-title">{{ option.title }}</div>
                    <div class="autocomplete-custom-item-subtitle">{{ option.subtitle }}</div>
                  </div>
                </mdb-option>
                <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
            </mdb-autocomplete>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { Observable, Subject } from 'rxjs';
            import {
              map,
              startWith,
              tap,
            } from 'rxjs/operators';

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              searchText = new Subject<string>();
              results: Observable<any[]>;
              notFound = false;
              data: any[] = [
                { title: 'One', subtitle: 'Secondary text' },
                { title: 'Two', subtitle: 'Secondary text' },
                { title: 'Three', subtitle: 'Secondary text' },
                { title: 'Four', subtitle: 'Secondary text' },
                { title: 'Five', subtitle: 'Secondary text' },
                { title: 'Six', subtitle: 'Secondary text' },
              ];

              constructor() {
                this.results = this.searchText.pipe(
                  startWith(''),
                  map((value: any) => {
                    const title = typeof value === 'string' ? value : value.title;
                    return this.filter(title)
                  }),
                  tap((results: string[]) =>
                    results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                  )
                );
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();
                return this.data.filter((item: any) => item.title.toLowerCase().includes(filterValue));
              }

              displayValue(value: any): string {
                return value ? value.title : '';
              }
            }
          
        
    
        
            
            .autocomplete-custom-item-content {
              display: flex;
              flex-direction: column;
            }
            .autocomplete-custom-item-title {
              font-weight: 500;
            }
            .autocomplete-custom-item-subtitle {
              font-size: 0.8rem;
            }
          
        
    

Custom content

A custom content container with a class .autocomplete-custom-content will be displayed at the end of the autocomplete-custom-item-subtitle dropdown. You can use it to display a number of search results.

        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete #autocomplete="mdbAutocomplete">
              <mdb-option *ngFor="let option of results | async" [value]="option">
                {{ option }}
              </mdb-option>
              <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
              <div class="autocomplete-custom-content">Search results: {{ customContentLength }}</div>
            </mdb-autocomplete>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { Observable, Subject } from 'rxjs';
            import {
              map,
              startWith,
              tap,
            } from 'rxjs/operators';

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              searchText = new Subject<string>();
              results: Observable<string[]>;
              notFound = false;
              customContentLength: number | null = null;
              data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];

              constructor() {
                this.results = this.searchText.pipe(
                  startWith(''),
                  map((value: string) => this.filter(value)),
                  tap((results: string[]) => {
                    results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                    this.customContentLength = results.length;
                  })
                );
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();
                return this.data.filter((item: string) => item.toLowerCase().includes(filterValue));
              }
            }
          
        
    

Reactive forms

        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete #autocomplete="mdbAutocomplete">
              <mdb-option *ngFor="let option of results | async" [value]="option">
                {{ option }}
              </mdb-option>
              <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
            </mdb-autocomplete>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { FormControl } from '@angular/forms';
            import { Observable } from 'rxjs';
            import {
              map,
              startWith,
              tap,
            } from 'rxjs/operators';

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              reactiveControl = new FormControl();
              results: Observable<string[]>;
              notFound = false;
              data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];

              constructor() {
                this.results = this.reactiveControl.valueChanges.pipe(
                  startWith(''),
                  map((value: string) => this.filter(value)),
                  tap((results: string[]) =>
                    results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                  )
                );
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();
                return this.data.filter((item: string) => item.toLowerCase().includes(filterValue));
              }
            }
          
        
    

Validation

Looks good!
Input value is required
        
            
            <form [formGroup]="validationFormGroup" style="width: 22rem">
              <mdb-form-control>
                <input
                  mdbInput
                  mdbValidate
                  formControlName="validationControl"
                  [mdbAutocomplete]="autocomplete"
                  type="text"
                  id="autocomplete"
                  class="form-control"
                />
                <label mdbLabel class="form-label" for="autocomplete">Example label</label>
                <mdb-error *ngIf="input?.invalid && (input?.dirty || input?.touched)"
                  >Input value is required</mdb-error
                >
                <mdb-success *ngIf="input?.valid && (input?.dirty || input?.touched)"
                  >Looks good!</mdb-success
                >
              </mdb-form-control>
              <mdb-autocomplete #autocomplete="mdbAutocomplete">
                <mdb-option *ngFor="let option of results | async" [value]="option">
                  {{ option }}
                </mdb-option>
                <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
              </mdb-autocomplete>
              <button type="submit" id="submit" class="btn btn-primary btn-sm mt-5">Submit</button>
            </form>
          
        
    
        
            
            import { Component } from '@angular/core';
            import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
            import { Observable } from 'rxjs';
            import {
              map,
              startWith,
              tap,
            } from 'rxjs/operators';

            @Component({
              selector: 'app-root',
              templateUrl: './app.component.html',
              styleUrls: ['./app.component.scss'],
            })
            export class AppComponent {
              validationFormGroup: FormGroup;
              results: Observable<string[]>;
              notFound = false;
              data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];

              constructor() {
                this.validationFormGroup = new FormGroup({
                  validationControl: new FormControl('', [Validators.required]),
                });
                this.results = this.input.valueChanges.pipe(
                  startWith(''),
                  map((value: string) => this.filter(value)),
                  tap((results: string[]) =>
                    results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                  )
                );
              }

              get input(): AbstractControl {
                return this.validationFormGroup.get('validationControl')!;
              }

              filter(value: string): string[] {
                const filterValue = value.toLowerCase();
                return this.data.filter((item: string) => item.toLowerCase().includes(filterValue));
              }
            }
          
        
    

Autocomplete - API


Import

        
            
        import { MdbAutocompleteModule } from 'mdb-angular-ui-kit/autocomplete';
        import { MdbFormsModule } from 'mdb-angular-ui-kit/forms';
        import { FormsModule, ReactiveFormsModule } from '@angular/forms';
          
          …
          @NgModule ({
            ...
            imports: [MdbAutocompleteModule, MdbFormsModule,FormsModule, ReactiveFormsModule],
            ...
          })
        
        
    

Inputs

Name Type Default Description
displayValue Function (value) => value Function executed for complex search results, to get value to display in the results list ue
optionHeight number 38 Changes the single option height value
listHeight number 190 Height of the results list

Outputs

Name Type Description
closed EventEmitter<void> Event emitted when the dropdown has been closed
opened EventEmitter<void> Event emitted when the dropdown has been opened
selected EventEmitter<MdbAutocompleteSelectedEvent> Event emitted when the autocomplete's item has been selected
        
            
            <mdb-form-control>
              <input
                mdbInput
                [ngModel]="searchText | async"
                (ngModelChange)="searchText.next($event)"
                [mdbAutocomplete]="autocomplete"
                type="text"
                id="autocomplete"
                class="form-control"
              />
              <label mdbLabel class="form-label" for="autocomplete">Example label</label>
            </mdb-form-control>
            <mdb-autocomplete
              #autocomplete="mdbAutocomplete"
              (opened)="onOpen()"
              (selected)="onSelected($event)"
            >
              <mdb-option *ngFor="let option of results | async" [value]="option">
                {{ option }}
              </mdb-option>
              <div *ngIf="notFound" class="autocomplete-no-results">No results found</div>
            </mdb-autocomplete>            
            
        
    
        
            
              import { Component } from '@angular/core';
              import { MdbAutocompleteSelectedEvent } from 'mdb-angular-ui-kit/autocomplete';
              import { Observable, Subject } from 'rxjs';
              import {
                map,
                startWith,
                tap,
              } from 'rxjs/operators';

              @Component({
                selector: 'app-root',
                templateUrl: './app.component.html',
                styleUrls: ['./app.component.scss'],
              })
              export class AppComponent {
                searchText = new Subject<string>();
                results: Observable<string[]>;
                notFound = false;
                data = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'];

                constructor() {
                  this.results = this.searchText.pipe(
                    startWith(''),
                    map((value: string) => this.filter(value)),
                    tap((results: string[]) =>
                      results.length > 0 ? (this.notFound = false) : (this.notFound = true)
                    )
                  );
                }

                filter(value: string): string[] {
                  const filterValue = value.toLowerCase();
                  return this.data.filter((item: string) => item.toLowerCase().includes(filterValue));
                }

                onOpen(): void {
                  console.log('opened');
                }

                onSelected(event: MdbAutocompleteSelectedEvent): void {
                  console.log('selected: ', event);
                }
              }
            
        
    

Advanced types

MdbAutocompleteSelectedEvent

        
            
          interface MdbAutocompleteSelectedEvent {
            component: MdbAutocompleteComponent;
            option: MdbOptionComponent;
          }
          
        
    

CSS variables

        
            
          --#{$prefix}autocomplete-label-active-transform: #{$autocomplete-label-active-transform};
          --#{$prefix}autocomplete-label-color: #{$autocomplete-label-color};

          --#{$prefix}form-outline-select-notch-border-color: #{$form-outline-select-notch-border-color};

          --#{$prefix}autocomplete-input-focused-color: #{$autocomplete-input-focused-color};

          --#{$prefix}autocomplete-dropdown-container-zindex: #{$autocomplete-dropdown-container-zindex};
          --#{$prefix}autocomplete-dropdown-background-color: #{$autocomplete-dropdown-background-color};
          --#{$prefix}autocomplete-dropdown-box-shadow: #{$autocomplete-dropdown-box-shadow};
          --#{$prefix}autocomplete-dropdown-margin: #{$autocomplete-dropdown-margin};
          --#{$prefix}autocomplete-dropdown-transform: #{$autocomplete-dropdown-transform};
          --#{$prefix}autocomplete-dropdown-transition: #{$autocomplete-dropdown-transition};
          --#{$prefix}autocomplete-dropdown-open-transform: #{$autocomplete-dropdown-open-transform};
          --#{$prefix}autocomplete-item-color: #{$autocomplete-item-color};
          --#{$prefix}autocomplete-item-padding: #{$autocomplete-item-padding};
          --#{$prefix}autocomplete-item-font-size: #{$autocomplete-item-font-size};
          --#{$prefix}autocomplete-item-font-weight: #{$autocomplete-item-font-weight};
          --#{$prefix}autocomplete-item-hover-background-color: #{$autocomplete-item-hover-background-color};
          --#{$prefix}autocomplete-item-disabled-color: #{$autocomplete-item-disabled-color};
        
        
    

SCSS variables

        
            
        $autocomplete-dropdown-container-zindex: 1065;

        $autocomplete-label-max-width: 80%;
        $autocomplete-label-active-transform: translateY(-1rem) translateY(0.1rem) scale(0.8);
        $autocomplete-input-focused-color: #616161;
        $autocomplete-label-color: $primary;
        $autocomplete-dropdown-background-color: #fff;
        $autocomplete-dropdown-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
        $autocomplete-dropdown-margin: 0;
        $autocomplete-dropdown-transform: scaleY(0.8);
        $autocomplete-dropdown-transition: all 0.2s;
        $autocomplete-dropdown-open-transform: scaleY(1);
        $autocomplete-item-color: rgba(0, 0, 0, 0.87);
        $autocomplete-item-padding: 6.5px 16px;
        $autocomplete-item-font-size: 1rem;
        $autocomplete-item-font-weight: 400;
        $autocomplete-item-hover-background-color: #ddd;
        $autocomplete-item-disabled-color: #9e9e9e;