Integrating Google Maps in Angular 17
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 renderedcenter
= 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:
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.
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.
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:
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.
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.
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! 😄