Angular 9|8 Material Autocomplete Example with Remote/ Server Side Results

We have already discussed the basic usage and implementation of Angular Material Autocomplete Component in an Angular Application in our previous post here.

Autocomplete is a very popular behaviour used in many application to easy user form filling experience by providing some already built strings to fast data entry. Autocomplete is very similar to Input fields with addition to some Items showing up when the user focuses on that Input.

Sometimes we may need to show some results from the server-side as per a request made by the user. For example, there are two fields for State and City. A user will first select a State, then City list will load in other Input from the server-side according to state selected, as we may have thousands of cities from 12-15 states Right?

In this post, we will add Angular Autocomplete with Debounce feature which will fetch results from server/API response. Here we will also use HttpClient service of Angular to make Get request to IMDB movies API for demonstration.

You can check my previous post to know more on adding Angular Material in Angular project.

Autocomplete with server-side responses will also use the debounceTime method of Rxjs for optimized API calls for results.

 

Implement Server Side Autocomplete

Let’s add HttpClientModule and some other Angular material Modules which will be used in our demo.

Open App’s main module file app.module.ts then replace below code.

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

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

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

To make server HTTP calls add HttpClientModule, for autocomplete import MatInputModule & MatAutocompleteModule.

We have FormsModule with ReactiveFormsModule so that we can easily bind RxJS debounceTime and also get the input value.

Add Autocomplete template

In app.component.html file replace below template to show Input Field with Autocomplete.

<div>

  <mat-form-field>
    <input matInput placeholder="Search" aria-label="State" [matAutocomplete]="auto" [formControl]="searchMoviesCtrl">
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
      <mat-option *ngIf="isLoading" class="is-loading">Loading...</mat-option>
      <ng-container *ngIf="!isLoading">
        <mat-option *ngFor="let movie of filteredMovies" [value]="movie.Title">
          <span><b>{{movie.Title}}</b> ({{movie.Year}})</span>
        </mat-option>
      </ng-container>
    </mat-autocomplete>
  </mat-form-field>

  <br>

<ng-container *ngIf="errorMsg; else elseTemplate">
  {{errorMsg}}
</ng-container>
<ng-template #elseTemplate>
  <h5>Selected Value: {{searchMoviesCtrl.value}}</h5>
</ng-template>

</div>

 

In app.component.ts file replace following code to make server calls to populate Autocomplete results using OMDB free API.

//app.component.ts
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';

import { debounceTime, tap, switchMap, finalize } from 'rxjs/operators';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  searchMoviesCtrl = new FormControl();
  filteredMovies: any;
  isLoading = false;
  errorMsg: string;

  constructor(
    private http: HttpClient
  ) { }

  ngOnInit() {
    this.searchMoviesCtrl.valueChanges
      .pipe(
        debounceTime(500),
        tap(() => {
          this.errorMsg = "";
          this.filteredMovies = [];
          this.isLoading = true;
        }),
        switchMap(value => this.http.get("http://www.omdbapi.com/?apikey=[YOUR_KEY_HERE]=" + value)
          .pipe(
            finalize(() => {
              this.isLoading = false
            }),
          )
        )
      )
      .subscribe(data => {
        if (data['Search'] == undefined) {
          this.errorMsg = data['Error'];
          this.filteredMovies = [];
        } else {
          this.errorMsg = "";
          this.filteredMovies = data['Search'];
        }

        console.log(this.filteredMovies);
      });
  }
}

 

The debounceTime checks if keypress interval is less then time provided, then cancels the further events.

The tap and finalize operators are used to handling events before the server call and after response.

The switchMap is most similar to mergeMap but it is used here as it cancels previous request event if the response is not received yet. But mergeMap waits for a previous response then make next call so this behaviour is not required here.

Also check: How to add Angular Material Loaders and Spinners in Angular App

That's it now you have a working Angular Material Autocomplete fetching server-side responses as user types in having RxJS DebounceTime which will only hit API HTTP call when the user stops typing.

 

Subscribe
Notify of
9 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments

can you please give a stackblitz link

switchMap(value => this.http.get(“http://www.omdbapi.com/?apikey=[YOUR_KEY_HERE]=” + value)
.pipe(
finalize(() => {
this.isLoading = false
}),
)
)

In the above snippet, how can I prevent service call if value length is 0? I want to call the service if length greater than 4?

Sorry.. I see the same question asked by someone. It solved my problem.

Super awesome tutorial!!!

Would you share CSS styles for this nice loading indicator (as shown on the animation) too?

Hi, this is an incredible tutorial!
How can I prevent the rest api to be called when value.length < 2?

Thank you!

I love you, helped me alot