Dude, Learn the Autocomplete from Angular Material

If you are using Angular Material, keep reading to learn about a very useful component: autocomplete. This is similar to the select HTML element, but it allows you to type your search and while narrowing the options down in real time.

Today we’ll do an example of a country calling code list:

Let’s get started!

Procedure

First let’s make sure we have the latest Node and npm installed on our system. Download them here:

Next we’ll set up our Angular project. Install the Angular CLI tool

npm install -g @angular/cli

Generate a new project

ng new code-autocomplete

Change to the directory of the newly-created project

cd code-autocomplete

Add Angular Material

ng add @angular/material

Run the local dev server

ng serve

Check localhost:4200 in your browser

localhost:4200

We will use a few components to create the desired feature, so let’s import the necessary Angular Material modules.

app.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { BrowserModule } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';

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

Now let’s add the template:

app.component.html

<form>
  <mat-form-field appearance="fill">
    <mat-label>Country Code</mat-label>
    <input
      matInput
      aria-label="country"
      [matAutocomplete]="auto"
      [formControl]="countryCtrl"
    />
    <mat-autocomplete #auto="matAutocomplete">
      <mat-option
        *ngFor="let country of filteredCountries | async"
        [value]="country.name"
      >
        <img aria-hidden [src]="country.flag" />
        <span>{{ country.name }}</span>
        <small> (+{{ country.code }})</small>
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

Add the business logic for the data and filter function:

app.component.ts

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Country {
  flag: string;
  name: string;
  code: number;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  countryCtrl = new FormControl();
  filteredCountries: Observable<Country[]>;

  countries: Country[] = [
    {
      name: 'England',
      code: 44,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg',
    },
    {
      name: 'France',
      code: 33,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/1/1c/Flag_of_France_%28darker_version%29.svg',
    },
    {
      name: 'Gambia',
      code: 220,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/7/77/Flag_of_The_Gambia.svg',
    },
    {
      name: 'Germany',
      code: 49,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/b/ba/Flag_of_Germany.svg',
    },
    {
      name: 'Iceland',
      code: 354,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/c/ce/Flag_of_Iceland.svg',
    },
    {
      name: 'Italy',
      code: 39,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/0/03/Flag_of_Italy.svg',
    },
    {
      name: 'Netherlands',
      code: 31,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/2/20/Flag_of_the_Netherlands.svg',
    },
    {
      name: 'Spain',
      code: 34,
      flag: 'https://upload.wikimedia.org/wikipedia/commons/7/7d/Flag_of_Spain_%281785%E2%80%931873%2C_1875%E2%80%931931%29.svg',
    },
  ];

  constructor() {
    this.filteredCountries = this.countryCtrl.valueChanges.pipe(
      startWith(''),
      map((state) =>
        state ? this._filterStates(state) : this.countries.slice()
      )
    );
  }

  private _filterStates(value: string): Country[] {
    const filterValue = value.toLowerCase();

    return this.countries.filter((country) =>
      country.name.toLowerCase().includes(filterValue)
    );
  }
}

…and finally add some style:

app.component.css

form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

mat-form-field {
  margin: 1rem;
}

img {
  vertical-align: middle;
  margin-right: 8px;
  width: 35px;
  height: 20px;
}

Here is the final result:

Latest Posts

Leave a comment