Angular Google Maps in Modal | Autocomplete & Select Location, Latitude, Longitude, Address

Compatible from Angular 2 to latest version 9

Angular Google Maps package provides fully featured integration of Javascript-based Google Maps in Angular Applications.

Here we will discuss how to:

  1. Install Google Maps and Add API Keys
  2. Add Dynamic Google Maps in a Component
  3. Add Location Search Filter
  4. Draggable Marker
  5. Zoom Control
  6. Get Lattitude and Longitude of Marker Placed

Check the working demo here.

There’s a lot to do 🙂 Let’s get started!

Install Packages

First, we need to install some required packages.

Angular Google Maps Package

To use Google Maps in Angular project, install AGM package by running following NPM command

$ npm install @agm/core --save

GoogleMaps types library

For using Google Maps search feature we need to declare google variable for that install @types/googlemaps by running following NPM command

$ npm install @types/googlemaps --save-dev

Bootstrap Framework

User can open a modal which will have Google Maps to select a location by using search or marker dragging feature.
To install Bootstrap in Angular project run following NPM command

@ npm install --save @ng-bootstrap/ng-bootstrap

Also, include bootstrap.css in index.html to use bootstrap stylings

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">

 

So here we have successfully installed the required packages, next, we need to import them in App Module.

Create Google Maps Component

In modal, we will show Google maps which in turn will be a separate component. Run the following generate command to create a new component.

$ ng generate component components/google-maps

Configurations

In the app.module.ts file, we need to import AgmCoreModule for Google Maps and NgbModule for Bootstrap. After that add them in imports array as shown below:

// app.module.ts

...

import { GoogleMapsComponent } from './components/google-maps/google-maps.component';

import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { AgmCoreModule } from '@agm/core';


@NgModule({
  declarations: [
    AppComponent,
    GoogleMapsComponent// <- Component to show in modal
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,

    NgbModule,
    AgmCoreModule.forRoot({
      apiKey: 'AIzaSyAzSnhHOwXXXXXXXXXXXXXmhZSZGGWU',
      libraries: ['places']
    })

  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Add GoogleMaps type in the tsconfig.app.json file at the project root. In the "types" array add "googlemaps" as shown below:

...  
 "compilerOptions": {
    "outDir": "./out-tsc/app",
    "types": [
      "googlemaps"
    ]
  },
...

Open Google Maps Modal

In the App component we will now add a button to open a bootstrap modal by calling openGoogelMapsModal() method.

<div class="text-center">

    <p>
        <button class="btn btn-primary" (click)="openGoogelMapsModal()">Open Google Map</button>
    </p>
    <h1>
        <p *ngIf="coordinates.address">
            Address: {{coordinates.address}}
        </p>
    </h1>
    <h3>
        <p *ngIf="coordinates.latitude">
            Latitude: {{coordinates.latitude}}
        </p>
        <p *ngIf="coordinates.longitude">
            Longitude: {{coordinates.longitude}}
        </p>
    </h3>
</div>

 

Using modal a user can select a coordinate with address where marker is placed.

After saving the location the coordinates and address will be returned and assigned to the coordinates object with following properties:

interface Coordinates {
  address: string;
  latitude: number;
  longitude: number;
}

The app.component.ts file will have following code to open the Bootstrap Modal

// app.component.ts
import { Component } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { GoogleMapsComponent } from './components/google-maps/google-maps.component';

interface Coordinates {
  address: string;
  latitude: number;
  longitude: number;
}

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

  coordinates: Coordinates;

  constructor(
    private modalService: NgbModal
  ) {
    this.coordinates = {} as Coordinates;
  }

  openGoogelMapsModal() {
    const modalRef = this.modalService.open(GoogleMapsComponent,
      {
        scrollable: true,
        // windowClass: 'myCustomModalClass',
        // keyboard: false,
        // backdrop: 'static'
      });
    let data = {
      prop1: 'Some Data',
      prop2: 'From Parent Component',
      prop3: 'This Can be anything'
    }

    modalRef.componentInstance.fromParent = data;
    modalRef.result.then((result) => {
      this.coordinates = result;
    }, (reason) => {
    });
  }
}

To use modal, import NgbModal service to call its the open method where we passed GoogleMapsComponent which we created earlier. Optional data can be passed in modal using data object.

Adding Google Maps with Draggable marker and Search for Places

To show a map, add the agm-map with [latitude], [longitude] and [zoom] properties for default location.

For draggable marker add agm-marker wrapped in agm-map.

We will also add an input field to search places with #search template variable to control search events and bind places result set.

Place the final HTML template in google-maps.component.html file

<div class="modal-header">
    <h3 class="modal-title">Angular Google Maps with Places Search Example</h3>
    <button type="button" class="close" data-dismiss="modal" (click)="closeModal('dismiss')">
        <span aria-hidden="true">&times;</span>
    </button>
</div>
<div class="modal-body">

    <div class="form-group">
        <input type="text" class="form-control" (keydown.enter)="$event.preventDefault()"
            placeholder="Search Nearest Location" autocorrect="off" autocapitalize="off" spellcheck="off" type="text"
            #search>
    </div>

    <agm-map [latitude]="latitude" [longitude]="longitude" [zoom]="zoom">
        <agm-marker [latitude]="latitude" [longitude]="longitude" [markerDraggable]="true"
            (dragEnd)="markerDragEnd($event)"></agm-marker>
    </agm-map>

    <h5>Address: {{address}}</h5>
    <div>Latitude: {{latitude}}</div>
    <div>Longitude: {{longitude}}</div>


</div>
<div class="modal-footer">
    <button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="closeModal('close')">Close</button>
    <button type="button" class="btn btn-primary" (click)="saveLocation()">Save Location</button>
</div>

Note: Please add following css in styles.css file to autocomplete search results on modal.

.pac-container{
    z-index: 9999;
}

 

Now update the GoogleMapsComponent with Autocomplete places search method which will be called when Modal is initialized.

The MapsAPILoader service imported from @agm/core will call the load method when maps are ready. The setCurrentLocation() will get current latitude and longitude using navigator's geolocation service.

The markerDragEnd() method is called when marker is dragged manually by user to fetch coordinates to call getAddress() method fetching address using the Geocoder service.

The saveLocation method will pass current selection of coordinates and address in the NgbActiveModal's close to pass them on App component's results callback.

The final code for google.maps.component.ts file will look like this:

// google-maps.component.ts
import { Component, OnInit, ViewChild, ElementRef, NgZone, Input } from '@angular/core';
import { MapsAPILoader } from '@agm/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';


@Component({
  selector: 'app-google-maps',
  templateUrl: './google-maps.component.html',
  styleUrls: ['./google-maps.component.scss']
})
export class GoogleMapsComponent implements OnInit {

  title: string = 'AGM project';
  latitude: number;
  longitude: number;
  zoom: number;
  address: string;
  private geoCoder;
 
  @ViewChild('search')
  public searchElementRef: ElementRef;

  @Input() fromParent;
 
 
  constructor(
    private mapsAPILoader: MapsAPILoader,
    private ngZone: NgZone,
    public activeModal: NgbActiveModal
  ) { }
 
 
  ngOnInit() {
    //load Places Autocomplete
    this.mapsAPILoader.load().then(() => {
      this.setCurrentLocation();
      this.geoCoder = new google.maps.Geocoder;
 
      let autocomplete = new google.maps.places.Autocomplete(this.searchElementRef.nativeElement);
      autocomplete.addListener("place_changed", () => {
        this.ngZone.run(() => {
          //get the place result
          let place: google.maps.places.PlaceResult = autocomplete.getPlace();
 
          //verify result
          if (place.geometry === undefined || place.geometry === null) {
            return;
          }
 
          //set latitude, longitude and zoom
          this.latitude = place.geometry.location.lat();
          this.longitude = place.geometry.location.lng();
          this.zoom = 12;
        });
      });
    });
  }
 
  // Get Current Location Coordinates
  private setCurrentLocation() {
    if ('geolocation' in navigator) {
      navigator.geolocation.getCurrentPosition((position) => {
        this.latitude = position.coords.latitude;
        this.longitude = position.coords.longitude;
        this.zoom = 8;
        this.getAddress(this.latitude, this.longitude);
      });
    }
  }
 
 
  markerDragEnd($event: any) {
    console.log($event);
    this.latitude = $event.coords.lat;
    this.longitude = $event.coords.lng;
    this.getAddress(this.latitude, this.longitude);
  }
 
  getAddress(latitude, longitude) {
    this.geoCoder.geocode({ 'location': { lat: latitude, lng: longitude } }, (results, status) => {
      console.log(results);
      console.log(status);
      if (status === 'OK') {
        if (results[0]) {
          this.zoom = 12;
          this.address = results[0].formatted_address;
        } else {
          window.alert('No results found');
        }
      } else {
        window.alert('Geocoder failed due to: ' + status);
      }
 
    });
  }

  closeModal(sendData) { 
    this.activeModal.close(sendData); 
  }

  saveLocation(){
    const data = {
      address: this.address,
      latitude: this.latitude,
      longitude: this.longitude
    }
    this.activeModal.close(data);
  }
}

That's it now you have Google Map component to get location inputs from user from any parent component.

 

 

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments