Learn-skills.dev angular-google-maps
Angular Google Maps (@angular/google-maps) integration for interactive map features. Use when embedding Google Maps, adding markers, polylines, polygons, info windows, or implementing map controls and event handling in Angular applications. Supports clustering, heatmaps, and drawing tools.
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/7spade/black-tortoise/angular-google-maps" ~/.claude/skills/neversight-learn-skills-dev-angular-google-maps && rm -rf "$T"
manifest:
data/skills-md/7spade/black-tortoise/angular-google-maps/SKILL.mdsource content
Angular Google Maps Skill
Rules
Setup and Configuration
- Import
fromGoogleMapsModule@angular/google-maps - Load Google Maps script in
with API keyindex.html - Use
for TypeScript types@types/google.maps - Restrict API key by domain/IP for security
Map Component
- Use
component with<google-map>
,[center]
,[zoom]
bindings[options] - Set explicit height on
element (required for display)google-map - Use
type for positions:google.maps.LatLngLiteral{ lat: number, lng: number } - Handle map events:
,(mapClick)
,(mapDrag)(zoomChanged)
Markers
- Use
component inside<map-marker><google-map> - Set marker properties:
,[position]
,[label]
,[title][options] - Handle marker events:
,(mapClick)(mapDragend) - Use marker clustering for more than 100 markers
Info Windows
- Use
component for marker popups<map-info-window> - Reference info window using
@ViewChild(MapInfoWindow) - Call
to display,infoWindow.open()
to hideinfoWindow.close() - Show info window content conditionally based on selected marker
Drawing Tools
- Use
for polygon shapes with<map-polygon>
and[paths][options] - Use
for lines with<map-polyline>
and[path][options] - Use
for circles with<map-circle>
,[center]
,[radius][options] - Configure styles:
,fillColor
,strokeColor
,opacitystrokeWeight
Geocoding
- Use
for address/coordinate conversiongoogle.maps.Geocoder - Cache geocoding results to avoid repeated API calls
- Debounce geocoding requests (300-500ms) for user input
- Handle geocoding errors gracefully
Performance
- Lazy load Google Maps script only when needed
- Use marker clustering for large datasets (>100 markers)
- Filter markers by viewport bounds
- NEVER load all markers at once for large datasets
- NEVER geocode on every keystroke without debouncing
Security
- NEVER expose API key in client code without restrictions
- Store API key in environment configuration
- Enable Google Cloud Console API restrictions (HTTP referrers, API scope)
Context
Summary
Angular Google Maps provides official Angular bindings for Google Maps JavaScript API, enabling declarative map integration with markers, polygons, info windows, and geocoding services.
When to Use This Skill
Activate this skill when you need to:
- Embed Google Maps in Angular components
- Add and customize map markers
- Draw polylines, polygons, and circles
- Implement info windows and custom overlays
- Handle map events (click, drag, zoom)
- Implement marker clustering
- Add heatmap layers
- Use Google Maps drawing tools
- Geocode addresses and reverse geocode coordinates
- Optimize map performance for large datasets
Installation
npm install @angular/google-maps # Types for TypeScript npm install @types/google.maps
Setup Steps
Get API Key
- Go to Google Cloud Console
- Create or select a project
- Enable Maps JavaScript API
- Create API key credentials
- Restrict API key (recommended)
Load Google Maps Script
<!-- index.html --> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
Basic Map Component Example
import { Component } from '@angular/core'; import { GoogleMapsModule } from '@angular/google-maps'; @Component({ selector: 'app-map', standalone: true, imports: [GoogleMapsModule], template: ` <google-map [center]="center" [zoom]="zoom" [options]="options" (mapClick)="onMapClick($event)"> <map-marker *ngFor="let marker of markers" [position]="marker.position" [label]="marker.label" [title]="marker.title" (mapClick)="onMarkerClick(marker)"> </map-marker> </google-map> `, styles: [` google-map { height: 400px; width: 100%; } `] }) export class MapComponent { center: google.maps.LatLngLiteral = { lat: 25.033, lng: 121.565 }; // Taipei zoom = 12; options: google.maps.MapOptions = { mapTypeId: 'roadmap', disableDefaultUI: false, zoomControl: true, scrollwheel: true }; markers: Marker[] = [ { position: { lat: 25.033, lng: 121.565 }, label: 'A', title: 'Taipei 101' } ]; onMapClick(event: google.maps.MapMouseEvent) { if (event.latLng) { const newMarker = { position: event.latLng.toJSON(), label: String.fromCharCode(65 + this.markers.length), title: 'New Location' }; this.markers.push(newMarker); } } onMarkerClick(marker: Marker) { console.log('Marker clicked:', marker); } } interface Marker { position: google.maps.LatLngLiteral; label?: string; title?: string; }
Map with Info Window Example
@Component({ template: ` <google-map [center]="center" [zoom]="zoom"> <map-marker *ngFor="let marker of markers" [position]="marker.position" (mapClick)="openInfo(marker, infoWindow)"> </map-marker> <map-info-window #infoWindow> <div *ngIf="selectedMarker"> <h3>{{ selectedMarker.title }}</h3> <p>{{ selectedMarker.description }}</p> </div> </map-info-window> </google-map> ` }) export class MapWithInfoComponent { @ViewChild(MapInfoWindow) infoWindow!: MapInfoWindow; selectedMarker: any; openInfo(marker: any, infoWindow: MapInfoWindow) { this.selectedMarker = marker; infoWindow.open(); } }
Marker Clustering Example
import { MarkerClusterer } from '@googlemaps/markerclusterer'; @Component({ template: ` <google-map #map [center]="center" [zoom]="zoom"> <map-marker *ngFor="let marker of markers" [position]="marker.position"> </map-marker> </google-map> ` }) export class ClusteredMapComponent implements AfterViewInit { @ViewChild('map') mapComponent!: GoogleMap; markers: Marker[] = []; ngAfterViewInit() { if (this.mapComponent.googleMap) { const markerClusterer = new MarkerClusterer({ map: this.mapComponent.googleMap, markers: this.getGoogleMarkers() }); } } private getGoogleMarkers(): google.maps.Marker[] { return this.markers.map(m => new google.maps.Marker({ position: m.position }) ); } }
Drawing Tools Example
@Component({ template: ` <google-map [center]="center" [zoom]="zoom"> <map-polygon [paths]="polygonPaths" [options]="polygonOptions"> </map-polygon> <map-polyline [path]="polylinePath" [options]="polylineOptions"> </map-polyline> <map-circle [center]="circleCenter" [radius]="circleRadius" [options]="circleOptions"> </map-circle> </google-map> ` }) export class DrawingMapComponent { polygonPaths: google.maps.LatLngLiteral[] = [ { lat: 25.033, lng: 121.565 }, { lat: 25.035, lng: 121.567 }, { lat: 25.031, lng: 121.569 } ]; polygonOptions: google.maps.PolygonOptions = { fillColor: '#FF0000', fillOpacity: 0.3, strokeColor: '#FF0000', strokeOpacity: 1, strokeWeight: 2 }; polylinePath: google.maps.LatLngLiteral[] = [ { lat: 25.030, lng: 121.560 }, { lat: 25.035, lng: 121.565 } ]; polylineOptions: google.maps.PolylineOptions = { strokeColor: '#0000FF', strokeOpacity: 1.0, strokeWeight: 3 }; circleCenter: google.maps.LatLngLiteral = { lat: 25.033, lng: 121.565 }; circleRadius = 1000; // meters circleOptions: google.maps.CircleOptions = { fillColor: '#00FF00', fillOpacity: 0.2, strokeColor: '#00FF00', strokeOpacity: 0.8, strokeWeight: 2 }; }
Geocoding Service Example
import { Injectable } from '@angular/core'; import { Observable, from } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class GeocodingService { private geocoder = new google.maps.Geocoder(); geocodeAddress(address: string): Observable<google.maps.LatLngLiteral | null> { return from( this.geocoder.geocode({ address }) ).pipe( map(response => { if (response.results && response.results[0]) { const location = response.results[0].geometry.location; return { lat: location.lat(), lng: location.lng() }; } return null; }) ); } reverseGeocode(location: google.maps.LatLngLiteral): Observable<string | null> { return from( this.geocoder.geocode({ location }) ).pipe( map(response => { if (response.results && response.results[0]) { return response.results[0].formatted_address; } return null; }) ); } }