Address autocomplete component – npm package

Get a location from an address and address from a location – this is the main purpose of the Geocoding API. However, it’s not how it’s usually used in real-life. In real-life people enter one by one symbol into address autocomplete field, get suggestions and select the correct result there.

We already published articles describing how to create an address autocomplete from scratch with JSFiddle code samples. In this article, we would like to introduce you to an npm package that allows adding geocoding with autocomplete into your project with a few lines of code only.

We’ve developed a base Geocoding Autocomplete JS Library, that turns a DIV-container into an Address field with autocomplete. Moreover, we’ve added components for React and Angular that wrap the JavaScript library and provide a convenient interface for it.

All 3 packages use Geoapify Geoacoder API as the geocoder and support all parameters of the API:

  • You can make the autocomplete show only countries, cities, states, streets, or amenities;
  • Set location bias. So the autocomplete will return first results near the location;
  • Choose between different languages. For example, depending on the language you will get “Germany”, “Deutschland”, “Allemagne” or “Германия” in the search results;
  • Filter results. For example, search only in given countries.

You require your own API key to make Geocoding API calls. Get an API key on MyProjects Geoapify. The APIs have the Freemium pricing model. So you can start for Free and extend when you need.

@geoapify/geocoder-autocomplete

Geoapify Geocoder Autocomplete is the lightweight JavaScript library, that lets you add an address input with autocomplete into your web app. It has 0 dependencies and easy to customize. Furthermore, it already includes a few style themes for both light and dark backgrounds.

Installation

npm install @geoapify/geocoder-autocomplete

or

yarn add @geoapify/geocoder-autocomplete

Usage

  • Prepare a DIV-container with position: relative:
.autocomplete-container {
    position: relative;
}
<div id="autocomplete" class="autocomplete-container"></div>
  • Add Geoapify Geocoder Autocomplete:
import { GeocoderAutocomplete } from '@geoapify/geocoder-autocomplete';
 
const autocomplete = new GeocoderAutocomplete( document.getElementById("autocomplete"),  'YOUR_API_KEY', { /* Geocoder options */ });
 
autocomplete.on('select', (location) => {
    // check selected location here 
});
 
autocomplete.on('suggestions', (suggestions) => {
    // process suggestions here
});
  • Add styles to make the control work properly. For example, you can do it by importing style into your css-file:
 
@import "[email protected]/geocoder-autocomplete/styles/minimal.css";

You can easily customize the input. Read more about provided themes and CSS-classes on @geoapify/geocoder-autocomplete page.

@geoapify/angular-geocoder-autocomplete

Geoapify Angular Geocoder Autocomplete is an Angular component that wraps the Geoapify Geocoder Autocomplete and provides easy to use interface for it. Instead of functions and callbacks, the component lets you work with Angular Inputs and Outputs.

Installation

npm install @geoapify/geocoder-autocomplete @geoapify/angular-geocoder-autocomplete

or

yarn add @geoapify/geocoder-autocomplete @geoapify/angular-geocoder-autocomplete

Usage

  •  Import the GeoapifyGeocoderAutocompleteModule module:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { GeoapifyGeocoderAutocompleteModule } from '@geoapify/angular-geocoder-autocomplete';
 
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    GeoapifyGeocoderAutocompleteModule.withConfig('YOUR_API_KEY')
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
  • Import Geoapify Geocoder Autocomplete styles. For example, in ‘angular.json’:
"styles": [
    ...
    "node_modules/@geoapify/geocoder-autocomplete/styles/round-borders.css",
    "src/styles.scss"
],
  • Add the component into your template:
<geoapify-geocoder-autocomplete 
    [value]="displayValue"
    [type]="options.type"
    [lang]="options.lang"
    [countryCodes]="options.countries"
    [position]="options.position"
    [limit]="options.limit" 
    (placeSelect)="placeSelected($event)" 
    (suggestionsChange)="suggestionsChanged($event)">
</geoapify-geocoder-autocomplete>

@geoapify/react-geocoder-autocomplete

Geoapify React Geocoder Autocomplete is an Angular component that wraps the Geoapify Geocoder Autocomplete and provides access to all API parameters via the component properties.

Installation

npm install @geoapify/geocoder-autocomplete @geoapify/react-geocoder-autocomplete

or

yarn add @geoapify/geocoder-autocomplete @geoapify/react-geocoder-autocomplete

Usage

  • Create the GeoapifyContext and set you API key there;
  • Import Geoapify Geocoder Autocomplete styles;
  • Add GeoapifyGeocoderAutocomplete to your code:
import React, { useState } from 'react'
import { GeoapifyGeocoderAutocomplete, GeoapifyContext } from '@geoapify/react-geocoder-autocomplete'
import '@geoapify/geocoder-autocomplete/styles/minimal.css'
 
const App = () => {
  ...
 
  function onPlaceSelect(value) {
    console.log(value);
  }
 
  function onSuggectionChange(value) {
    console.log(value);
  }
 
  return <GeoapifyContext apiKey="YOUR_API_KEY_HERE">
      <GeoapifyGeocoderAutocomplete placeholder="Enter address here"
        type={type}
        lang={language}
        position={position}
        countryCodes={countryCodes}
        limit={limit}
        placeSelect={onPlaceSelect}
        suggestionsChange={onSuggectionChange}
        />
    </GeoapifyContext>
}
 
export default App

Address field with autocomplete – step by step tutorial with JSFiddle example

Adding an address field ideally with autocomplete is a very common task for web-developer. And even if there are libraries providing components and “ready-to-use” solutions they often are difficult to style, missing some functionality required and need to be maintained. Moreover, when you start to use a component “from-a-box” you bind yourself with the exact API provider.

In this tutorial, we propose you to create an address field with autocomplete with Vanilla Javascript only. We use the Geoapify Geocoding service to convert addresses into locations and JSFiddle for a playground.

Before you start

Register and get an API key for free on MyProjects Geoapify. We have a Freemium pricing model, so you can start for Free and extend when you need it.

We provide an API key for JSFiddle example and you can reuse it for other JSFiddle examples. However, you require your own API key to make API calls from your own application.

Step 1. Create an input field in a provided HTML-container

  • We start just with a DIV-element, which is positioned relatively;
  • Let’s create an input field inside the container and make it spread full width:

Step 2. Send a geocoding request on user input and show a dropdown list with results

  • Now let’s add a user input event and send a geocoding request when the event is fired;
  • As the geocoding result comes asynchronously we need to cancel the previous request if the user input changes;
  • We create a DIV-element with geocoding results and show it as a dropdown to the input element;
  • We hide the dropdown list on new user input:

Step 3. Select an option and notify when an option was selected

  • Now let’s make options clickable and notify about selected option;
  • We add a callback parameter to the addressAutocomplete() to get notifications:

Geoapify Geocoding API returns results in GeoJSON format. Learn more about GeoJSON feature properties on the Geocoding documentation page.

Step 4. Add clear button

The Geocoding Autocomplete is functional now, but not user-friendly enough. Let’s improve this!

  • Add a clear button to the input field;
  • Hide the button when input is empty;
  • Send notification when the text was cleared:

Step 5. Add keyboard navigation support

It would be great to have the possibility to navigate the address field with a keyboard.

  • Let’s add a listener for arrow UP, arrow DOWN and ENTER key;
  • We are going to navigate and send notifications when a user navigates the dropdown list;
  • The focused item in the dropdown list is highlighted with a background color. We store the index of the focused element in the focusedItemIndex;
  • We close the dropdown list when the user presses ENTER:

Step 6. Make the dropdown list responsive

At the moment the dropdown is opened when a user types in the input field and closed only when an option was selected. Let’s close the dropdown list when a user clicked outside the input control and show the dropdown list again when the control is clicked again:

Step 7. Search countries, cities, streets

Geoapify Geocoding API allows specifying a type for locations. Let’s add a parameter for the addressAutocomplete() that limits the search to the specific location type:

Geoapify Geocoding API parameters

With Goeapify Geocoding you can make your search more concrete:

  • Search by exact address type. For example, search only countries, cities or streets.
  • Limit the search to a list of counties.
  • Search around a given location.
  • Change the language of results.

Learn more about Geoapify Geocoding API parameters and options.

Location autocomplete with Angular

Geoapify provides an API which allows searching a location by the query string. In this article, we provide you an example of how to create a location autocomplete field by using Geoapify Geocoding API.

In our example, we use Angular platform together with Angular Material framework.

Geoapify Geocoding API Playground contains a working example of the location autocomplete described in this article.

Pre-requirements

  • Angular Material installed. Read about installation here.

Step 1. Create a new component for a location autocomplete

Create a new component with Angular Cli command:

ng generate component Autocomplete

Step 2. Required imports

To make the component from this tutorial work, it’s required to import the following modules into your module:

@NgModule({
  declarations: [..., AutocompleteComponent],
  imports: [
    CommonModule,
    HttpClientModule,
    MatInputModule,
    MatFormFieldModule,
    FormsModule,
    ReactiveFormsModule,
    MatAutocompleteModule,
    MatTooltipModule,
    ...
  ],
  exports: [...]
})

Step 3. HTML template

We use the following components:

  • mat-form-input
  • mat-input
  • mat-autocomplete
  • mat-tooltip
  • formControl from ReactiveFormsModule.

Add the following code into your component html template:

<mat-form-field floatLabel="never">
  <input matInput type="text" [matAutocomplete]="auto" 
    [formControl]="inputFieldFormControl" placeholder="Enter location here" />

  <mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
    <mat-option *ngFor="let option of searchOptions | async" [value]="option.shortAddress" 
      (onSelectionChange)="optionSelectionChange(option, $event)"
      [matTooltip]="option.fullAddress" matTooltipShowDelay="1000">
      <span class="mat-body">{{ option.shortAddress }}</span>
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

Here is an idea of how the location autocomplete will work:

  • The string value will be stored in the “inputFieldFormControl” reactive form field.
  • The “inputFieldFormControl” field fires an event when its value was changed.
  • When an event fired we send an HTTP Get request to Geocoding API to retrieve place suggestions and store them in “searchOptions”.
  • mat-autocomplete loads options from the “searchOptions” asynchronously.

Step 4. AutocompleteComponent class

Here is the code of the AutocompleteComponent class:

import { Component, Output, EventEmitter, OnDestroy } from '@angular/core';
import { MatOptionSelectionChange } from '@angular/material';
import { Subject, Subscription } from 'rxjs';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-autocomplete',
  templateUrl: './autocomplete.component.html',
  styleUrls: ['./autocomplete.component.scss']
})
export class AutocompleteComponent implements OnDestroy {
  @Output()
  locationChange: EventEmitter<PlaceSuggestion> = new EventEmitter<PlaceSuggestion>();

  searchOptions: Subject<PlaceSuggestion[]> = new Subject<PlaceSuggestion[]>();
  inputFieldFormControl: FormControl = new FormControl();

  private valueChangesSub: Subscription;
  private choosenOption: PlaceSuggestion;

  private userInputTimeout: number;
  private requestSub: Subscription;

  constructor(private http: HttpClient) {
    this.valueChangesSub = this.inputFieldFormControl.valueChanges.subscribe((value) => {
      if (this.userInputTimeout) {
        window.clearTimeout(this.userInputTimeout);
      }

      if (this.choosenOption && this.choosenOption.shortAddress === value) {
        this.searchOptions.next(null);
        return;
      }

      if (!value || value.length < 3) {
        // do not need suggestions until for less than 3 letters
        this.searchOptions.next(null);
        return;
      }

      this.userInputTimeout = window.setTimeout(() => {
        this.generateSuggestions(value);
      }, 300);
    });
  }

  ngOnDestroy() {
    this.valueChangesSub.unsubscribe();
  }

  private generateSuggestions(text: string) {
    const url = `https://api.geoapify.com/v1/geocode/autocomplete?text=${text}&limit=5&apiKey=${YOUR_API_KEY}`;

    if (this.requestSub) {
      this.requestSub.unsubscribe();
    }

    this.requestSub = this.http.get(url).subscribe((data: GeoJSON.FeatureCollection) => {
      const placeSuggestions = data.features.map(feature => {
        const properties: GeocodingFeatureProperties = (feature.properties as GeocodingFeatureProperties);

        return {
          shortAddress: this.generateShortAddress(properties),
          fullAddress: this.generateFullAddress(properties),
          data: properties
        }
      });

      this.searchOptions.next(placeSuggestions.length ? placeSuggestions : null);
    }, err => {
      console.log(err);
    });
  }

  private generateShortAddress(properties: GeocodingFeatureProperties): string {
    let shortAddress = properties.name;

    if (!shortAddress && properties.street && properties.housenumber) {
      // name is not set for buildings
      shortAddress = `${properties.street} ${properties.housenumber}`;
    }

    shortAddress += (properties.postcode && properties.city) ? `, ${properties.postcode}-${properties.city}`: '';
    shortAddress += (!properties.postcode && properties.city && properties.city  !== properties.name) ? `, ${properties.city}`: '';
    shortAddress += (properties.country && properties.country !== properties.name) ? `, ${properties.country}` : '';

    return shortAddress;
  }

  private generateFullAddress(properties: GeocodingFeatureProperties): string {
    let fullAddress = properties.name;
    fullAddress += properties.street ? `, ${properties.street}` : '';
    fullAddress += properties.housenumber ? ` ${properties.housenumber}` : '';
    fullAddress += (properties.postcode && properties.city) ? `, ${properties.postcode}-${properties.city}`: '';
    fullAddress += (!properties.postcode && properties.city && properties.city  !== properties.name) ? `, ${properties.city}`: '';
    fullAddress += properties.state ? `, ${properties.state}`: '';
    fullAddress += (properties.country && properties.country !== properties.name) ? `, ${properties.country}` : '';
    return fullAddress;
  }

  public optionSelectionChange(option: PlaceSuggestion, event: MatOptionSelectionChange) {
    if (event.isUserInput) {
      this.choosenOption = option;
      this.locationChange.emit(option);
    }
  }
}

export interface PlaceSuggestion {
  shortAddress: string;
  fullAddress: string;
  data: GeocodingFeatureProperties;
}

interface GeocodingFeatureProperties {
  name: string;
  country: string;
  state: string;
  postcode: string;
  city: string;
  street: string;
  housenumber: string;
}

inputFieldFormControl

As described above, the field holds the value of the search string, which is observed by this.inputFieldFormControl.valueChanges.subscribe().

To keep the code clean we save the created subscription in the variable valueChangesSub and unsubscribe on destroy.

userInputTimeout

To avoid too many unnecessary requests and decrease the application load, we perform HTTP request only when a user stops to type.

This implemented by using userInputTimeout, which sets every time when the user enters a new value.

searchOptions

Contain the values returned by Geocoding API and displayed by autocomplete control.

When we set this.searchOptions.next(null), the autocomplete control is hidden.

PlaceSuggestion & GeocodingFeatureProperties

We use the interfaces to simplify work with JSON object returned by the Geocoding API. We export PlaceSuggestion to be able to use the interface in other components, services, and modules.

locationChange

Is an Output() of the Autocomplete component. We this.locationChange.emit(option) when a new place suggestion was selected.

generateShortAddress() & generateFullAddress()

As Geocoding API returns value with address components, but not a formatted address, we need to generate an address string of required format. generateShortAddress() & generateFullAddress() are examples of how an address string could be generated.

Step 5. Add the Location Autocomplete component into your code

The new component could be added into your code in the following way:

<app-autocomplete (locationChange)="autocompleteChanged($event)"></app-autocomplete>

When a new value was chosen in the location autocomplete, the event is fired:

autocompleteChanged(value: PlaceSuggestion) {}