Skip to content

Commit 28d5a46

Browse files
committed
fix(google-maps): allow ground overlay bounds to be changed
Allows for the bounds of the Google Maps ground overlay to be changed after initialization. This case is different from other similar components, because Google Maps doesn't provide an API to change the bounds so we have to recreate the overlay any time they change. Fixes #20865.
1 parent 79b932d commit 28d5a46

File tree

3 files changed

+50
-32
lines changed

3 files changed

+50
-32
lines changed

src/google-maps/map-ground-overlay/map-ground-overlay.spec.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,6 @@ describe('MapGroundOverlay', () => {
5555
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
5656
});
5757

58-
it('has an error if required bounds are not provided', () => {
59-
expect(() => {
60-
const fixture = TestBed.createComponent(TestApp);
61-
fixture.detectChanges();
62-
}).toThrow(new Error('Image bounds are required'));
63-
});
64-
6558
it('exposes methods that provide information about the Ground Overlay', () => {
6659
const groundOverlaySpy = createGroundOverlaySpy(url, bounds, groundOverlayOptions);
6760
createGroundOverlayConstructorSpy(groundOverlaySpy).and.callThrough();
@@ -143,6 +136,22 @@ describe('MapGroundOverlay', () => {
143136
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
144137
});
145138

139+
it('should recreate the ground overlay when the bounds change', () => {
140+
const groundOverlaySpy = createGroundOverlaySpy(url, bounds, groundOverlayOptions);
141+
createGroundOverlayConstructorSpy(groundOverlaySpy).and.callThrough();
142+
143+
const fixture = TestBed.createComponent(TestApp);
144+
fixture.detectChanges();
145+
146+
const oldOverlay = fixture.componentInstance.groundOverlay.groundOverlay;
147+
fixture.componentInstance.bounds = {...bounds};
148+
fixture.detectChanges();
149+
150+
const newOverlay = fixture.componentInstance.groundOverlay.groundOverlay;
151+
expect(newOverlay).toBeTruthy();
152+
expect(newOverlay).not.toBe(oldOverlay);
153+
});
154+
146155
});
147156

148157
@Component({

src/google-maps/map-ground-overlay/map-ground-overlay.ts

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import {Directive, Input, NgZone, OnDestroy, OnInit, Output} from '@angular/core';
1313
import {BehaviorSubject, Observable, Subject} from 'rxjs';
14-
import {map, take, takeUntil} from 'rxjs/operators';
14+
import {takeUntil} from 'rxjs/operators';
1515

1616
import {GoogleMap} from '../google-map/google-map';
1717
import {MapEventManager} from '../map-event-manager';
@@ -30,6 +30,9 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
3030

3131
private readonly _opacity = new BehaviorSubject<number>(1);
3232
private readonly _url = new BehaviorSubject<string>('');
33+
private readonly _bounds =
34+
new BehaviorSubject<google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral|undefined>(
35+
undefined);
3336
private readonly _destroyed = new Subject<void>();
3437

3538
/**
@@ -46,7 +49,13 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
4649
}
4750

4851
/** Bounds for the overlay. */
49-
@Input() bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral;
52+
@Input()
53+
get bounds(): google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral {
54+
return this._bounds.value!;
55+
}
56+
set bounds(bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral) {
57+
this._bounds.next(bounds);
58+
}
5059

5160
/** Whether the overlay is clickable */
5261
@Input() clickable: boolean = false;
@@ -77,21 +86,30 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
7786
constructor(private readonly _map: GoogleMap, private readonly _ngZone: NgZone) {}
7887

7988
ngOnInit() {
80-
if (!this.bounds && (typeof ngDevMode === 'undefined' || ngDevMode)) {
81-
throw Error('Image bounds are required');
82-
}
8389
if (this._map._isBrowser) {
84-
this._combineOptions().pipe(take(1)).subscribe(options => {
90+
// The ground overlay setup is slightly different from the other Google Maps objects in that
91+
// we have to recreate the `GroundOverlay` object whenever the bounds change, because
92+
// Google Maps doesn't provide an API to update the bounds of an existing overlay.
93+
this._bounds.pipe(takeUntil(this._destroyed)).subscribe(bounds => {
94+
if (this.groundOverlay) {
95+
this.groundOverlay.setMap(null);
96+
this.groundOverlay = undefined;
97+
}
98+
8599
// Create the object outside the zone so its events don't trigger change detection.
86100
// We'll bring it back in inside the `MapEventManager` only for the events that the
87101
// user has subscribed to.
88-
this._ngZone.runOutsideAngular(() => {
89-
this.groundOverlay =
90-
new google.maps.GroundOverlay(this._url.getValue(), this.bounds, options);
91-
});
92-
this._assertInitialized();
93-
this.groundOverlay.setMap(this._map.googleMap!);
94-
this._eventManager.setTarget(this.groundOverlay);
102+
if (bounds) {
103+
this._ngZone.runOutsideAngular(() => {
104+
this.groundOverlay = new google.maps.GroundOverlay(this._url.getValue(), bounds, {
105+
clickable: this.clickable,
106+
opacity: this._opacity.value,
107+
});
108+
});
109+
this._assertInitialized();
110+
this.groundOverlay.setMap(this._map.googleMap!);
111+
this._eventManager.setTarget(this.groundOverlay);
112+
}
95113
});
96114

97115
this._watchForOpacityChanges();
@@ -138,19 +156,9 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
138156
return this.groundOverlay.getUrl();
139157
}
140158

141-
private _combineOptions(): Observable<google.maps.GroundOverlayOptions> {
142-
return this._opacity.pipe(map(opacity => {
143-
const combinedOptions: google.maps.GroundOverlayOptions = {
144-
clickable: this.clickable,
145-
opacity,
146-
};
147-
return combinedOptions;
148-
}));
149-
}
150-
151159
private _watchForOpacityChanges() {
152160
this._opacity.pipe(takeUntil(this._destroyed)).subscribe(opacity => {
153-
if (opacity) {
161+
if (opacity != null) {
154162
this._assertInitialized();
155163
this.groundOverlay.setOpacity(opacity);
156164
}

tools/public_api_guard/google-maps/google-maps.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ export declare class MapCircle implements OnInit, OnDestroy {
113113
}
114114

115115
export declare class MapGroundOverlay implements OnInit, OnDestroy {
116-
bounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
116+
get bounds(): google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
117+
set bounds(bounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral);
117118
clickable: boolean;
118119
groundOverlay?: google.maps.GroundOverlay;
119120
mapClick: Observable<google.maps.MouseEvent>;

0 commit comments

Comments
 (0)