@ng-select TypeAhead with debounceTime to fetch Server Response

In this Angular 9/8 post, we’ll discuss how to implement a TypeAhead or Auto Suggestion control in Angular application by using the @ng-select npm package module.

The Ng Select package is very popular to add a customized Single and Multi-select drop-down or select box with many type features and customization options.

You can check our other tutorial for implementation and adding features like Multiple select boxes with checkbox, Virtual Scroll for huge option performance, Custom search for multiple keys, etc.

By default, the ng-select box shows all values to the user when it is focused or clicked, but there is some situations where we don’t want to display all the options available and show only matching results as ser types like a type ahead or suggestion box.

Today, we’ll discuss how to show dynamic server fetched results in the ng-select control by making a server call as user types. It will also have min length property to search only if the user type at least 2 characters as a search term.

We’ll be using RxJS advanced method and operators to optimize the server result fetching using debounceTime and distinctUntilChanged to limit unnecessary HTTP calls

Let’s quickly create a new Angular application and setup @ng-select package to demonstrate TypeAhead to fetch server response.

Create a new Angular project

Run following ng command to create a new Angular 9 project

$ ng new angular-ngselect-typeahead-app

? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? SCSS

Move inside the Angular project

$ cd angular-ngselect-typeahead-app

Run Angular project

$ ng serve --open

 

Install ng-select package

Now install the ng-select package module in the Angular project we created

$ npm install --save @ng-select/ng-select

 

Import ng-select in App Module

To use the ng-select component, we need to import the NgSelectModule in the App’s main module. For using ngModel also import the FormsModule.As results will be fetched from a server also import the HttpClientModuleto make HTTP get API calls

Open the app.module.ts file and make the following changes.

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    NgSelectModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

Add ng-select Style Theme

Finally, add a theme to style our awesome ng-select component.

We already have three themes available to select from. Import any of the following in the style.css file:

@import "~@ng-select/ng-select/themes/default.theme.css";
@import "~@ng-select/ng-select/themes/material.theme.css";
@import "~@ng-select/ng-select/themes/ant.design.theme.css";
Now we are done with ng-select installation and configurations required to use anywhere in our application.

Using TypeAhead in ng-select Box

Create an ng-select box by adding the below component in the template

<ng-select 
    [items]="movies$ | async" 
    bindLabel="Title" 
    [trackByFn]="trackByFn"
    [minTermLength]="minLengthTerm" 
    [loading]="moviesLoading"
    typeToSearchText="Please enter {{minLengthTerm}} or more characters" [typeahead]="moviesInput$"
    [(ngModel)]="selectedMovie">
    
  </ng-select>

Let’s have a look at the properties we used in the ng-select component to show remote server results in the select box

  • [items]: This represents the Observable to keep track of the change in the items collection which we are fetching from the server by making HTTP get a call.
  • bindTitle: To display the property value after a value is selected by the user, By default, it shows the name by here we want to show Title in the response Object.
  • [trackByFn]: Used to track the ID of the item selected. here we have imdbID.
  • [minTermLength]: It takes a number of characters typed by the user after which the result options are shown to select from.
  • [loading]: This displays the loading message to indicate the status of the result set to the user.
  • typeToSearchText: Message displayed to the user as a placeholder.
  • [typeahead]: This is the Subject that watches the change in the term typed by the user to fetch results from the server.

 

Update Template Class Component

In the app.component.ts file, we’ll make the following changes:

Add a few variables in the class

 movies$: Observable<any>;
  moviesLoading = false;
  moviesInput$ = new Subject<string>();
  selectedMovie: any;
  minLengthTerm = 3;

The movies$ and moviesInput$ are Observable used to subscribe to changes in the collection object and input control respectively.

The loadMovies() method is called on component init to subscribe to the change in the Object data fetched from the server.

loadMovies() {

    this.movies$ = concat(
      of([]), // default items
      this.moviesInput$.pipe(
        filter(res => {
          return res !== null && res.length >= this.minLengthTerm
        }),
        distinctUntilChanged(),
        debounceTime(800),
        tap(() => this.moviesLoading = true),
        switchMap(term => {

          return this.getMovies(term).pipe(
            catchError(() => of([])), // empty list on error
            tap(() => this.moviesLoading = false)
          )
        })
      )
    );

  }

The switchMap() fetches the server result by calling the getMovies() method by passing the term typed by the user.

Here we used three methods to control the HTTP get calls

  • filter(): The event will be triggered only when the length of the input value is more than 2 or whatever you like.
  • debounceTime(): This operator takes time in milliseconds. This is the time between key events before a user stops typing.
  • distinctUntilChanged(): This operator checks whether the current input is sitting from a previously entered value. So that API will not hit if the current and previous value is the same

 

So the final component class will look like this

// app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { concat, Observable, of, Subject, throwError } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged, switchMap, tap, map, filter } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'angular-ngselect-typeahead-app';

  movies$: Observable<any>;
  moviesLoading = false;
  moviesInput$ = new Subject<string>();
  selectedMovie: any;
  minLengthTerm = 3;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.loadMovies();
  }

  trackByFn(item: any) {
    return item.imdbID;
  }

  loadMovies() {

    this.movies$ = concat(
      of([]), // default items
      this.moviesInput$.pipe(
        filter(res => {
          return res !== null && res.length >= this.minLengthTerm
        }),
        distinctUntilChanged(),
        debounceTime(800),
        tap(() => this.moviesLoading = true),
        switchMap(term => {

          return this.getMovies(term).pipe(
            catchError(() => of([])), // empty list on error
            tap(() => this.moviesLoading = false)
          )
        })
      )
    );

  }

  getMovies(term: string = null): Observable<any> {
    return this.http
      .get<any>('http://www.omdbapi.com/?apikey=[YOUR_OMDB_KEY]&s=' + term)
      .pipe(map(resp => {
        if (resp.Error) {
          throwError(resp.Error);
        } else {
          return resp.Search;
        }
      })
      );
  }

}

NOTE: We have used OMDB API for demo purposes. You can get your own key for free by simply adding your email.

Now run your project to see it working by hitting $ ng serve --open

angular-ng-select-typeahead-demo-1

 

Conclusion

Using ng-select we can add a powerful yet optimized select box for Single or Multiple selection control. Here we discussed how to display dynamic server response objects in the ng-select components by using Observable. To implement this we added [typeahead] and [minTermLength] properties.

Also, we used RxJS methods to add debountTime methods to further optimize the server HTTP calls.

I hope you enjoyed this tutorial and it was helpful. Do share your comments and feedback.

Stay Safe Stay Happy 🙂

3 thoughts on “@ng-select TypeAhead with debounceTime to fetch Server Response”

  1. Hi, just wanted to mention that I think the “debounceTime” operator needs to come before the “distinctUntilChanged()” operator in order for it to work correctly. Otherwise any input change will cause it to be distinct as it runs directly after the change event occurs.

  2. Hi, Thanks for the detailed explanation. Can you let me know how can we set a default value for this field. Im using a reactive form and trying to set by using setValue, but its not working

Leave a Comment

Your email address will not be published. Required fields are marked *