Building Realtime Blood Donor App using Angular 8, Google Maps, and Firebase

Building Realtime Blood Donor App using Angular 8, Google Maps, and Firebase

In this post, you'll see step by step tutorial on building Realtime blood donor app using Angular 8, Google Maps, Location, and Firebase Realtime Database

Originally published at https://www.djamware.com

In this Angular 8 tutorial, we will be building a realtime blood donor app based on location tracking. Set the donor location to Firebase realtime database that automatically updated and seen by other users as real-time Google maps markers. The other users that see the blood donor markers on Google maps based on near to their device or computer location. So, they can choose and contact the desire blood donor then contact it.

Simply as that the flow of the Angular 8 Google Map Location Firebase realtime donor app.

Table of Contents:

  • Prerequisites
  • Setup Google Maps API Key
  • Set up Firebase Realtime Database
  • Create a New Angular 8 Web App
  • Install Google Firebase SDK
  • Create Angular Component and Routing
  • Display Google Maps
  • Get Donor List from Firebase Realtime Database
  • Display Donor Position to Google Maps
  • Add a New Donor Data using Angular Material
  • Run and Test Angular 8 Google Maps Firebase Realtime Blood Donor App
Prerequisites

As we mention in the first paragraph of this tutorial, this Angular 8 web app involves Google Map, Browser Location, and Firebase Realtime Database. So, the following tools, frameworks, libraries, and modules are required for this tutorial achievement:

  1. Node.js
  2. Angular 8
  3. Google Maps Javascript API
  4. Google Maps API Key
  5. Firebase Realtime Database
  6. Firebase Javascript Module
  7. Terminal or Node Command Line
  8. Text Editor or IDE

As usual, we have to make sure the Node.js installed correctly before going to the main steps. Type this command in the terminal or command line to check the installed Node.js and NPM.

node -v
v10.15.1
npm -v
6.11.2

If there's no Node.js and NPM installed, go to the Node.js official site then download and install it.

Setup Google Maps API Key

To use Google Maps Javascript API we need a Google Maps API key. The Google Maps JavaScript API lets you customize maps with your own content and imagery for display on web pages and mobile devices. The Maps JavaScript API features four basic map types (roadmap, satellite, hybrid, and terrain) which you can modify using layers and styles, controls and events, and various services and libraries.

Next, open your browser the go to the Google Developer Console. You will be taken to this Google Developer Console page.

Just scroll the default opened projects then it will take to this dialog.

Click the "New Project" button then it will take to this New Google Project page.

Fill the project name and leave other fields as default then click the "Create" button and it will take to the Google Developer Console home page with default opened project. Select again the project then choose the newly created Angular Maps project and click the "OK" button. It will take to the Angular Maps project dashboard without any APIs enabled.

Click the "Enable API" button then it will take to this Google APIs library page.

Find and choose Maps Javascript API then it will take to this Maps Javascript API page.

Just click on the "Enable" button then it will take back to the Maps Javascript API dashboard.

Click on Credentials link then it will take to the Credentials page.

Click on "+ Create Credentials" link then choose "API KEY" and it will take to the API KEY dialog. Copy and paste the newly created API KEY to your Notepad or Text Editor for using in the Angular app.

Set up Firebase Realtime Database

Next, we will set up or create a new Google Firebase project that can use the realtime database. Just open your browser then go to Google Firebase Console and it will take to this page.

From that page, click "+" add project button to create a Google Firebase project then it will be redirected to this page.

Type the previously created Google APIs project then choose the existing Google APIs project and click the "Continue" button.

This time, choose to not add Firebase analytics for now then click the "Add Firebase" button. And now, the Firebase will be added to the Angular Maps project.

Click the "Continue" button then it will take to the Firebase Angular Maps project dashboard.

Click the "Database" menu then it will take to the Firebase Database dashboard.

Scroll down and find "Or Choose Realtime Database" then click on the "Create Database" button. Next, it will take to the "Security rules for Realtime Database" dialog.

Choose "Start in test mode" radio button then click on the "Enable" button and it will take to the "Realtime Database" dashboard. Now, the Google Firebase realtime database is ready to use with your Angular Maps web app.

Create a New Angular 8 Web App

In the prerequisites step, we have checked the Node.js and NPM version. Now, we have to install or check the Angular CLI version. The Angular CLI is a tool to initialize, develop, scaffold and maintain Angular applications. From the Terminal or Node Command Line, type this command to install or update the Angular CLI.

sudo npm install -g @angular/cli

Now, we have the latest version of Angular when this example was written.

  _           _        ____ _  ___
  / \ _ __ __ _ _ _| | __ _ _ __  / ___| | |_ _|
 / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | |  | |
 / ___ \| | | | (_| | |_| | | (_| | |   | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_|   \____|_____|___|
        |___/

Angular CLI: 8.3.2
Node: 10.15.1
OS: darwin x64
Angular:
...

Package           Version

@angular-devkit/architect  0.803.2
@angular-devkit/core    8.3.2
@angular-devkit/schematics 8.3.2
@schematics/angular     8.3.2
@schematics/update     0.803.2
rxjs            6.4.0

Next, create an Angular 8 application for this Google Maps Firebase Realtime Blood Donor App by typing this command.

ng new angular-maps

Answer all questions like below which we will skip Angular Routing and use SCSS as a stylesheet.

? Would you like to add Angular routing? No
?
Which stylesheet format would you like to use? SCSS   [
https://sass-lang.com/documentation/syntax#scss        
        ]

Next, to sanitize the newly created Angular 8 project go to that project folder then run the Angular application.

cd ./angular-maps
ng serve

You will this page or view when you open "http://localhost:4200/" in your browser which means the Angular 8 is ready to go.


Install Google Firebase SDK

We will use Google Firebase Javascript SDK for accessing Firebase Realtime Database. For that, type this command to install the module.

npm install --save firebase

Next, register the Firebase SDK module in the Angular Maps app by open and edit this file src/app/app.component.ts then add these imports of Firebase.

import * as firebase from 'firebase';

Declare a constant variable for holds Firebase setting before @Component that contain the configuration variable for accessing Firebase using apiKey, authDomain, databaseURL, projectId, storageBucket.

 const config = {
 apiKey: 'YOUR_APIKEY',
 authDomain: 'YOUR_AUTH_DOMAIN',
 databaseURL: 'YOUR_DATABASE_URL',
 projectId: 'YOUR_PROJECT_ID',
 storageBucket: 'YOUR_STORAGE_BUCKET',
};

You can find or get those configuration parameters by click on the settings (gear button) -> Project Settings to get web API key and Project ID. Click Develop -> Authentication then scrolls to Authorized domain to get authDomain value. In order to get the value of storageBucket, you have to enable or start a Storage by going to Develop -> Storage. Next, initialize Firebase configuration settings inside Angular constructor.

constructor() {
 firebase.initializeApp(config);
}

Now, the Firebase realtime database is ready to use.

Create Angular Component and Routing

We will display the Google maps and add a new donor in the new Angular components. For that, generate component using Angular schematics.

ng g component donor-list
ng g component add-donor

Because in the first Angular creation not including the Routing, we have to add it manually. Open and edit src/app/app.module.ts then add this import of Angular RouterModule and Router.

import { RouterModule, Routes } from '@angular/router';

Add a constant variable after the imports for the routes to the new Angular component.

const appRoutes: Routes = [
 { path: 'donor-list', component: DonorListComponent },
 { path: 'add-donor/:lat/:lng', component: AddDonorComponent },
 { path: '',
  redirectTo: '/donor-list',
  pathMatch: 'full'
 }
];

Add the Angular RouterModule to the @NgModule imports.

imports: [
 ...
 RouterModule.forRoot(
  appRoutes
 )
],

As you see, the newly created components already registered in the Angular AppModule.

Display Google Maps

Now, we will display a Google Maps in the Angular component (donor-list) template using Google Maps Javascript SDK. Before that, we need to load the Google Maps Javascript SDK first by open and edit src/index.html then add this Script loader before the closing of </body> tag.

<body>
 <app-root></app-root>
  <script src="http://maps.googleapis.com/maps/api/js?key=AIzaSyCeUZ4BzsoVCSMnnGdNUQxDlIMSxEQhmLU"></script>
</body>

Next, open and edit src/app/donor-list/donor-list.component.html then replace all HTML tags with this tag that represent the Google Maps.

<div #map id="map"></div>

Open and edit src/app/donor-list/donor-list.component.ts then modify the @angular/core import to add ViewChild and ElementRef.

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';

Declare the basic required Google maps variables (map, marker, options) before the Angular @Component.

declare var google: any;
let map: any;
let marker: any;
const options = {
 enableHighAccuracy: true,
 timeout: 5000,
 maximumAge: 0
};
let infowindow: any;
const iconBase = 'http://maps.google.com/mapfiles/ms/icons/';

Load map element, after the title variable.

@ViewChild('map', {static: false}) mapElement: ElementRef;

Create a function to load Google Maps and set a default marker of the current browser location.

initMap() {
 navigator.geolocation.getCurrentPosition((location) => {
  map = new google.maps.Map(this.mapElement.nativeElement, {
   center: { lat: location.coords.latitude, lng: location.coords.longitude },
   zoom: 15
  });

  infowindow = new google.maps.InfoWindow();

  marker = new google.maps.Marker({
   position: { lat: location.coords.latitude, lng: location.coords.longitude },
   map,
   title: 'Click to zoom',
   icon: iconBase + 'blue-dot.png'
  });

  map.addListener('center_changed', () => {
   window.setTimeout(() => {
    map.panTo(marker.getPosition());
   }, 3000);
  });

  marker.addListener('click', (event: any) => {
   infowindow.setPosition(event.latLng);
   infowindow.setContent('<h2>Yes, I wanna be a donor!</h2>' +
   '<h3><a href="/add-donor/' + marker.getPosition().lat() + '/' + marker.getPosition().lng() + '">Register Here</a></h3>');
   infowindow.open(map, marker);
  });
 }, (error) => {
  console.log(error);
 }, options);
}

Call that function inside Angular constructor.

constructor() {
 this.initMap();
}

Now, the Google maps are displayed in the Angular page and ready to populate with a lot of markers that load from the Firebase realtime database. The current user location will be marked as a blue-dot icon.

Get Donor List from Firebase Realtime Database

Let's get back to the Firebase Realtime Database. We will get the list of Donor data from the Firebase Realtime Database. Still on src/app/donor-list/donor-list.component.ts then add this import of the Firebase.

import * as firebase from 'firebase';

Next, add this export constant function before the Angular @Component to extract the Firebase Realtime Database response to the Angular array.

export const snapshotToArray = (snapshot: any) => {
 const returnArr = [];

 snapshot.forEach((childSnapshot: any) => {
   const item = childSnapshot.val();
   item.key = childSnapshot.key;
   returnArr.push(item);
 });

 return returnArr;
};

Add the variables after @ViewChild variable to holds response data from Firebase realtime database.

donors = [];

Create a constructor after the variables to implement the Firebase realtime database. So, the data from Firebase automatically fetched on every value changes in Firebase realtime database side.

constructor() {
 firebase.database().ref('donors/').on('value', resp => {
  this.donors = [];
  this.donors = snapshotToArray(resp);
 });
 this.initMap();
}

The received data should contain the name, phone, email, and the coords.

Display Donor Position to Google Maps

After we have got donors data from the Firebase realtime database, we will display that data to Google Maps as the markers. Still, on src/app/donor-list/donor-list.component.ts iterate the donors' array to set the marker of the donors to the Google Maps.

constructor() {
 firebase.database().ref('donors/').on('value', resp => {
  this.donors = [];
  this.donors = snapshotToArray(resp);
  for (const donor of this.donors) {
   this.createMarkers(donor);
  }
 });
 this.initMap();
}

Add a new function to create markers that iterated from the donors' array.

createMarkers(place: any) {
 const latitude = parseFloat(place.coords.latitude);
 const longitude = parseFloat(place.coords.longitude);
 const donorMarker = new google.maps.Marker({
  map,
  position: { lat: latitude, lng: longitude },
  icon: iconBase + 'green-dot.png'
 });

 google.maps.event.addListener(donorMarker, 'click', function() {
  infowindow.setContent('<h3>' + place.name + '</h3><p>Phone number: ' + place.phone + '<br>Email: ' + place.email + '</p>');
  infowindow.open(map, this);
 });
}

Now, we have displayed the donors as green-dot markers.

Add a New Donor Data using Angular Material

Next, we will use another newly created Angular component for add new donor data. For add data form, we will use Angular Material and Reactive Form. For that, add Angular Material to this project using schematics.

ng add @angular/material

Open and edit src/app/app.module.ts then add these imports of BrowserAnimationModule, the required Angular Material components, and Angular ReactiveForm.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
 MatInputModule,
 MatPaginatorModule,
 MatProgressSpinnerModule,
 MatSortModule,
 MatTableModule,
 MatIconModule,
 MatButtonModule,
 MatCardModule,
 MatFormFieldModule } from '@angular/material';

Add them into @NgModule imports.

imports: [
 ...
 FormsModule,
 ReactiveFormsModule,
 BrowserAnimationsModule,
 MatInputModule,
 MatTableModule,
 MatPaginatorModule,
 MatSortModule,
 MatProgressSpinnerModule,
 MatIconModule,
 MatButtonModule,
 MatCardModule,
 MatFormFieldModule,
 ...
],

Next, open and edit src/app/add-donor/add-donor.component.ts then add these imports of Angular ActivatedRoute, Router, FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators, ErrorStateMatcher, and Firebase.

import { ActivatedRoute, Router } from '@angular/router';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
import * as firebase from 'firebase';

Add a new export class for handling the error of each FormControl after the imports.

export class MyErrorStateMatcher implements ErrorStateMatcher {
 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
  const isSubmitted = form && form.submitted;
  return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
 }
}

Add the required variables of donor FormGroup, name, phone, email, lat, lng, and Firebase database reference.

donorForm: FormGroup;
name: string='';
phone: string='';
email: string='';
lat = '';
lng = '';
ref = firebase.database().ref('donors/');

Inject the previously imported modules to the constructor and fill the constructor body with data from the previous page and set to LAT and LNG variables.

constructor(private route: ActivatedRoute, private router: Router, private formBuilder: FormBuilder) {
 this.lat = this.route.snapshot.paramMap.get('lat');
 this.lng = this.route.snapshot.paramMap.get('lng');
}

Initialize each of the FormControl in the FormGroup including the validations inside NgOnInit function.

ngOnInit() {
 this.donorForm = this.formBuilder.group({
  'name' : [null, Validators.required],
  'phone' : [null, Validators.required],
  'email' : [null, Validators.required]
 });
}

Add a function for handling the submitted form than send the form controls values to the Firebase Realtime Database.

onFormSubmit(form: any) {
 const donor = form;
 donor.coords = { latitude: this.lat, longitude: this.lng };
 const newDonor = firebase.database().ref('donors/').push();
 newDonor.set(donor);
 this.router.navigate(['/']);
}

After form submitted and data sent to Firebase realtime database, it will return to the previous Google Maps page.

Run and Test Angular 8 Google Maps Firebase Realtime Blood Donor App

To run this Angular 8 Google Maps Firebase Realtime Blood Donor App, simply type this Angular command.

ng serve

Now, you can go to localhost:4200 using your browser then you see the maps. For demo purpose, you use a single browser then change the geolocation of the browser.

That it's, the Angular 8 Google Maps Firebase Realtime Blood Donor App. You can get the full source code from our GitHub.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Best 50 Angular Interview Questions for Frontend Developers in 2019

How to build a CRUD Web App with Angular 8.0

React vs Angular: An In-depth Comparison

React vs Angular vs Vue.js by Example

Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)




Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Hire Now: https://bit.ly/394wdOx

Angular 8 Google Maps Tutorial with Example

Angular 8 Google Maps Tutorial with Example

In this article, you'll learn how to integrate Google Maps in Your Angular 8 Application using this example. In this Angular Google Maps integration tutorial, we would love to share each things step by step with example.

In this article, you'll learn how to integrate Google Maps in Your Angular 8 Application using this example. In this Angular Google Maps integration tutorial, we would love to share each things step by step with example.

Before start integrating of Google maps in your Angular 8 Application example, We need a Angular 8 Project.

So, Let’s get started Angular 8 Google Maps

First, let’s add a placeholder for Google Map in this Angular 8 application example.

Open app.component.html file and put the below html markup

<div class="row mt-5">
    <div class="col-md-9 mx-auto">
        <h2 class="text-left">Google Map</h2>
        <div class="card mt-3">
            <div class="card-body">
                <div #mapRef style="width:100%;height:400px"></div>
            </div>
        </div>
    </div>
</div>

Next, Open the app.component.ts file and add the below code

@ViewChild('mapRef', {static: true }) mapElement: ElementRef;

Access <div #mapRef>: mapElement is a reference to <div #mapRef> inside app.component.html file. ViewChild directive creates a direct link between

element and a mapElement member variable.

Loading the Maps JavaScript API

We are going to load the Maps JavaScript** **API in the app.module.tsfile. If you don’t want to load it globally. You can create a new component and load it.

So, open the app.component.ts file and put the below two methods on this

renderMap() {
    
  window['initMap'] = () => {
    this.loadMap();     
  }
  if(!window.document.getElementById('google-map-script')) {
    var s = window.document.createElement("script");
    s.id = "google-map-script";
    s.type = "text/javascript";
    s.src = "https://maps.googleapis.com/maps/api/js?key=AIzaSyDRmqZ-1VD-DbsccElMGtMtlRz9FndbPB4&amp;callback=initMap";
      
    window.document.body.appendChild(s);
  } else {
    this.loadMap();
  }
}

loadMap = () => {
  var map = new window['google'].maps.Map(this.mapElement.nativeElement, {
    center: {lat: 24.5373, lng: 81.3042},
    zoom: 8
  });
  
  var marker = new window['google'].maps.Marker({
    position: {lat: 24.5373, lng: 81.3042},
    map: map,
    title: 'Hello World!',
    draggable: true,
    animation: window['google'].maps.Animation.DROP,
  });
  
  var contentString = '<div id="content">'+
  '<div id="siteNotice">'+
  '</div>'+
  '<h3 id="thirdHeading" class="thirdHeading">W3path.com</h3>'+
  '<div id="bodyContent">'+
  '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>'+
  '</div>'+
  '</div>';
  
  var infowindow = new window['google'].maps.InfoWindow({
    content: contentString
  });
  
    marker.addListener('click', function() {
      infowindow.open(map, marker);
    });
  
}

In order to do any kind of interaction with the Maps API, you need an **API **key from Google. Follow the instructions here to get that set up. After the setup complete, You need to replace YOUR_API_KEYwith google map key.

You may noticed that, we have load the Google Maps JavaScript CDN under the renderMap method.

The renderMap method will check that the Google Maps JavaScript CDN is already loaded or not. If not then it will load the Google Maps JavaScript CDN. If already loaded then will call the loadMapmethod.

The loadMap will draw the map.

Next, We will call the renderMap method under ngOnInit method. like this

ngOnInit() {
    this.renderMap();
}

Angular 8 will invoked the ngOnInit only once when the directive is instantiated.

After above changes our app.component.ts file will looks like this

import {AfterViewInit, Component, ElementRef, ViewChild, OnInit} from '@angular/core';
  
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  
  @ViewChild('mapRef', {static: true }) mapElement: ElementRef;
  
  constructor() {};
  
  ngOnInit() {   
    this.renderMap();
  }
  
  loadMap = () => {
    var map = new window['google'].maps.Map(this.mapElement.nativeElement, {
      center: {lat: 24.5373, lng: 81.3042},
      zoom: 8
    });
  
    var marker = new window['google'].maps.Marker({
      position: {lat: 24.5373, lng: 81.3042},
      map: map,
      title: 'Hello World!',
      draggable: true,
      animation: window['google'].maps.Animation.DROP,
    });
  
    var contentString = '<div id="content">'+
    '<div id="siteNotice">'+
    '</div>'+
    '<h3 id="thirdHeading" class="thirdHeading">W3path.com</h3>'+
    '<div id="bodyContent">'+
    '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>'+
    '</div>'+
    '</div>';
  
    var infowindow = new window['google'].maps.InfoWindow({
      content: contentString
    });
  
      marker.addListener('click', function() {
        infowindow.open(map, marker);
      });
  
  }
  renderMap() {
      
    window['initMap'] = () => {
      this.loadMap();     
    }
    if(!window.document.getElementById('google-map-script')) {
      var s = window.document.createElement("script");
      s.id = "google-map-script";
      s.type = "text/javascript";
      s.src = "https://maps.googleapis.com/maps/api/js?key=AIzaSyDRmqZ-1VD-DbsccElMGtMtlRz9FndbPB4&amp;callback=initMap";
        
      window.document.body.appendChild(s);
    } else {
      this.loadMap();
    }
  }
  
}

Running application

Run application using ng serve –o and you should see Google Maps inside browser. Congrats!! See, it was easy.

Getting started with Angular Google Maps component

Getting started with Angular Google Maps component

The new Angular Component release introduces the second official @angular/component component, a Google Maps component. In this post, we take a look at getting started with the Angular Google Maps component.

The pearl-lullaby (v9.0.0-rc.0) introduces the second official @angular/component component, a Google Maps component. In this post, we take a look at getting started with the Google Maps component.

Earlier this year, we changed the name of this repo to “angular/components” to emphasize our goal to provide more than Material Design components. The 9.0.0 release includes one of the next new features in that regard- a new package that wraps the Google Maps JavaScript API in an easy-to-use Angular component.

I’m really looking forward to the team broadening up the repository to create components. We already saw a [YouTube Player](https://github.com/angular/components/tree/master/src/youtube-player) component in v8.2.0, which is explored in Craig's article.

These new integrations with existing JavaScript APIs are making it easier for us to do our jobs, and I’m curious about the other new components that will be released in the upcoming versions!

Setup

Angular

The Google Maps module can be installed from @angular/google-maps.

npm install @angular/google-maps

When the installation is finished, we must add the Angular module GoogleMapsModule to the import declaration.

import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { GoogleMapsModule } from '@angular/google-maps'
import { AppComponent } from './app.component'
@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, GoogleMapsModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

The GoogleMapsModule exports three components that we can use:

  • GoogleMap: this is the wrapper around Google Maps, available via the google-map selector
  • MapMarker: used to add markers on the map, available via the map-marker selector
  • MapInfoWindow: the info window of a marker, available via the map-info-window selector

Loading the Maps JavaScript API

We also have to import the Maps API, this can be done by adding a script tag in the index.html file.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Map</title>
    <base href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>

To use the map in a production environment you will need to create a new API Key, follow the documentation to create a new key.

Usage

GoogleMap

By adding the Google Maps component to a template we can already see and use the Google Map. The map will behave as a default map with the default functionality, for example, you can zoom in and out, and drag in the map.

<google-map></google-map>

Input properties

We can customize the styling of the default map by using the @Input() properties. The most commonly used properties are added as @Input properties, we can set the size of the map, set the center, and set the zoom level.

| Property  | Description                                                                                                                          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `height`  | Sets the initial height                                                                                                              |
| `width`   | Sets the initial width                                                                                                               |
| `center`  | Sets the initial center                                                                                                              |
| `zoom`    | Sets the initial zoom                                                                                                                |
| `options` | Sets the options, for more info see the [docs](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions) |

To make full use of the Google Maps API, we can also use the options property. Using the explicit properties wins over using the options property. The options property has the same has the interface as the [Map Options interface](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions).

export class AppComponent implements OnInit {
  zoom = 12
  center: google.maps.LatLngLiteral
  options: google.maps.MapOptions = {
    mapTypeId: 'hybrid',
    zoomControl: false,
    scrollwheel: false,
    disableDoubleClickZoom: true,
    maxZoom: 15,
    minZoom: 8,
  }

  ngOnInit() {
    navigator.geolocation.getCurrentPosition(position => {
      this.center = {
        lat: position.coords.latitude,
        lng: position.coords.longitude,
      }
    })
  }

  zoomIn() {
    if (this.zoom < this.options.maxZoom) this.zoom++
  }

  zoomOut() {
    if (this.zoom > this.options.minZoom) this.zoom--
  }
}

Output properties

The GoogleMap component exposes all the Google Maps API events as @Output() properties:

| Property            | JavaScript API Method | Description                                                                                                                 |
| ------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `boundsChanged`     | `bounds_changed`      | This event is fired when the viewport bounds have changed                                                                   |
| `centerChanged`     | `center_changed`      | This event is fired when the map center property changes                                                                    |
| `mapClick`          | `click`               | This event is fired when the user clicks on the map                                                                         |
| `mapDblclick`       | `dblclick`            | This event is fired when the user double-clicks on the map. Note that the click event will also fire, right before this one |
| `mapDrag`           | `drag`                | This event is repeatedly fired while the user drags the map                                                                 |
| `mapDragend`        | `dragend`             | This event is fired when the user stops dragging the map                                                                    |
| `mapDragstart`      | `dragstart`           | This event is fired when the user starts dragging the map                                                                   |
| `headingChanged`    | `heading_changed`     | This event is fired when the map heading property changes                                                                   |
| `idle`              | `idle`                | This event is fired when the map becomes idle after panning or zooming                                                      |
| `maptypeidChanged`  | `maptypeid_changed`   | This event is fired when the mapTypeId property changes                                                                     |
| `mapMousemove`      | `mousemove`           | This event is fired whenever the user's mouse moves over the map container                                                  |
| `mapMouseout`       | `mouseout`            | This event is fired when the user's mouse exits the map container                                                           |
| `mapMouseover`      | `mouseover`           | This event is fired when the user's mouse enters the map container                                                          |
| `projectionChanged` | `projection_changed`  | This event is fired when the projection has changed                                                                         |
| `mapRightclick`     | `rightclick`          | This event is fired when the DOM contextmenu event is fired on the map container                                            |
| `tilesloaded`       | `tilesloaded`         | This event is fired when the visible tiles have finished loading                                                            |
| `tiltChanged`       | `tilt_changed`        | This event is fired when the map tilt property changes                                                                      |
| `zoomChanged`       | `zoom_changed`        | This event is fired when the map zoom property changes                                                                      |

Covering all of these events would be a lot for one post, in this post we’ll go over the click() event. If you're interested in all the events I refer you to the Google Maps API Docs for the complete list, and the Angular implementation in the source code.

<google-map (mapClick)="click($event)"></google-map>
export class AppComponent implements OnInit {
  click(event: google.maps.MouseEvent) {
    console.log(event)
  }
}

Methods and getters

If we keep a reference to the map component, by using the @ViewChild decorator, we can also use the following methods and getters.

| Function            | Description                                                                                                      |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `fitBounds`         | Sets the viewport to contain the given bounds                                                                    |
| `panBy`             | Changes the center of the map by the given distance in pixels                                                    |
| `panTo`             | Changes the center of the map to the given LatLng                                                                |
| `panToBounds`       | Pans the map by the minimum amount necessary to contain the given LatLngBounds                                   |
| `getBounds`         | Returns the lat/lng bounds of the current viewport                                                               |
| `getCenter`         | Returns the position displayed at the center of the map                                                          |
| `getClickableIcons` | Returns the clickability of the map icons                                                                        |
| `getHeading`        | Returns the compass heading of aerial imagery                                                                    |
| `getMapTypeId`      | Returns the map type id                                                                                          |
| `getProjection`     | Returns the current Projection                                                                                   |
| `getStreetView`     | Returns the default StreetViewPanorama bound to the map, which may be a default panorama embedded within the map |
| `getTilt`           | Returns the current angle of incidence of the map, in degrees from the viewport plane to the map plane           |
| `getZoom`           | Returns the current zoom                                                                                         |
| `controls`          | Additional controls to attach to the map                                                                         |
| `data`              | An instance of Data, bound to the map                                                                            |
| `mapTypes`          | A registry of MapType instances by string ID                                                                     |
| `overlayMapTypes`   | Additional map types to overlay                                                                                  |

As an example, we can log the current center of the map.

export class AppComponent implements OnInit {
  @ViewChild(MapInfoWindow, { static: false }) info: MapInfoWindow

  logCenter() {
    console.log(JSON.stringify(this.map.getCenter()))
  }
}
MapMarker

Input properties

With the map in place, we can start adding markers. This is done by using the MapMarker component. To add a marker, make sure the marker is added inside the google-map tag otherwise it will not be displayed.

Just like the MapControl, the most frequently used options can be set directly with @Input() properties, but it's also possible to make use of the full options set of the MapMarker.

<google-map>
  <map-marker
    *ngFor="let marker of markers"
    [position]="marker.position"
    [label]="marker.label"
    [title]="marker.title"
    [options]="marker.options"
  >
  </map-marker>
</google-map>
export class AppComponent implements OnInit {
  addMarker() {
    this.markers.push({
      position: {
        lat: this.center.lat + ((Math.random() - 0.5) * 2) / 10,
        lng: this.center.lng + ((Math.random() - 0.5) * 2) / 10,
      },
      label: {
        color: 'red',
        text: 'Marker label ' + (this.markers.length + 1),
      },
      title: 'Marker title ' + (this.markers.length + 1),
      options: { animation: google.maps.Animation.BOUNCE },
    })
  }
}


The full specification of the marker:

| Property    | Description                                                                                                                                |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `title`     | Sets the title, visible on hover                                                                                                           |
| `position`  | Sets the position                                                                                                                          |
| `label`     | Sets the label                                                                                                                             |
| `clickable` | If the marker should listen to mouse and touch events, default is `true`                                                                   |
| `options`   | Sets the options, for more info see the [docs](https://developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions) |

Output properties

The MapMarker component also exposes the Google Maps API events as @Output() properties:

| Property           | JavaScript API Method | Description                                                           |
| ------------------ | --------------------- | --------------------------------------------------------------------- |
| `animationChanged` | `animation_changed`   | This event is fired when the marker's animation property changes      |
| `mapClick`         | `click`               | This event is fired when the marker icon was clicked                  |
| `clickableChanged` | `clickable_changed`   | This event is fired when the marker's cursor property changes         |
| `cursorChanged`    | `cursor_changed`      | This event is fired when the marker's cursor property changes         |
| `mapDblclick`      | `dblclick`            | This event is fired when the marker icon was double clicked           |
| `mapDrag`          | `drag`                | This event is repeatedly fired while the user drags the marker        |
| `mapDragend`       | `dragend`             | This event is fired when the user stops dragging the marker           |
| `draggableChanged` | `draggable_changed`   | This event is fired when the marker's draggable property changes      |
| `mapDragstart`     | `dragstart`           | This event is fired when the user starts dragging the marker          |
| `flatChanged`      | `flat_changed`        | This event is fired when the marker's flat property changes           |
| `iconChanged`      | `icon_changed`        | This event is fired when the marker icon property changes             |
| `mapMousedown`     | `mousedown`           | This event is fired for a mousedown on the marker                     |
| `mapMouseout`      | `mouseout`            | This event is fired when the mouse leaves the area of the marker icon |
| `mapMouseover`     | `mouseover`           | This event is fired when the mouse enters the area of the marker icon |
| `mapMouseup`       | `mouseup`             | This event is fired for a mouseup on the marker                       |
| `positionChanged`  | `position_changed`    | This event is fired when the marker position property changes         |
| `mapRightclick`    | `rightclick`          | This event is fired for a rightclick on the marker                    |
| `shapeChanged`     | `shape_changed`       | This event is fired when the marker's shape property changes          |
| `titleChanged`     | `title_changed`       | This event is fired when the marker title property changes            |
| `visibleChanged`   | `visible_changed`     | This event is fired when the marker's visible property changes        |
| `zindexChanged`    | `zindex_changed`      | This event is fired when the marker's zIndex property changes         |

The full reference to the API can be found at the Google Maps API Docs, and the Angular implementation in the source code.

MapInfoWindow

The last component is MapInfoWindow, it can be used to open a pop-up window of a marker. To show the pop-up we have to add the component inside the google-map template.

<map-info-window>Hello Google Maps</map-info-window>

The above does nothing, to make it appear we have to open the info window when we click on a marker. We bind the mapClick() method to the marker and pass the marker reference to the openInfo method to open the info window.

<map-marker
  #markerElem
  *ngFor="let marker of markers"
  [position]="marker.position"
  [label]="marker.label"
  [title]="marker.title"
  [options]="marker.options"
  (mapClick)="openInfo(markerElem)"
>
</map-marker>

Finally, we also have to add a reference to theMapInfoWindow component inside our component, we can do this by using the @ViewChild decorator. By having the reference to the info window and the marker, we can open the info window by using the infoWindow.open() method.

export class AppComponent implements OnInit {
  @ViewChild(MapInfoWindow, { static: false }) infoWindow: MapInfoWindow

  openInfo(marker: MapMarker, content) {
    this.infoWindow.open(marker)
  }
}

Input properties

| Property  | Description                                                                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `options` | Sets the options, for more info see the [docs](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindowOptions) |

Output properties

| Property          | JavaScript API Method | Description                                                                                     |
| ----------------- | --------------------- | ----------------------------------------------------------------------------------------------- |
| `closeclick`      | `closeclick`          | This event is fired when the close button was clicked                                           |
| `contentChanged`  | `content_changed`     | This event is fired when the content property changes                                           |
| `domready`        | `domready`            | This event is fired when the `<div>` containing the InfoWindow's content is attached to the DOM |
| `positionChanged` | `position_changed`    | This event is fired when the position property changes                                          |
| `zindexChanged`   | `zindex_changed`      | This event is fired when the InfoWindow's zIndex changes                                        |

Methods and getters

By using the infoWindow property, that has a reference to the MapInfoWindow component we can make use of its following methods and getters:

| Property      | Description                                                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `close`       | Closes this InfoWindow by removing it from the DOM structure                                                                                                   |
| `getContent`  | Returns the content of the InfoWindow                                                                                                                          |
| `getPosition` | Returns the position of the InfoWindow                                                                                                                         |
| `getZIndex`   | Returns the z-index of the InfoWindow                                                                                                                          |
| `open`        | Opens the MapInfoWindow using the provided MapMarker as the anchor. If the anchor is not set, then the position property of the options input is used instead. |

Dynamic content

Having static content inside the info window is a bit dull. To provide dynamic content inside the info window, we can create a string property within the component, this looks as follows.

<map-info-window>{{ infoContent }}</map-info-window>
export class AppComponent implements OnInit {
  @ViewChild(MapInfoWindow, { static: false }) infoWindow: MapInfoWindow
  infoContent = ''

  openInfo(marker: MapMarker, content) {
    this.infoContent = content
    this.infoWindow.open(marker)
  }
}

The full reference to the API can be found at the Google Maps API Docs, and the Angular implementation in the source code.

Putting it all together

The upcoming version of Angular brings us a whole range of positive changes and new possibilities. We were already looking forward to the Ivy release in Angular v9, but now we’ll also look forward to the new release of @angular/components.

Besides the new MapComponent there will also be an integration with the Clipboard API within the Angular CDK, as explored in Use the new Angular Clipboard CDK to interact with the clipboard.

The Angular component is very new, that’s why the documentation and example code is minimal. Because the Angular implementation follows the Google Maps API specs, we can take a look at the rich documentation from the JavaScript API.

See the code below for the full explored example of this post.

<google-map
  height="500px"
  width="100%"
  [zoom]="zoom"
  [center]="center"
  [options]="options"
  (mapClick)="click($event)"
>
  <map-marker
    #markerElem
    *ngFor="let marker of markers"
    [position]="marker.position"
    [label]="marker.label"
    [title]="marker.title"
    [options]="marker.options"
    (mapClick)="openInfo(markerElem, marker.info)"
  >
  </map-marker>

  <map-info-window>{{ infoContent }}</map-info-window>
</google-map>

<button (click)="zoomIn()">Zoom in</button>
<button (click)="zoomOut()">Zoom out</button>
<button (click)="logCenter()">Log center</button>
<button (click)="addMarker()">Add marker</button>
import { Component, OnInit, ViewChild } from '@angular/core'
import { MapInfoWindow, MapMarker, GoogleMap } from '@angular/google-maps'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  @ViewChild(GoogleMap, { static: false }) map: GoogleMap
  @ViewChild(MapInfoWindow, { static: false }) info: MapInfoWindow

  zoom = 12
  center: google.maps.LatLngLiteral
  options: google.maps.MapOptions = {
    zoomControl: false,
    scrollwheel: false,
    disableDoubleClickZoom: true,
    mapTypeId: 'hybrid',
    maxZoom: 15,
    minZoom: 8,
  }
  markers = []
  infoContent = ''

  ngOnInit() {
    navigator.geolocation.getCurrentPosition(position => {
      this.center = {
        lat: position.coords.latitude,
        lng: position.coords.longitude,
      }
    })
  }

  zoomIn() {
    if (this.zoom < this.options.maxZoom) this.zoom++
  }

  zoomOut() {
    if (this.zoom > this.options.minZoom) this.zoom--
  }

  click(event: google.maps.MouseEvent) {
    console.log(event)
  }

  logCenter() {
    console.log(JSON.stringify(this.map.getCenter()))
  }

  addMarker() {
    this.markers.push({
      position: {
        lat: this.center.lat + ((Math.random() - 0.5) * 2) / 10,
        lng: this.center.lng + ((Math.random() - 0.5) * 2) / 10,
      },
      label: {
        color: 'red',
        text: 'Marker label ' + (this.markers.length + 1),
      },
      title: 'Marker title ' + (this.markers.length + 1),
      info: 'Marker info ' + (this.markers.length + 1),
      options: {
        animation: google.maps.Animation.BOUNCE,
      },
    })
  }

  openInfo(marker: MapMarker, content) {
    this.infoContent = content
    this.info.open(marker)
  }
}