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

In the Angular Material tutorial, we’re going to discuss how to create a Material Autocomplete showing suggestion results from a remote server by using the mat-autocomplete component in Angular 10/9/8/7/6/5/4 application.

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 behavior used in many applications 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 the Debounce feature which will fetch results from server/API response. Here we will also use the HttpClient service of Angular to make a Get request to IMDB movies API for demonstration.

You can check my previous post to know more about adding Angular Material to the Angular project.

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

 

Angular 10|9 Material Autocomplete Example with Remote/ Server Side 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 the 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 the keypress interval is less than 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 the previous request event if the response is not received yet. But mergeMap waits for a previous response then make the next call so this behavior 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
guest
9 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
aadsfasdf

can you please give a stackblitz link

Krishna

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.

Unknown

Super awesome tutorial!!!

a b

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

Holden

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

Thank you!

Martín Rodríguez

I love you, helped me alot