Integrating Google Maps in Angular 17

Selsa Pingardi
9 min readApr 3, 2024

--

If you have been using the Google Maps component by Angular, you may have noticed a warning about the Marker being deprecated as of February 21st, 2024 and we are encouraged to use the new and more customizable Advanced Marker.

Unfortunately, there have not been many guides and information about how to use the Advanced Marker in Angular, and most articles still use the old Marker in their examples.

In this article, however, I will be sharing how we can make use of the new Advanced Marker whether you have an existing Angular project that needs to be updated, or you are just trying out the component from scratch.

So let’s get started!

Prerequisites

Before we create our Angular project, please ensure that you have a valid API key to utilize the Google Maps JavaScript API. You can refer to the link below for further instructions:
https://developers.google.com/maps/documentation/javascript/get-api-key

Additionally, we will also require a Map ID for this component, which can be obtained by following the link below:
https://developers.google.com/maps/documentation/get-map-id

If you do not have / cannot obtain a map ID, you may use the one provided for development by Google:

  • Map ID = DEMO_MAP_ID

Setting Up

Assuming that you are starting a new project and already have Angular v17 set up, create a new app:

ng new angular-google-maps
cd angular-google-maps

Install the Google Maps module by running the command:

npm i @angular/google-maps

In Angular v17, a new application is created as a standalone project by default. You can find out if your component is a standalone by taking a look at its standalone property in the src/app/app.component.ts file. If the component is a standalone, the property will be flagged as true.

You can read more about standalone components here:
https://angular.io/guide/standalone-components

For standalone components, import the module into the src/app/app.component.ts file as so:

...
import { GoogleMapsModule } from "@angular/google-maps";

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, GoogleMapsModule],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
...

If the component is not a standalone, import the module into the src/app/app.module.ts file instead:

...
import { GoogleMapsModule } from "@angular/google-maps";

@NgModule({
declarations: [...],
imports: [..., GoogleMapsModule],
...
})
export class AppModule {}

Add the following script to the application’s src/index.html file and replace YOUR_API_KEY with your corresponding API key:

<!DOCTYPE html>
<head>
...
</head>
<body>
...
<script>
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
v: "weekly",
key: "YOUR_API_KEY"
});
</script>
</body>
</html>

Make sure the script is placed before the closing body tag and not between the head tags.

Adding the Map

We can now add the google-map component into our application by adding the height and width of the map, as well as its options:

  • mapId = the ID of the map that is rendered
  • center = the map’s center (in latitude and longitude)
  • zoom = the zoom level of the map
<!-- app.component.html -->
<h1>Google Maps in Angular 17</h1>
<google-map height="600px" width="800px" [options]="options"> </google-map>
// app.component.ts
...
export class AppComponent {
...
options: google.maps.MapOptions = {
mapId: "DEMO_MAP_ID",
center: { lat: -31, lng: 147 },
zoom: 4,
};
}

For a full list of modifiable MapOptions, please refer to the following link:
https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions

You can now try to run the application using the command:

ng serve

You should get a map similar to the following image:

Example of the map

Adding the Advanced Markers

First, we have to define the positions (latitude and longitude) of our markers. Declare the following variables into the app.component.ts file:

export class AppComponent {
...
nzLocations: any[] = [
{ lat: -36.817685, lng: 175.699196 },
{ lat: -36.828611, lng: 175.790222 },
{ lat: -39.927193, lng: 175.053218 },
{ lat: -41.330162, lng: 174.865694 },
{ lat: -43.999792, lng: 170.463352 },
];
auLocations: any[] = [
{ lat: -31.56391, lng: 147.154312 },
{ lat: -33.718234, lng: 150.363181 },
{ lat: -33.727111, lng: 150.371124 },
{ lat: -33.848588, lng: 151.209834 },
{ lat: -33.851702, lng: 151.216968 },
{ lat: -34.671264, lng: 150.863657 },
{ lat: -35.304724, lng: 148.662905 },
{ lat: -37.75, lng: 145.116667 },
{ lat: -37.759859, lng: 145.128708 },
{ lat: -37.765015, lng: 145.133858 },
{ lat: -37.770104, lng: 145.143299 },
{ lat: -37.7737, lng: 145.145187 },
{ lat: -37.774785, lng: 145.137978 },
{ lat: -37.819616, lng: 144.968119 },
{ lat: -38.330766, lng: 144.695692 },
{ lat: -42.734358, lng: 147.439506 },
{ lat: -42.734358, lng: 147.501315 },
{ lat: -42.735258, lng: 147.438 },
];
}

Next, we will add the Advanced Markers onto the map using a for-loop in the app.component.html file.

...
<google-map height="600px" width="800px" [options]="options">
@for (location of nzLocations; track location) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
/>
} @for (location of auLocations; track location) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
/>
}
</google-map>

Now, you can save the files and see that the map has been populated with several Australian and New Zealand location markers.

Example of the map with advanced markers added

Note: We are using 2 different variables because in the next section, I will be using the first one to show you how to change the default marker with inline SVG and the other with a PNG image.

Customizing the Advanced Markers

Now that we’ve successfully initialized the map and added the markers, we can continue to customize them with a custom image.

Using an inline SVG

To replace the default markers, we must first add a DOMParser to convert the SVG string into a DOM element. In the app.component.ts file, initialize the parser and the SVG string we want to use:

...
ngOnInit() {
const parser = new DOMParser();
// this is an SVG string of a house icon, but feel free to use whatever SVG icon you'd like
const svgString = `<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#FF5733" stroke="#FFFFFF" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M11.293 3.293a1 1 0 0 1 1.414 0l6 6 2 2a1 1 0 0 1-1.414 1.414L19 12.414V19a2 2 0 0 1-2 2h-3a1 1 0 0 1-1-1v-3h-2v3a1 1 0 0 1-1 1H7a2 2 0 0 1-2-2v-6.586l-.293.293a1 1 0 0 1-1.414-1.414l2-2 6-6Z" clip-rule="evenodd"/>
</svg>`;
}

Next, we will loop through our New Zealand markers to set their content as the image we want. Using a forEach loop, set each location’s content property as a document element using the parser after the code above within the ngOnInit() function.

this.nzLocations.forEach((location) => {
location.content = parser.parseFromString(svgString, "image/svg+xml").documentElement;
});

Once the content of each location has been set, we have to add and modify the New Zealand locations’ <map-advanced-marker> tags’ content property in the app.component.html file too.

...
@for (location of nzLocations; track location) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
[content]="location.content"
/>
}
...

The following image shows what the current map should look like after changing the marker’s default pin appearance.

Example of New Zealand markers with a custom SVG icon

Note: If the SVG is not added to each marker using a loop, the custom image would only be displayed in the last marker that was added causing the rest of the markers to have empty images/icons.

Using a PNG image

If you would like to use a PNG image, we would have to create an img element that references the image and sets that element into the location’s content tag instead.

In the app.component.ts file, we will define the link to our PNG image first:

ngOnInit() {
...
// we will be using Google's beach flag image as an example, but feel free to use any image you'd like
const beachFlag = "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png";
}

Then, we will use a forEach loop on the Australian markers, as we did similarly in the inline SVG section, to create an img element that will be passed onto each location’s content after the code above within the ngOnInit() function.

this.auLocations.forEach((location) => {
let imgTag = document.createElement("img");
imgTag.src = beachFlag;
location.content = imgTag;
});

Once again, we need to add and modify the Australian locations’ <map-advanced-marker> tags’ content property in the app.component.html file.

...
@for (location of auLocations; track location) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
[content]="location.content"
/>
}
...

The final result should look like the following image:

Example of Australian markers with a custom PNG image

Note: Similar to the inline SVG, if the img element is not added to each marker using a loop, the custom image would only be displayed in the last marker that was added causing the rest of the markers to have empty images/icons.

Adding a Title

The title text appears when a marker is hovered over and is set by modifying the <map-advanced-marker> tag’s title property. In the code below, I have added a title based on each location’s index number, but you can change it to whatever you’d like.

...
@for (location of nzLocations; track location; let index = $index) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
[content]="location.content"
[title]="'Location ' + (index + 1)"
/>
} @for (location of auLocations; track location; let index = $index) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
[content]="location.content"
[title]="'Location ' + (index + 1)"
/>
}
...

So when the cursor hovers over a marker, a small text will appear to indicate which location number this is.

Example of an Advanced Marker hovered with the title “Location 18”

If you would like to modify other properties of the Advanced Marker Element aside from its position, content and title, please refer to the following link for a full list of properties:
https://developers.google.com/maps/documentation/javascript/reference/advanced-markers

Adding an Info Window

Info windows can provide more information about a marker and are usually shown when a marker is clicked on. Since we are using Advanced Markers, we can utilize the new function provided for the Advanced Marker: openAdvancedMarkerElement().

Firstly, we need to update the imports and define a ViewChild of the infoWindow in the app.component.ts file.

import { Component, ViewChild } from "@angular/core";
import { GoogleMapsModule, MapAdvancedMarker, MapInfoWindow } from "@angular/google-maps";
...
export class AppComponent {
...
@ViewChild(MapInfoWindow) infoWindow!: MapInfoWindow;
...
}

Then, we can construct a function in the AppComponent class that will run when the marker is clicked:

...
export class AppComponent {
...
onMarkerClick(marker: MapAdvancedMarker) {
this.infoWindow.openAdvancedMarkerElement(marker.advancedMarker, marker.advancedMarker.title);
}
...
}

The first parameter of the openAdvancedMarkerElement() function accepts the position of the marker element where the info window will open, whereas the second parameter accepts a nullable string or Element that will be used as the content in the info window.

Now, all that’s left to do is to add the mapClick event with the onMarkerClick function we created earlier and the <map-info-window> tag into the app.component.html file.

...
@for (location of nzLocations; track location; let index = $index) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
[content]="location.content"
[title]="'Location ' + (index + 1)"
(mapClick)="onMarkerClick(markerElem)"
/>
} @for (location of auLocations; track location; let index = $index) {
<map-advanced-marker
#markerElem="mapAdvancedMarker"
[position]="{ lat: location.lat, lng: location.lng }"
[content]="location.content"
[title]="'Location ' + (index + 1)"
(mapClick)="onMarkerClick(markerElem)"
/>
}
<map-info-window #infoWindow></map-info-window>
...

Save the files and you should now see an info window with the marker’s title pop-up on whichever marker you click on.

Example of an Advanced Marker clicked and an info window shows up with the text “Location 5”

If you would like to read more about Info Windows, have a look at this link: https://developers.google.com/maps/documentation/javascript/infowindows

Conclusion

If you made it this far, congratulations! We’ve successfully implemented a Google Map with customized Advanced Markers that open up info windows when clicked.

If you encounter any troubles with the code above or want to see the full code instead, you can hop over to this GitHub repository where I have shared the project. Be sure to replace the API key with your own!

That’s all for this article! Feel free to share your thoughts in the comments below. I hope you’ve found it helpful and easy to follow. I would sincerely appreciate it if you gave me some claps for my first Medium article. Thank you! 😄

--

--