React Apps with the Google Maps API and google-maps-react

React Apps with the Google Maps API and google-maps-react

This tutorial aims at integrating the google maps API to your React components and enabling you to display maps on your website.

This tutorial aims at integrating the google maps API to your React components and enabling you to display maps on your website.

Introduction to Google Maps API.

At some point of our lives, we’ve all gotten a chance to use and interact with the google maps, either through finding directions, viewing our current location, ordering cabs or estimating distance and time from one point to another.

Table of Contents

Including the Google maps API in a React App is actually far much easier than expected due to its rich documentation which I highly suggest that you look into, and the npm package by Fullstack React.

Prerequisite:

Go on ahead and grab your API key here.

  1. Simply click on the Get Started button
  2. Tick on the maps checkbox
  3. Click Create A New Project
  4. Once you’ve named your project (whatever you want) you can set up your billing info which will automatically enable your the API and generate the API key.

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.

Project Setup

For some quick setup, we are going to use facebook’s create-react-app which saves us the hassle of having to configure webpack or babel.

So go on ahead and run this command

npm i -g create-react-app
create-react-app my-googlemap
cd my-googlemap

Before we add any code, let’s go ahead and install our dependency.

npm install --save google-maps-react

Let’s go on and edit our src folder and remove files and imports that we do not need ie

  1. Simply click on the Get Started button
  2. Tick on the maps checkbox
  3. Click Create A New Project
  4. Once you’ve named your project (whatever you want) you can set up your billing info which will automatically enable your the API and generate the API key.

Time to create our component.

We will need to edit our App.js file and instead have our component that will load our google map.

import React, { Component } from 'react';
import { Map, GoogleApiWrapper } from 'google-maps-react';

const mapStyles = {
  width: '100%',
  height: '100%'
};

export class MapContainer extends Component {
  render() {
    return (
      <Map
        google={this.props.google}
        zoom={14}
        style={mapStyles}
        initialCenter={{
         lat: -1.2884,
         lng: 36.8233
        }}
      />
    );
  }
}

export default GoogleApiWrapper({
  apiKey: 'YOUR_GOOGLE_API_KEY_GOES_HERE'
})(MapContainer);

For a simple Google Map, this is literally all the code you need. Go ahead and run your app and ensure that the map loads to the browser.

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.
The GoogleApiWrapper is simply a Higher Order Component(HOC) that provides wrapper around Google APIs. Alternatively, the GoogleApiWrapper HOC can be configured by passing a function that will be called with the wrapped component’s props and should return the configuration object like so.

export default GoogleApiWrapper(
  (props) => ({
    apiKey: props.apiKey
  }
))(MapContainer)

Markers and infoWindow

Wouldn’t it be exciting to have a Marker and an infoWindow on our map showing our set initialCenter position? So let’s add this functionality to our code.

First we need to import Marker and infoWindow components from the google-maps-react library inorder to help us achieve loading of the two.

import { GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';

Notice that our component before was stateless?, We need to add state for state management.

[...]

export class MapContainer extends Component {
   state = {
    showingInfoWindow: false,  //Hides or the shows the infoWindow
    activeMarker: {},          //Shows the active marker upon click
    selectedPlace: {}          //Shows the infoWindow to the selected place upon a marker
  };

Next, we need to add event handlers for when the map and the marker are clicked.

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onClose = props => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      });
    }
  };

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.* Prerequisite:

Let’s complete our component by adding our Marker and InfoWindow components to our render method

  render() {
    return (
      <Map
        google={this.props.google}
        zoom={14}
        style={style}
        initialCenter={{ lat: -1.2884, lng: 36.8233 }
      >
        <Marker
          onClick={this.onMarkerClick}
          name={'Kenyatta International Convention Centre'}
        />
        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          onClose={this.onClose}
        >
          <div>
            <h4>{this.state.selectedPlace.name}</h4>
          </div>
        </InfoWindow>
      </Map>
    );
  }
}

[...]

Run your app and ensure you have the one marker with the infoWindow upon click.

Browser’s current location

Let’s spice things up by having our map pick our browser’s current location. We will be using navigator which is a read-only property that returns a Geolocation object that gives Web content access to the location of the device.

In our src folder create a new file and name it Map.js and create a component named CurrentLocation this is where all our functionality to pick our browser’s location will lie.

We will begin by adding some default props to our CurrentLocation component, since we will need to set the map with a center incase the current location is not provided.This is handled by the boolean prop centerAroundCurrentLocation

import React from 'react';
import ReactDOM from 'react-dom';

const mapStyles = {
  map: {
    position: 'absolute',
    width: '100%',
    height: '100%'
  }
};

export class CurrentLocation extends React.Component {

[...]

}
export default CurrentLocation;

CurrentLocation.defaultProps = {
  zoom: 14,
  initialCenter: {
    lat: -1.2884,
    lng: 36.8233
  },
  centerAroundCurrentLocation: false,
  visible: true
};

Next, we need to make our component stateful,

[...]

export class CurrentLocation extends React.Component {
  constructor(props) {
    super(props);

    const { lat, lng } = this.props.initialCenter;
    this.state = {
      currentLocation: {
        lat: lat,
        lng: lng
      }
    };
  }

[...]

}

Let’s also update our CurrentLocation component to cater for the instance when the Map is first loaded as we cannot solely depend upon the google API being always available, hence we need to check if it’s loaded. And also check if the browser’s current location is provided and recenter the map to it.

[...]

componentDidUpdate(prevProps, prevState) {
    if (prevProps.google !== this.props.google) {
      this.loadMap();
    }
    if (prevState.currentLocation !== this.state.currentLocation) {
      this.recenterMap();
    }
  }

[...]

Let’s define the recenterMap() function which only gets called when the currentLocation in the component’s state is updated and uses the .panTo() method on the google.maps.Map instance to change the center of the map.

[...]

 recenterMap() {
    const map = this.map;
    const current = this.state.currentLocation;

    const google = this.props.google;
    const maps = google.maps;

    if (map) {
      let center = new maps.LatLng(current.lat, current.lng);
      map.panTo(center);
    }
  }

[...]

Next, we need to handle the instance when the map has already loaded.This will be handled by the componentDidMount() Lifecycle method which will set a call back to fetch the current location.

[...]

 componentDidMount() {
    if (this.props.centerAroundCurrentLocation) {
      if (navigator && navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(pos => {
          const coords = pos.coords;
          this.setState({
            currentLocation: {
              lat: coords.latitude,
              lng: coords.longitude
            }
          });
        });
      }
    }
    this.loadMap();
  }

[...]

Notice the loadMap() function? Let’s go on ahead and define it.

[...]

loadMap() {
    if (this.props && this.props.google) {
      // checks if google is available
      const { google } = this.props;
      const maps = google.maps;

      const mapRef = this.refs.map;

      // reference to the actual DOM element
      const node = ReactDOM.findDOMNode(mapRef);

      let { zoom } = this.props;
      const { lat, lng } = this.state.currentLocation;
      const center = new maps.LatLng(lat, lng);
      const mapConfig = Object.assign(
        {},
        {
          center: center,
          zoom: zoom
        }
      );

      // maps.Map() is constructor that instantiates the map
      this.map = new maps.Map(node, mapConfig);
    }
  }

Basically, the loadMap() function is only called after the component has been rendered and grabs a reference to the DOM component to where we want our map to be placed.

Our CurrentLocation component is almost looking up, But we need to ensure that our previous Marker picks our currenct location ie the browsers current location and so we need to introduce Parent-Child concept through the renderChildren() method which will be responsible for actually calling the method on the child component.

Read more about Parent-Child communication here

[...]

 renderChildren() {
    const { children } = this.props;

    if (!children) return;

    return React.Children.map(children, c => {
      if (!c) return;
      return React.cloneElement(c, {
        map: this.map,
        google: this.props.google,
        mapCenter: this.state.currentLocation
      });
    });
  }

[...]

And finally, let’s add our render() method

[...]

render() {
     const style = Object.assign({}, mapStyles.map);
    return (
      <div>
        <div style={style} ref="map">
          Loading map...
        </div>
        {this.renderChildren()}
      </div>
    );
  }

[...]

Lastly, before we wind up we need to update our MapContainer component to include our new changes. So let’s change it to this

import React, { Component } from 'react';
import { GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';

import CurrentLocation from './Map';

export class MapContainer extends Component {
  state = {
    showingInfoWindow: false,
    activeMarker: {},
    selectedPlace: {}
  };

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onClose = props => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      });
    }
  };

  render() {
    return (
      <CurrentLocation
        centerAroundCurrentLocation
        google={this.props.google}
      >
        <Marker onClick={this.onMarkerClick} name={'current location'} />
        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          onClose={this.onClose}
        >
          <div>
            <h4>{this.state.selectedPlace.name}</h4>
          </div>
        </InfoWindow>
      </CurrentLocation>
    );
  }
}

export default GoogleApiWrapper({
  apiKey: 'YOUR_GOOGLE_API_KEY_GOES_HERE'
})(MapContainer);

Run your app; heading over to our browser, our map should first load with our initialCenter then reload to pick our browser’s current location with the marker positioned to this location and Voilà we are done.

Conclusion

In this article we’ve been able to load our google maps React component, add a marker and have an infoWindow onto it and also have the map pick our current location. While we did not dive into adding more functionalities to our map such as, having polylines and polygons or adding event listeners, I highly recommend that you look into them

Learn More

Full Stack Developers: Everything You Need to Know

React Hooks Tutorial for Beginners: Getting Started With React Hooks

Learn React.js for Beginners

Learn React - React Crash Course 2019 - React Tutorial with Examples

React Router: Add the Power of Navigation

React - The Complete Guide (incl Hooks, React Router, Redux)

Modern React with Redux [2019 Update]

React Native - The Practical Guide

How to use Google Maps in Angular?

How to use Google Maps in Angular?

Learn the easiest way to use Google Maps in Angular

Google maps meet Angular. Having such a strong developer community and being used by thousands of developers around the world made Angular ideal for bringing most tools to the framework. bringing Google maps to Angular is one of them, it solved many problems for the Angular users. Manually adding Google maps to Angular is very difficult, so some people took it up upon themselves to make adding GMaps to your apps as easy as importing a module.

In this post, we will be looking at integrating Google Maps in your Angular application

The AGM library

This is the main library we will be using. The main library in the AGM is the @agm/core library. @agm/core contains solutions for the Google Maps JavaScript Core API.

To use this, we will first scaffold an Angular project:

ng new ng-maps

The above command will create a new Angular project.

Now, we install the @agm/core library:

npm i @agm/core

We need a component where we can display the map, to do that we scaffold a component:

g c map

This command will create map.component.ts in src/app/map/ folder.

We need to import the AgmModule in our AppModule:

...
import { AgmCoreModule } from '@agm/core';

@NgModule({
    declarations: [
        AppComponent,
        MapComponent
    ],
    imports: [
        BrowserModule,
        AgmCoreModule.forRoot({
            apiKey: environment.keys.gmap,
            libraries: ["places", "geometry"]
            /* apiKey is required, unless you are a premium customer, in which case you can use clientId */
        })
    ],
    providers: [
        ...
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

See, the AgmModule is imported and placed in the imports section. We need a GMaps key, to get it go to console.developers.google.com and follow the instructions.

After successfully obtaining the key, you can paste it here

...
        AgmCoreModule.forRoot({
            apiKey: "",
            libraries: ["places", "geometry"]
            /* apiKey is required, unless you are a premium customer, in which case you can use clientId */
        })
...

I added mine to the environment.ts file. For simplicity just add it to the apiKey property above.

We are done now. To display the map, we open up our map.component.ts and paste this:

<!-- map.component.html -->
<agm-map></agm-map>
// map.component.ts
@Component({
    ...
})
export class MapComponent {}

This will render the Google Map on our MapComponent.

Run ng serve, we will see it on our browser.

The agm-map selector tag is used to render the Google Maps on our components. It has inputs and outputs which we will see in the below sections.

Display a Selected Latitude and Longitude 🌍

The above code does nothing just displays a Map. Let’s make it display a selected location from a provided latitude and longitude position.

Remember, latitude and longitude is what is used to locate a position on a map, It is triangulated in the x-y plane. The latitude is in the x-plane while longitude is in the y-plane.

So when given a latitude and longitude, we trace from the x-axis and the y-axis, till they meet. The intersection is the location/position of the object or place.

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude"></agm-map>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785
        }
    }
}

interface Location {
    latitude: number;
    longitude: number
}
Map Type 🌍

Google Maps have options on how we want the map view displayed.

street view : This will show the streets on an eye-level, we can navigate/walk the streets with the arrow buttons

satellite: Displays the amp as seen by a satellite.

normal: The default map showed us by Google.

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType"></agm-map>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite"
        }
    }
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
}
Zooming 🔎

agm-map has an option to zoom in or out in the map view. We use the [zoom] input to accomplish that.

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom"></agm-map>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5
        }
    }
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
}

We set the zoom depth to 5.

This has zoom levels:

  • 1: World
  • 5: Landmass/continent
  • 10: City
  • 15: Streets
  • 20: Buildings

Specifying a zoom level of 0-4 will show the map of the entire Earth. 5-9 will show the map in the continent at a higher resolution more than 0-5. Zoom level from 11 - 14 will show the cities in the map location at a higher resolution than the previous zoom level. 15-19 will show the streets and 20-~ will show the buildings at an eye level.

So our above example will show the location on the map on the landmass/continent level.

Placing a marker 📌

To place an indicator/marker📍 on the map to show the precise location, we will use the agm-marker. Markers are like pins on the map.

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker [latitude]="location.marker.lat" [longitude]="location.marker.lng"></agm-marker>
</agm-map>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5,
            marker: {
                lat: -28.68352,
                lng: -147.20785
            }
        }
    }
}

interface Marker {
    lat: number;
    lng: number;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    marker: Marker;
}

A marker/pin will appear at the precise location of (-28.68352,-147.20785).

Adding a marker 📌
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker *ngFor="let marker of location.markers" [latitude]="marker.lat" [longitude]="marker.lng"></agm-marker>
</agm-map>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5,
            markers: [
                {
                    lat: -28.68352,
                    lng: -147.20785
                }
            ]
        }
    }

    addMarker(lat: number, lng: number) {
        this.location.markers.push({
            lat,
            lng
        })
    }
}

interface Marker {
    lat: number;
    lng: number;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    markers: Array<Marker>;
}

In this example, when a user clicks the map, it adds a new marker to the map. We hold the markers in an array, the agm-marker iterates over the array and places them on the map with a pin/marker. When we click on the map, a new marker is pushed to the markers array. A pin/marker appears over the position clicked.

Selecting a marker
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker *ngFor="let marker of location.markers" [latitude]="marker.lat" [longitude]="marker.lng" (markerClick)="selectMarker($event)"></agm-marker>
</agm-map>
<div>
    Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location
    selectedMarker: Marker

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5,
            markers: [
                {
                    lat: -28.68352,
                    lng: -147.20785
                }
            ]
        }
    }

    addMarker(lat: number, lng: number) {
        this.location.markers.push({
            lat,
            lng
        })
    }

    selectMarker(event) {
        this.selectedMarker = {
            lat: event.latitude,
            lng: event.longitude
        }
    }
}

interface Marker {
    lat: number;
    lng: number;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    markers: Array<Marker>;
}
Dragging marker

We can click, hold and drag a marker around.

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker 
    *ngFor="let marker of location.markers" [latitude]="marker.lat" 
    [longitude]="marker.lng" 
    (markerClick)="selectMarker($event)"     [markerDraggable]="true"
    (dragEnd)="markerDragEnd(coords, $event)"
></agm-marker>
</agm-map>
<div>
    Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location
    selectedMarker: Marker

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5,
            markers: [
                {
                    lat: -28.68352,
                    lng: -147.20785
                }
            ]
        }
    }

    addMarker(lat: number, lng: number) {
        this.location.markers.push({
            lat,
            lng
        })
    }

    selectMarker(event) {
        this.selectedMarker = {
            lat: event.latitude,
            lng: event.longitude
        }
    }

    markerDragEnd(coords: any, $event: MouseEvent) {
        this.location.latitude = coords.latitude
        this.location.longitude = coodrs.longitude
    }
}

interface Marker {
    lat: number;
    lng: number;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    markers: Array<Marker>;
}

We use the [markerDraggable]="true" to make the marker draggable. Then, we have the event (dragEnd)="markerDragEnd(coords, $event)", this event is fired when the user stops dragging the marker.

Labelling a marker

We can label a marker, i.e attach a name or description to a marker. It will be a name hovering above the marker, to identify the marker.

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker 
    *ngFor="let marker of location.markers" [latitude]="marker.lat" 
    [longitude]="marker.lng" 
    (markerClick)="selectMarker($event)"     [markerDraggable]="true"
    (dragEnd)="markerDragEnd(coords, $event)"
    [label]="marker.label"
></agm-marker>
</agm-map>
<div>
    Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
@Component({
    ...
})
export class MapComponent implements OnInit{
    location: Location
    selectedMarker: Marker

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5,
            markers: [
                {
                    lat: -28.68352,
                    lng: -147.20785,
                    label: "new york"
                }
            ]
        }
    }

    addMarker(lat: number, lng: number) {
        this.location.markers.push({
            lat,
            lng,
            label: Date.now().toLocaleString()
        })
    }
    ...
}

interface Marker {
    lat: number;
    lng: number;
    label: string;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    markers: Array<Marker>;
}

We used the [label] input to add label to the marker. We added a label property to the Marker interface, so we changed our code to include the new addition. Our first marker will display new york on top of the marker, others will have Date it was created on top of them.

Now, it would be nice if the names of the markers will be shown on their heads instead of dates they were created. We use geocoding.

We need to install googlemaps library:

npm i googlemaps

We import it in our MapComponent and declare a google var:

import {} from "googlemaps"
declare var google: any;
...

Then, our code:

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker 
    *ngFor="let marker of location.markers" [latitude]="marker.lat" 
    [longitude]="marker.lng" 
    (markerClick)="selectMarker($event)"     [markerDraggable]="true"
    (dragEnd)="markerDragEnd(coords, $event)"
    [label]="marker.label"
></agm-marker>
</agm-map>
<div>
    Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
...
@Component({
    ...
})
export class MapComponent implements OnInit {
    location: Location
    selectedMarker: Marker

    ngOnInit() {
        this.location = {
            latitude: -28.68352,
            longitude: -147.20785,
            mapType: "satelite",
            zoom: 5,
            markers: [
                {
                    lat: -28.68352,
                    lng: -147.20785,
                    label: this.getAddress(-28.68352, -147.20785)
                }
            ]
        }
    }

    addMarker(lat: number, lng: number) {
        this.location.markers.push({
            lat,
            lng,
            label: this.getAddress(lat, lng)
        })
    }

  getAddress(lat, lng) {
    const geocoder = new google.maps.Geocoder();
    var latlng = new google.maps.LatLng(lat, lng);
    const request: google.maps.GeocoderRequest = {
      location: latlng
    };
    geocoder.geocode(request, (results, status) => {
      this.ngZone.run(() => {
        const address = results[0].formatted_address;
        return address
      });
    });
  }    
    ...
}

interface Marker {
    lat: number;
    lng: number;
    label: string;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    markers: Array<Marker>;
}

The getAddress method uses the latitude and longitude passed to it to get the address of the place. See, we modified the addMarker method, so it calls the getAddress method to assign the marker address to the label property.

We constructed a geocoder object, then set a GeoCoder request with the lat and lng passed, finally, we called the geocode method in the geocoder, and in the function callback we got the address of the location.

Displaying our current position 🎌🌍

Here, let’s make it display our current position:

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker 
    *ngFor="let marker of location.markers" [latitude]="marker.lat" 
    [longitude]="marker.lng" 
    (markerClick)="selectMarker($event)"     [markerDraggable]="true"
    (dragEnd)="markerDragEnd(coords, $event)"
    [label]="marker.label"
></agm-marker>
</agm-map>
<div>
    Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
...
@Component({
    ...
})
export class MapComponent implements OnInit {
    location: Location
    selectedMarker: Marker

    ngOnInit() {
        this.setCurrentPosition()
    }

    addMarker(lat: number, lng: number) {
        this.location.markers.push({
            lat,
            lng,
            label: this.getAddress(lat, lng)
        })
    }
    setCurrentPosition() {
        if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(position => {
            const {latitude, longitude} = position
            this.location = {
                latitude,
                longitude,
                mapType: "satelite",
                zoom: 14,
                markers: [
                    {
                        lat: longitude,
                        lng: latitude,
                        label: "My current position"
                    }
                ]
            }
        });
        } else {
        alert("Geolocation is not supported by this browser, please use google chrome.");
        }
    }
    ...
}

interface Marker {
    lat: number;
    lng: number;
    label: string;
}

interface Location {
    latitude: number;
    longitude: number;
    mapType: ?string;
    zoom: ?number;
    markers: ?Array<Marker>;
}

Now, we have set the component to display our current position on the map when loaded. How did we do it? Simple, we used the navigator object. The navigator has a geolocation object which we use to call the getCurrentPosition() function, this function accepts a function callback which it calls with the location object. See our function callback will receive the location in the position parameter.

Setting direction

We can plot directions on a map. This we do with the help of agm-direction.

First, we install it:

npm i agm-direction

Next, we import the modules:

// app.module.ts
...
import { AgmDirectionModule } from 'agm-direction';

@NgModule({
  ...
  imports: [
    ...
    AgmDirectionModule
  ]
})
export class AppModule { }

So, now to plot direction from origin Taipei Main Station to destination Taiwan Presidential Office:

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <!-- ... -->
    <agm-direction 
        [origin]="origin" 
        [destination]="destination"
    ></agm-direction>
</agm-map>
<!-- ... -->
// map.component.ts
...
@Component({
    ...
})
export class MapComponent implements OnInit {
    ...
    origin: any;
    destination: any;

    ngOnInit() {
        this.origin = { 
            lat: 24.799448, 
            lng: 120.979021 
        };
        this.destination = { 
            lat: 24.799524, 
            lng: 120.975017 
        };
    }
    ...
}
...
Calculating distance between points

We can calculate the distance between points on the map.

To calculate the distance between Taipei Main Station and destination Taiwan Presidential Office, we do this:

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <!-- ... -->
    <agm-direction 
        [origin]="origin" 
        [destination]="destination"
    ></agm-direction>
</agm-map>
<div>
    Distance: {{distance}}
</div>
<!-- ... -->
// map.component.ts
...
@Component({
    ...
})
export class MapComponent implements OnInit {
    ...
    origin: any;
    destination: any;
    distance: Number;

    ngOnInit() {
        this.origin = { 
            lat: 24.799448, 
            lng: 120.979021 
        };
        this.destination = { 
            lat: 24.799524, 
            lng: 120.975017 
        };
        this.distance = this.calculatedistance(this.origin, this.destination)
    }

    // calculate the distances from point1 to point2
    calculateDistance(point1, point2) {
        const p1 = new google.maps.LatLng(
        point1.lat,
        point1.lng
        );
        const p2 = new google.maps.LatLng(
        point2.lat,
        point2.lng
        );
        return (
        google.maps.geometry.spherical.computeDistanceBetween(p1, p2)/1000
        ).toFixed(2);
    }
    ...
}
...

We have a method calculateDistance that computes the distance between two points passed to it. We construct LatLng objects of the two points and pass it to google.maps.geometry.spherical.computeDistanceBetween(...). This returns the distance between the two points.

Rendering info window

We can place an info window over a marker. This is how we do it:

<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <agm-marker 
    *ngFor="let marker of location.markers" [latitude]="marker.lat" 
    [longitude]="marker.lng" 
    (markerClick)="selectMarker($event)"     [markerDraggable]="true"
    (dragEnd)="markerDragEnd(coords, $event)"
    [label]="marker.label"
>
         <agm-info-window [disableAutoPan]="true">{{marker.label}}</agm-info-window>
</agm-marker>
    <!-- ... -->
</agm-map>
<!-- ... -->

See it is placed inside the agm-marker.

Drawing a circle on the map

To draw a circle over a location we use the agm-circle tag.

It has the following inputs:

“latitude”: The latitude of the location

“longitude”: The longitude of the location

“clickable”: Sets the circle clickable or not

“draggable”: Sets the circle draggable or not

“editable”: Sets the circle editable or not

“fillColor”: The color of the circle

“fillOpacity”: The transparency of the circle

“radius”: The radius of the circle

<!-- map.component.html -->
<agm-map 
    [latitude]="location.latitude" 
    [longitude]="location.longitude" 
    [mapTypeId]="location.mapType" 
    [zoom]="location.zoom" 
    (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <!-- ... -->
    <agm-circle 
        [latitude]="location.latitude" 
        [longitude]="location.longitude" [clickable]="false" 
        [draggable]="true" 
        [editable]="false" 
        [fillColor]="green" 
        [fillOpacity]="0" 
        [radius]="circleRadius">
    </agm-circle>
</agm-map>
<!-- ... -->
Drawing a rectangle on the map

We use the agm-rectangle. We must provide the bounds:

“north”: The north bound

“east”: The east bound

“south”: The south bound

“west”: The west bound

<!-- map.component.html -->
<agm-map 
    [latitude]="location.latitude" 
    [longitude]="location.longitude" 
    [mapTypeId]="location.mapType" 
    [zoom]="location.zoom" 
    (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
    <!-- ... -->
    <agm-rectangle 
        [north]="north" 
        [south]="south" 
        [east]="east" 
        [west]="west"> 
    </agm-rectangle>
</agm-map>
<!-- ... -->
Conclusion

We pretty much exhausted Google Maps on Angular. There are still many more on this. Many inputs and outputs in the agm-circle, agm-rectangle, agm-info, agm-marker, agm-direction and agm-map. This should serve as a basic guide to Google Maps in Angular.

Write your comments below, if I missed a point or concept, spelling, grammar error, in short anything at all.

Thanks!!!

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)
  }
}