Skip to content

Commit 485352e

Browse files
authored
fix(google-maps): allow for ground overlay image to be changed (#19306)
1 parent 2f8b271 commit 485352e

File tree

6 files changed

+85
-12
lines changed

6 files changed

+85
-12
lines changed

src/dev-app/google-map/google-map-demo.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,16 @@
101101
</label>
102102
</div>
103103

104+
<div>
105+
<label for="ground-overlay-image">
106+
Ground Overlay image
107+
108+
<select id="ground-overlay-image" (change)="groundOverlayUrlChanged($event)">
109+
<option
110+
*ngFor="let image of groundOverlayImages"
111+
[value]="image.url">{{image.title}}</option>
112+
</select>
113+
</label>
114+
</div>
115+
104116
</div>

src/dev-app/google-map/google-map-demo.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,17 @@ export class GoogleMapDemo {
6666
circleOptions: google.maps.CircleOptions =
6767
{center: CIRCLE_CENTER, radius: CIRCLE_RADIUS, strokeColor: 'grey', strokeOpacity: 0.8};
6868
isGroundOverlayDisplayed = false;
69-
groundOverlayUrl = 'https://angular.io/assets/images/logos/angular/angular.svg';
69+
groundOverlayImages = [
70+
{
71+
title: 'Red logo',
72+
url: 'https://angular.io/assets/images/logos/angular/angular.svg'
73+
},
74+
{
75+
title: 'Black logo',
76+
url: 'https://angular.io/assets/images/logos/angular/angular_solidBlack.svg'
77+
}
78+
];
79+
groundOverlayUrl = this.groundOverlayImages[0].url;
7080
groundOverlayBounds = RECTANGLE_BOUNDS;
7181

7282
mapTypeId: google.maps.MapTypeId;
@@ -149,4 +159,8 @@ export class GoogleMapDemo {
149159
toggleGroundOverlayDisplay() {
150160
this.isGroundOverlayDisplayed = !this.isGroundOverlayDisplayed;
151161
}
162+
163+
groundOverlayUrlChanged(event: Event) {
164+
this.groundOverlayUrl = (event.target as HTMLSelectElement).value;
165+
}
152166
}

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ describe('MapGroundOverlay', () => {
5757
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
5858
});
5959

60-
it('has an error if required url or bounds are not provided', () => {
60+
it('has an error if required bounds are not provided', () => {
6161
expect(() => {
6262
const fixture = TestBed.createComponent(TestApp);
6363
fixture.detectChanges();
64-
}).toThrow(new Error('An image url is required'));
64+
}).toThrow(new Error('Image bounds are required'));
6565
});
6666

6767
it('exposes methods that provide information about the Ground Overlay', () => {
@@ -119,6 +119,32 @@ describe('MapGroundOverlay', () => {
119119
expect(addSpy).toHaveBeenCalledWith('dblclick', jasmine.any(Function));
120120
subscription.unsubscribe();
121121
});
122+
123+
it('should be able to change the image after init', () => {
124+
const groundOverlaySpy = createGroundOverlaySpy(url, bounds, groundOverlayOptions);
125+
const groundOverlayConstructorSpy =
126+
createGroundOverlayConstructorSpy(groundOverlaySpy).and.callThrough();
127+
128+
const fixture = TestBed.createComponent(TestApp);
129+
fixture.componentInstance.url = url;
130+
fixture.componentInstance.bounds = bounds;
131+
fixture.componentInstance.clickable = clickable;
132+
fixture.componentInstance.opacity = opacity;
133+
fixture.detectChanges();
134+
135+
expect(groundOverlayConstructorSpy).toHaveBeenCalledWith(url, bounds, groundOverlayOptions);
136+
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
137+
138+
groundOverlaySpy.setMap.calls.reset();
139+
fixture.componentInstance.url = 'foo.png';
140+
fixture.detectChanges();
141+
142+
expect(groundOverlaySpy.set).toHaveBeenCalledWith('url', 'foo.png');
143+
expect(groundOverlaySpy.setMap).toHaveBeenCalledTimes(2);
144+
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(null);
145+
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
146+
});
147+
122148
});
123149

124150
@Component({

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
2828
private _eventManager = new MapEventManager(this._ngZone);
2929

3030
private readonly _opacity = new BehaviorSubject<number>(1);
31+
private readonly _url = new BehaviorSubject<string>('');
3132
private readonly _destroyed = new Subject<void>();
3233

3334
/**
@@ -37,13 +38,19 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
3738
*/
3839
groundOverlay?: google.maps.GroundOverlay;
3940

40-
@Input() url!: string; // Asserted in ngOnInit.
41-
41+
/** URL of the image that will be shown in the overlay. */
4242
@Input()
43-
bounds!: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral; // Asserted in ngOnInit.
43+
set url(url: string) {
44+
this._url.next(url);
45+
}
4446

45-
@Input() clickable = false;
47+
/** Bounds for the overlay. */
48+
@Input() bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral;
4649

50+
/** Whether the overlay is clickable */
51+
@Input() clickable: boolean = false;
52+
53+
/** Opacity of the overlay. */
4754
@Input()
4855
set opacity(opacity: number) {
4956
this._opacity.next(opacity);
@@ -69,9 +76,6 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
6976
constructor(private readonly _map: GoogleMap, private readonly _ngZone: NgZone) {}
7077

7178
ngOnInit() {
72-
if (!this.url) {
73-
throw Error('An image url is required');
74-
}
7579
if (!this.bounds) {
7680
throw Error('Image bounds are required');
7781
}
@@ -81,14 +85,16 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
8185
// We'll bring it back in inside the `MapEventManager` only for the events that the
8286
// user has subscribed to.
8387
this._ngZone.runOutsideAngular(() => {
84-
this.groundOverlay = new google.maps.GroundOverlay(this.url, this.bounds, options);
88+
this.groundOverlay =
89+
new google.maps.GroundOverlay(this._url.getValue(), this.bounds, options);
8590
});
8691
this._assertInitialized();
8792
this.groundOverlay.setMap(this._map.googleMap!);
8893
this._eventManager.setTarget(this.groundOverlay);
8994
});
9095

9196
this._watchForOpacityChanges();
97+
this._watchForUrlChanges();
9298
}
9399
}
94100

@@ -150,6 +156,18 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
150156
});
151157
}
152158

159+
private _watchForUrlChanges() {
160+
this._url.pipe(takeUntil(this._destroyed)).subscribe(url => {
161+
this._assertInitialized();
162+
const overlay = this.groundOverlay;
163+
overlay.set('url', url);
164+
165+
// Google Maps only redraws the overlay if we re-set the map.
166+
overlay.setMap(null);
167+
overlay.setMap(this._map.googleMap!);
168+
});
169+
}
170+
153171
private _assertInitialized(): asserts this is {groundOverlay: google.maps.GroundOverlay} {
154172
if (!this._map.googleMap) {
155173
throw Error(

src/google-maps/testing/fake-google-map-utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,18 @@ export function createCircleConstructorSpy(circleSpy: jasmine.SpyObj<google.maps
241241
export function createGroundOverlaySpy(
242242
url: string, bounds: google.maps.LatLngBoundsLiteral,
243243
options?: google.maps.GroundOverlayOptions): jasmine.SpyObj<google.maps.GroundOverlay> {
244+
const values: {[key: string]: any} = {url};
244245
const groundOverlaySpy = jasmine.createSpyObj('google.maps.GroundOverlay', [
245246
'addListener',
246247
'getBounds',
247248
'getOpacity',
248249
'getUrl',
249250
'setMap',
250251
'setOpacity',
252+
'set',
251253
]);
252254
groundOverlaySpy.addListener.and.returnValue({remove: () => {}});
255+
groundOverlaySpy.set.and.callFake((key: string, value: any) => values[key] = value);
253256
return groundOverlaySpy;
254257
}
255258

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export declare class MapGroundOverlay implements OnInit, OnDestroy {
9494
mapClick: Observable<google.maps.MouseEvent>;
9595
mapDblclick: Observable<google.maps.MouseEvent>;
9696
set opacity(opacity: number);
97-
url: string;
97+
set url(url: string);
9898
constructor(_map: GoogleMap, _ngZone: NgZone);
9999
getBounds(): google.maps.LatLngBounds;
100100
getOpacity(): number;

0 commit comments

Comments
 (0)