diff --git a/src/google-maps/google-map/google-map.ts b/src/google-maps/google-map/google-map.ts index 73ecd63e45be..ca9c0414a6e8 100644 --- a/src/google-maps/google-map/google-map.ts +++ b/src/google-maps/google-map/google-map.ts @@ -309,16 +309,24 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy { // Create the object outside the zone so its events don't trigger change detection. // We'll bring it back in inside the `MapEventManager` only for the events that the // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const mapConstructor = - google.maps.Map || (await importLibrary('maps', 'Map')); - this.googleMap = new mapConstructor(this._mapEl, this._combineOptions()); - this._eventManager.setTarget(this.googleMap); - this.mapInitialized.emit(this.googleMap); - }); + if (google.maps.Map) { + this._initialize(google.maps.Map); + } else { + this._ngZone.runOutsideAngular(async () => { + this._initialize(await importLibrary('maps', 'Map')); + }); + } } } + private _initialize(mapConstructor: typeof google.maps.Map) { + this._ngZone.runOutsideAngular(() => { + this.googleMap = new mapConstructor(this._mapEl, this._combineOptions()); + this._eventManager.setTarget(this.googleMap); + this.mapInitialized.emit(this.googleMap); + }); + } + ngOnDestroy() { this.mapInitialized.complete(); this._eventManager.destroy(); diff --git a/src/google-maps/map-base-layer.ts b/src/google-maps/map-base-layer.ts index 51c21733f285..5448a9e5678b 100644 --- a/src/google-maps/map-base-layer.ts +++ b/src/google-maps/map-base-layer.ts @@ -26,11 +26,11 @@ export class MapBaseLayer implements OnInit, OnDestroy { ngOnInit() { if (this._map._isBrowser) { - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - await this._initializeObject(); - this._setMap(map); + this._ngZone.runOutsideAngular(() => { + this._initializeObject(); }); + this._assertInitialized(); + this._setMap(); } } @@ -38,7 +38,16 @@ export class MapBaseLayer implements OnInit, OnDestroy { this._unsetMap(); } - protected async _initializeObject() {} - protected _setMap(_map: google.maps.Map) {} + private _assertInitialized() { + if (!this._map.googleMap) { + throw Error( + 'Cannot access Google Map information before the API has been initialized. ' + + 'Please wait for the API to load before trying to interact with it.', + ); + } + } + + protected _initializeObject() {} + protected _setMap() {} protected _unsetMap() {} } diff --git a/src/google-maps/map-bicycling-layer/map-bicycling-layer.ts b/src/google-maps/map-bicycling-layer/map-bicycling-layer.ts index 720a009ce8c9..f7f1df0b26b2 100644 --- a/src/google-maps/map-bicycling-layer/map-bicycling-layer.ts +++ b/src/google-maps/map-bicycling-layer/map-bicycling-layer.ts @@ -9,10 +9,10 @@ // Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265 /// -import {Directive, EventEmitter, Output} from '@angular/core'; +import {Directive, EventEmitter, NgZone, OnDestroy, OnInit, Output, inject} from '@angular/core'; -import {MapBaseLayer} from '../map-base-layer'; import {importLibrary} from '../import-library'; +import {GoogleMap} from '../google-map/google-map'; /** * Angular component that renders a Google Maps Bicycling Layer via the Google Maps JavaScript API. @@ -24,7 +24,10 @@ import {importLibrary} from '../import-library'; exportAs: 'mapBicyclingLayer', standalone: true, }) -export class MapBicyclingLayer extends MapBaseLayer { +export class MapBicyclingLayer implements OnInit, OnDestroy { + private _map = inject(GoogleMap); + private _zone = inject(NgZone); + /** * The underlying google.maps.BicyclingLayer object. * @@ -36,20 +39,33 @@ export class MapBicyclingLayer extends MapBaseLayer { @Output() readonly bicyclingLayerInitialized: EventEmitter = new EventEmitter(); - protected override async _initializeObject() { - const layerConstructor = - google.maps.BicyclingLayer || - (await importLibrary('maps', 'BicyclingLayer')); - this.bicyclingLayer = new layerConstructor(); - this.bicyclingLayerInitialized.emit(this.bicyclingLayer); + ngOnInit(): void { + if (this._map._isBrowser) { + if (google.maps.BicyclingLayer && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.BicyclingLayer); + } else { + this._zone.runOutsideAngular(async () => { + this._initialize( + ...(await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'BicyclingLayer'), + ])), + ); + }); + } + } } - protected override _setMap(map: google.maps.Map) { - this._assertLayerInitialized(); - this.bicyclingLayer.setMap(map); + private _initialize(map: google.maps.Map, layerConstructor: typeof google.maps.BicyclingLayer) { + this._zone.runOutsideAngular(() => { + this.bicyclingLayer = new layerConstructor(); + this.bicyclingLayerInitialized.emit(this.bicyclingLayer); + this._assertLayerInitialized(); + this.bicyclingLayer.setMap(map); + }); } - protected override _unsetMap() { + ngOnDestroy() { this.bicyclingLayer?.setMap(null); } diff --git a/src/google-maps/map-circle/map-circle.ts b/src/google-maps/map-circle/map-circle.ts index a6b5912793a1..47c561279604 100644 --- a/src/google-maps/map-circle/map-circle.ts +++ b/src/google-maps/map-circle/map-circle.ts @@ -175,25 +175,40 @@ export class MapCircle implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const circleConstructor = - google.maps.Circle || (await importLibrary('maps', 'Circle')); - this.circle = new circleConstructor(options); - this._assertInitialized(); - this.circle.setMap(map); - this._eventManager.setTarget(this.circle); - this.circleInitialized.emit(this.circle); - this._watchForOptionsChanges(); - this._watchForCenterChanges(); - this._watchForRadiusChanges(); - }); + if (google.maps.Circle && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.Circle, options); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, circleConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'Circle'), + ]); + this._initialize(map, circleConstructor, options); + }); + } }); } + private _initialize( + map: google.maps.Map, + circleConstructor: typeof google.maps.Circle, + options: google.maps.CircleOptions, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.circle = new circleConstructor(options); + this._assertInitialized(); + this.circle.setMap(map); + this._eventManager.setTarget(this.circle); + this.circleInitialized.emit(this.circle); + this._watchForOptionsChanges(); + this._watchForCenterChanges(); + this._watchForRadiusChanges(); + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroyed.next(); diff --git a/src/google-maps/map-directions-renderer/map-directions-renderer.ts b/src/google-maps/map-directions-renderer/map-directions-renderer.ts index b207f5580fa9..79ad83da588e 100644 --- a/src/google-maps/map-directions-renderer/map-directions-renderer.ts +++ b/src/google-maps/map-directions-renderer/map-directions-renderer.ts @@ -82,23 +82,37 @@ export class MapDirectionsRenderer implements OnInit, OnChanges, OnDestroy { ngOnInit() { if (this._googleMap._isBrowser) { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._googleMap._resolveMap(); - const rendererConstructor = - google.maps.DirectionsRenderer || - (await importLibrary('routes', 'DirectionsRenderer')); - this.directionsRenderer = new rendererConstructor(this._combineOptions()); - this._assertInitialized(); - this.directionsRenderer.setMap(map); - this._eventManager.setTarget(this.directionsRenderer); - this.directionsRendererInitialized.emit(this.directionsRenderer); - }); + if (google.maps.DirectionsRenderer && this._googleMap.googleMap) { + this._initialize(this._googleMap.googleMap, google.maps.DirectionsRenderer); + } else { + this._ngZone.runOutsideAngular(async () => { + this._initialize( + ...(await Promise.all([ + this._googleMap._resolveMap(), + importLibrary('routes', 'DirectionsRenderer'), + ])), + ); + }); + } } } + private _initialize( + map: google.maps.Map, + rendererConstructor: typeof google.maps.DirectionsRenderer, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.directionsRenderer = new rendererConstructor(this._combineOptions()); + this._assertInitialized(); + this.directionsRenderer.setMap(map); + this._eventManager.setTarget(this.directionsRenderer); + this.directionsRendererInitialized.emit(this.directionsRenderer); + }); + } + ngOnChanges(changes: SimpleChanges) { if (this.directionsRenderer) { if (changes['options']) { diff --git a/src/google-maps/map-ground-overlay/map-ground-overlay.ts b/src/google-maps/map-ground-overlay/map-ground-overlay.ts index 619c117e828f..69967f65a527 100644 --- a/src/google-maps/map-ground-overlay/map-ground-overlay.ts +++ b/src/google-maps/map-ground-overlay/map-ground-overlay.ts @@ -117,34 +117,48 @@ export class MapGroundOverlay implements OnInit, OnDestroy { return; } - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const overlayConstructor = - google.maps.GroundOverlay || - (await importLibrary('maps', 'GroundOverlay')); - this.groundOverlay = new overlayConstructor(this._url.getValue(), bounds, { - clickable: this.clickable, - opacity: this._opacity.value, + if (google.maps.GroundOverlay && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.GroundOverlay, bounds); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, overlayConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'GroundOverlay'), + ]); + this._initialize(map, overlayConstructor, bounds); }); - this._assertInitialized(); - this.groundOverlay.setMap(map); - this._eventManager.setTarget(this.groundOverlay); - this.groundOverlayInitialized.emit(this.groundOverlay); - - // We only need to set up the watchers once. - if (!this._hasWatchers) { - this._hasWatchers = true; - this._watchForOpacityChanges(); - this._watchForUrlChanges(); - } - }); + } }); } } + private _initialize( + map: google.maps.Map, + overlayConstructor: typeof google.maps.GroundOverlay, + bounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.groundOverlay = new overlayConstructor(this._url.getValue(), bounds, { + clickable: this.clickable, + opacity: this._opacity.value, + }); + this._assertInitialized(); + this.groundOverlay.setMap(map); + this._eventManager.setTarget(this.groundOverlay); + this.groundOverlayInitialized.emit(this.groundOverlay); + + // We only need to set up the watchers once. + if (!this._hasWatchers) { + this._hasWatchers = true; + this._watchForOpacityChanges(); + this._watchForUrlChanges(); + } + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroyed.next(); diff --git a/src/google-maps/map-heatmap-layer/map-heatmap-layer.ts b/src/google-maps/map-heatmap-layer/map-heatmap-layer.ts index b2e387cf3500..de94992add6a 100644 --- a/src/google-maps/map-heatmap-layer/map-heatmap-layer.ts +++ b/src/google-maps/map-heatmap-layer/map-heatmap-layer.ts @@ -92,25 +92,39 @@ export class MapHeatmapLayer implements OnInit, OnChanges, OnDestroy { ); } - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._googleMap._resolveMap(); - const heatmapConstructor = - google.maps.visualization?.HeatmapLayer || - (await importLibrary( - 'visualization', - 'HeatmapLayer', - )); - this.heatmap = new heatmapConstructor(this._combineOptions()); - this._assertInitialized(); - this.heatmap.setMap(map); - this.heatmapInitialized.emit(this.heatmap); - }); + if (google.maps.visualization?.HeatmapLayer && this._googleMap.googleMap) { + this._initialize(this._googleMap.googleMap, google.maps.visualization.HeatmapLayer); + } else { + this._ngZone.runOutsideAngular(async () => { + this._initialize( + ...(await Promise.all([ + this._googleMap._resolveMap(), + importLibrary( + 'visualization', + 'HeatmapLayer', + ), + ])), + ); + }); + } } } + private _initialize( + map: google.maps.Map, + heatmapConstructor: typeof google.maps.visualization.HeatmapLayer, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.heatmap = new heatmapConstructor(this._combineOptions()); + this._assertInitialized(); + this.heatmap.setMap(map); + this.heatmapInitialized.emit(this.heatmap); + }); + } + ngOnChanges(changes: SimpleChanges) { const {_data, heatmap} = this; diff --git a/src/google-maps/map-info-window/map-info-window.ts b/src/google-maps/map-info-window/map-info-window.ts index 614b171b109e..f3e68a87cc67 100644 --- a/src/google-maps/map-info-window/map-info-window.ts +++ b/src/google-maps/map-info-window/map-info-window.ts @@ -117,23 +117,36 @@ export class MapInfoWindow implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const infoWindowConstructor = - google.maps.InfoWindow || - (await importLibrary('maps', 'InfoWindow')); - this.infoWindow = new infoWindowConstructor(options); - this._eventManager.setTarget(this.infoWindow); - this.infoWindowInitialized.emit(this.infoWindow); - this._watchForOptionsChanges(); - this._watchForPositionChanges(); - }); + if (google.maps.InfoWindow) { + this._initialize(google.maps.InfoWindow, options); + } else { + this._ngZone.runOutsideAngular(async () => { + this._initialize( + await importLibrary('maps', 'InfoWindow'), + options, + ); + }); + } }); } } + private _initialize( + infoWindowConstructor: typeof google.maps.InfoWindow, + options: google.maps.InfoWindowOptions, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.infoWindow = new infoWindowConstructor(options); + this._eventManager.setTarget(this.infoWindow); + this.infoWindowInitialized.emit(this.infoWindow); + this._watchForOptionsChanges(); + this._watchForPositionChanges(); + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroy.next(); diff --git a/src/google-maps/map-kml-layer/map-kml-layer.ts b/src/google-maps/map-kml-layer/map-kml-layer.ts index 358f07d240a3..e7ae9a809f02 100644 --- a/src/google-maps/map-kml-layer/map-kml-layer.ts +++ b/src/google-maps/map-kml-layer/map-kml-layer.ts @@ -94,26 +94,40 @@ export class MapKmlLayer implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const layerConstructor = - google.maps.KmlLayer || - (await importLibrary('maps', 'KmlLayer')); - this.kmlLayer = new layerConstructor(options); - this._assertInitialized(); - this.kmlLayer.setMap(map); - this._eventManager.setTarget(this.kmlLayer); - this.kmlLayerInitialized.emit(this.kmlLayer); - this._watchForOptionsChanges(); - this._watchForUrlChanges(); - }); + if (google.maps.KmlLayer && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.KmlLayer, options); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, layerConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'KmlLayer'), + ]); + this._initialize(map, layerConstructor, options); + }); + } }); } } + private _initialize( + map: google.maps.Map, + layerConstructor: typeof google.maps.KmlLayer, + options: google.maps.KmlLayerOptions, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.kmlLayer = new layerConstructor(options); + this._assertInitialized(); + this.kmlLayer.setMap(map); + this._eventManager.setTarget(this.kmlLayer); + this.kmlLayerInitialized.emit(this.kmlLayer); + this._watchForOptionsChanges(); + this._watchForUrlChanges(); + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroyed.next(); diff --git a/src/google-maps/map-marker/map-marker.ts b/src/google-maps/map-marker/map-marker.ts index 85989b3f506a..9f2054084292 100644 --- a/src/google-maps/map-marker/map-marker.ts +++ b/src/google-maps/map-marker/map-marker.ts @@ -288,14 +288,25 @@ export class MapMarker implements OnInit, OnChanges, OnDestroy, MapAnchorPoint { return; } + if (google.maps.Marker && this._googleMap.googleMap) { + this._initialize(this._googleMap.googleMap, google.maps.Marker); + } else { + this._ngZone.runOutsideAngular(async () => { + this._initialize( + ...(await Promise.all([ + this._googleMap._resolveMap(), + importLibrary('marker', 'Marker'), + ])), + ); + }); + } + } + + private _initialize(map: google.maps.Map, markerConstructor: typeof google.maps.Marker) { // Create the object outside the zone so its events don't trigger change detection. // We'll bring it back in inside the `MapEventManager` only for the events that the // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._googleMap._resolveMap(); - const markerConstructor = - google.maps.Marker || (await importLibrary('marker', 'Marker')); - + this._ngZone.runOutsideAngular(() => { this.marker = new markerConstructor(this._combineOptions()); this._assertInitialized(); this.marker.setMap(map); diff --git a/src/google-maps/map-polygon/map-polygon.ts b/src/google-maps/map-polygon/map-polygon.ts index 9a3e3204d765..38819f091915 100644 --- a/src/google-maps/map-polygon/map-polygon.ts +++ b/src/google-maps/map-polygon/map-polygon.ts @@ -152,25 +152,40 @@ export class MapPolygon implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const polygonConstructor = - google.maps.Polygon || (await importLibrary('maps', 'Polygon')); - this.polygon = new polygonConstructor(options); - this._assertInitialized(); - this.polygon.setMap(map); - this._eventManager.setTarget(this.polygon); - this.polygonInitialized.emit(this.polygon); - this._watchForOptionsChanges(); - this._watchForPathChanges(); - }); + if (google.maps.Polygon && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.Polygon, options); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, polygonConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'Polygon'), + ]); + this._initialize(map, polygonConstructor, options); + }); + } }); } } + private _initialize( + map: google.maps.Map, + polygonConstructor: typeof google.maps.Polygon, + options: google.maps.PolygonOptions, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.polygon = new polygonConstructor(options); + this._assertInitialized(); + this.polygon.setMap(map); + this._eventManager.setTarget(this.polygon); + this.polygonInitialized.emit(this.polygon); + this._watchForOptionsChanges(); + this._watchForPathChanges(); + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroyed.next(); diff --git a/src/google-maps/map-polyline/map-polyline.ts b/src/google-maps/map-polyline/map-polyline.ts index beb7d5aa6093..ce45896e15c9 100644 --- a/src/google-maps/map-polyline/map-polyline.ts +++ b/src/google-maps/map-polyline/map-polyline.ts @@ -150,26 +150,40 @@ export class MapPolyline implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const polylineConstructor = - google.maps.Polyline || - (await importLibrary('maps', 'Polyline')); - this.polyline = new polylineConstructor(options); - this._assertInitialized(); - this.polyline.setMap(map); - this._eventManager.setTarget(this.polyline); - this.polylineInitialized.emit(this.polyline); - this._watchForOptionsChanges(); - this._watchForPathChanges(); - }); + if (google.maps.Polyline && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.Polyline, options); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, polylineConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'Polyline'), + ]); + this._initialize(map, polylineConstructor, options); + }); + } }); } } + private _initialize( + map: google.maps.Map, + polylineConstructor: typeof google.maps.Polyline, + options: google.maps.PolygonOptions, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.polyline = new polylineConstructor(options); + this._assertInitialized(); + this.polyline.setMap(map); + this._eventManager.setTarget(this.polyline); + this.polylineInitialized.emit(this.polyline); + this._watchForOptionsChanges(); + this._watchForPathChanges(); + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroyed.next(); diff --git a/src/google-maps/map-rectangle/map-rectangle.ts b/src/google-maps/map-rectangle/map-rectangle.ts index 50987febfd33..17da6d66c681 100644 --- a/src/google-maps/map-rectangle/map-rectangle.ts +++ b/src/google-maps/map-rectangle/map-rectangle.ts @@ -159,26 +159,40 @@ export class MapRectangle implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - // Create the object outside the zone so its events don't trigger change detection. - // We'll bring it back in inside the `MapEventManager` only for the events that the - // user has subscribed to. - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const rectangleConstructor = - google.maps.Rectangle || - (await importLibrary('maps', 'Rectangle')); - this.rectangle = new rectangleConstructor(options); - this._assertInitialized(); - this.rectangle.setMap(map); - this._eventManager.setTarget(this.rectangle); - this.rectangleInitialized.emit(this.rectangle); - this._watchForOptionsChanges(); - this._watchForBoundsChanges(); - }); + if (google.maps.Rectangle && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.Rectangle, options); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, rectangleConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'Rectangle'), + ]); + this._initialize(map, rectangleConstructor, options); + }); + } }); } } + private _initialize( + map: google.maps.Map, + rectangleConstructor: typeof google.maps.Rectangle, + options: google.maps.RectangleOptions, + ) { + // Create the object outside the zone so its events don't trigger change detection. + // We'll bring it back in inside the `MapEventManager` only for the events that the + // user has subscribed to. + this._ngZone.runOutsideAngular(() => { + this.rectangle = new rectangleConstructor(options); + this._assertInitialized(); + this.rectangle.setMap(map); + this._eventManager.setTarget(this.rectangle); + this.rectangleInitialized.emit(this.rectangle); + this._watchForOptionsChanges(); + this._watchForBoundsChanges(); + }); + } + ngOnDestroy() { this._eventManager.destroy(); this._destroyed.next(); diff --git a/src/google-maps/map-traffic-layer/map-traffic-layer.ts b/src/google-maps/map-traffic-layer/map-traffic-layer.ts index bb4700cc0b6b..8a7f4f50f873 100644 --- a/src/google-maps/map-traffic-layer/map-traffic-layer.ts +++ b/src/google-maps/map-traffic-layer/map-traffic-layer.ts @@ -59,21 +59,35 @@ export class MapTrafficLayer implements OnInit, OnDestroy { this._combineOptions() .pipe(take(1)) .subscribe(options => { - this._ngZone.runOutsideAngular(async () => { - const map = await this._map._resolveMap(); - const layerConstructor = - google.maps.TrafficLayer || - (await importLibrary('maps', 'TrafficLayer')); - this.trafficLayer = new layerConstructor(options); - this._assertInitialized(); - this.trafficLayer.setMap(map); - this.trafficLayerInitialized.emit(this.trafficLayer); - this._watchForAutoRefreshChanges(); - }); + if (google.maps.TrafficLayer && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.TrafficLayer, options); + } else { + this._ngZone.runOutsideAngular(async () => { + const [map, layerConstructor] = await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'TrafficLayer'), + ]); + this._initialize(map, layerConstructor, options); + }); + } }); } } + private _initialize( + map: google.maps.Map, + layerConstructor: typeof google.maps.TrafficLayer, + options: google.maps.TrafficLayerOptions, + ) { + this._ngZone.runOutsideAngular(() => { + this.trafficLayer = new layerConstructor(options); + this._assertInitialized(); + this.trafficLayer.setMap(map); + this.trafficLayerInitialized.emit(this.trafficLayer); + this._watchForAutoRefreshChanges(); + }); + } + ngOnDestroy() { this._destroyed.next(); this._destroyed.complete(); diff --git a/src/google-maps/map-transit-layer/map-transit-layer.ts b/src/google-maps/map-transit-layer/map-transit-layer.ts index 4f1d62ca8e6c..90992bb0bbbb 100644 --- a/src/google-maps/map-transit-layer/map-transit-layer.ts +++ b/src/google-maps/map-transit-layer/map-transit-layer.ts @@ -9,10 +9,10 @@ // Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265 /// -import {Directive, EventEmitter, Output} from '@angular/core'; +import {Directive, EventEmitter, NgZone, OnDestroy, OnInit, Output, inject} from '@angular/core'; -import {MapBaseLayer} from '../map-base-layer'; import {importLibrary} from '../import-library'; +import {GoogleMap} from '../google-map/google-map'; /** * Angular component that renders a Google Maps Transit Layer via the Google Maps JavaScript API. @@ -24,7 +24,10 @@ import {importLibrary} from '../import-library'; exportAs: 'mapTransitLayer', standalone: true, }) -export class MapTransitLayer extends MapBaseLayer { +export class MapTransitLayer implements OnInit, OnDestroy { + private _map = inject(GoogleMap); + private _zone = inject(NgZone); + /** * The underlying google.maps.TransitLayer object. * @@ -36,20 +39,33 @@ export class MapTransitLayer extends MapBaseLayer { @Output() readonly transitLayerInitialized: EventEmitter = new EventEmitter(); - protected override async _initializeObject() { - const layerConstructor = - google.maps.TransitLayer || - (await importLibrary('maps', 'TransitLayer')); - this.transitLayer = new layerConstructor(); - this.transitLayerInitialized.emit(this.transitLayer); + ngOnInit(): void { + if (this._map._isBrowser) { + if (google.maps.TransitLayer && this._map.googleMap) { + this._initialize(this._map.googleMap, google.maps.TransitLayer); + } else { + this._zone.runOutsideAngular(async () => { + this._initialize( + ...(await Promise.all([ + this._map._resolveMap(), + importLibrary('maps', 'TransitLayer'), + ])), + ); + }); + } + } } - protected override _setMap(map: google.maps.Map) { - this._assertLayerInitialized(); - this.transitLayer.setMap(map); + private _initialize(map: google.maps.Map, layerConstructor: typeof google.maps.TransitLayer) { + this._zone.runOutsideAngular(() => { + this.transitLayer = new layerConstructor(); + this.transitLayerInitialized.emit(this.transitLayer); + this._assertLayerInitialized(); + this.transitLayer.setMap(map); + }); } - protected override _unsetMap() { + ngOnDestroy() { this.transitLayer?.setMap(null); } diff --git a/tools/public_api_guard/google-maps/google-maps.md b/tools/public_api_guard/google-maps/google-maps.md index c0cb6a9df5a9..892c09c3ec6a 100644 --- a/tools/public_api_guard/google-maps/google-maps.md +++ b/tools/public_api_guard/google-maps/google-maps.md @@ -143,7 +143,7 @@ export interface MapAnchorPoint { export class MapBaseLayer implements OnInit, OnDestroy { constructor(_map: GoogleMap, _ngZone: NgZone); // (undocumented) - protected _initializeObject(): Promise; + protected _initializeObject(): void; // (undocumented) protected readonly _map: GoogleMap; // (undocumented) @@ -153,7 +153,7 @@ export class MapBaseLayer implements OnInit, OnDestroy { // (undocumented) protected readonly _ngZone: NgZone; // (undocumented) - protected _setMap(_map: google.maps.Map): void; + protected _setMap(): void; // (undocumented) protected _unsetMap(): void; // (undocumented) @@ -163,15 +163,13 @@ export class MapBaseLayer implements OnInit, OnDestroy { } // @public -export class MapBicyclingLayer extends MapBaseLayer { +export class MapBicyclingLayer implements OnInit, OnDestroy { bicyclingLayer?: google.maps.BicyclingLayer; readonly bicyclingLayerInitialized: EventEmitter; // (undocumented) - protected _initializeObject(): Promise; - // (undocumented) - protected _setMap(map: google.maps.Map): void; + ngOnDestroy(): void; // (undocumented) - protected _unsetMap(): void; + ngOnInit(): void; // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) @@ -687,16 +685,14 @@ export class MapTrafficLayer implements OnInit, OnDestroy { } // @public -export class MapTransitLayer extends MapBaseLayer { +export class MapTransitLayer implements OnInit, OnDestroy { // (undocumented) - protected _initializeObject(): Promise; + ngOnDestroy(): void; // (undocumented) - protected _setMap(map: google.maps.Map): void; + ngOnInit(): void; transitLayer?: google.maps.TransitLayer; readonly transitLayerInitialized: EventEmitter; // (undocumented) - protected _unsetMap(): void; - // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration;