Skip to content

Commit 29afab4

Browse files
committed
fix(google-maps): handle trying to access the map before it has been initialized
As things are set up at the moment, the Google `Map` object will be initialized once the API has loaded, however all of the methods on the `GoogleMap` component assume that the object will always be defined. This means that if any of the methods are called before it is initialized, we'll throw a null pointer error. These changes switch the type to `| undefined` and add guards around all the usages.
1 parent 49527e5 commit 29afab4

File tree

3 files changed

+48
-30
lines changed

3 files changed

+48
-30
lines changed

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

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
190190
@Output() zoomChanged = new EventEmitter<void>();
191191

192192
private _mapEl: HTMLElement;
193-
_googleMap!: UpdatedGoogleMap;
193+
_googleMap: UpdatedGoogleMap | undefined;
194194

195195
/** Whether we're currently rendering inside a browser. */
196196
private _isBrowser: boolean;
@@ -244,8 +244,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
244244
this._googleMapChanges = this._initializeMap(combinedOptionsChanges);
245245
this._googleMapChanges.subscribe((googleMap: google.maps.Map) => {
246246
this._googleMap = googleMap as UpdatedGoogleMap;
247-
248-
this._initializeEventHandlers();
247+
this._initializeEventHandlers(this._googleMap);
249248
});
250249

251250
this._watchForOptionsChanges();
@@ -267,23 +266,29 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
267266
fitBounds(
268267
bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral,
269268
padding?: number|google.maps.Padding) {
270-
this._googleMap.fitBounds(bounds, padding);
269+
if (this._googleMap) {
270+
this._googleMap.fitBounds(bounds, padding);
271+
}
271272
}
272273

273274
/**
274275
* See
275276
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.panBy
276277
*/
277278
panBy(x: number, y: number) {
278-
this._googleMap.panBy(x, y);
279+
if (this._googleMap) {
280+
this._googleMap.panBy(x, y);
281+
}
279282
}
280283

281284
/**
282285
* See
283286
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.panTo
284287
*/
285288
panTo(latLng: google.maps.LatLng|google.maps.LatLngLiteral) {
286-
this._googleMap.panTo(latLng);
289+
if (this._googleMap) {
290+
this._googleMap.panTo(latLng);
291+
}
287292
}
288293

289294
/**
@@ -293,111 +298,125 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
293298
panToBounds(
294299
latLngBounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral,
295300
padding?: number|google.maps.Padding) {
296-
this._googleMap.panToBounds(latLngBounds, padding);
301+
if (this._googleMap) {
302+
this._googleMap.panToBounds(latLngBounds, padding);
303+
}
297304
}
298305

299306
/**
300307
* See
301308
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getBounds
302309
*/
303310
getBounds(): google.maps.LatLngBounds|null {
304-
return this._googleMap.getBounds() || null;
311+
const googleMap = this._googleMap;
312+
return (googleMap ? googleMap.getBounds() : null) || null;
305313
}
306314

307315
/**
308316
* See
309317
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getCenter
318+
* @deprecated Type to be changed to `google.maps.LatLng|null`.
319+
* @breaking-change 10.0.0
310320
*/
311321
getCenter(): google.maps.LatLng {
312-
return this._googleMap.getCenter();
322+
return this._googleMap ? this._googleMap.getCenter() : null!;
313323
}
314324

315325
/**
316326
* See
317327
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getClickableIcons
318328
*/
319329
getClickableIcons(): boolean {
320-
return this._googleMap.getClickableIcons();
330+
return this._googleMap ? this._googleMap.getClickableIcons() : false;
321331
}
322332

323333
/**
324334
* See
325335
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getHeading
326336
*/
327337
getHeading(): number {
328-
return this._googleMap.getHeading();
338+
return this._googleMap ? this._googleMap.getHeading() : 0;
329339
}
330340

331341
/**
332342
* See
333343
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getMapTypeId
334344
*/
335345
getMapTypeId(): google.maps.MapTypeId|string {
336-
return this._googleMap.getMapTypeId();
346+
return this._googleMap ? this._googleMap.getMapTypeId() : '';
337347
}
338348

339349
/**
340350
* See
341351
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getProjection
342352
*/
343353
getProjection(): google.maps.Projection|null {
344-
return this._googleMap.getProjection();
354+
return this._googleMap ? this._googleMap.getProjection() : null;
345355
}
346356

347357
/**
348358
* See
349359
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getStreetView
360+
*
361+
* @deprecated Return type to be changed to `google.maps.StreetViewPanorama|null`.
362+
* @breaking-change 10.0.0
350363
*/
351364
getStreetView(): google.maps.StreetViewPanorama {
352-
return this._googleMap.getStreetView();
365+
return this._googleMap!.getStreetView();
353366
}
354367

355368
/**
356369
* See
357370
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getTilt
358371
*/
359372
getTilt(): number {
360-
return this._googleMap.getTilt();
373+
return this._googleMap ? this._googleMap.getTilt() : 0;
361374
}
362375

363376
/**
364377
* See
365378
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getZoom
366379
*/
367380
getZoom(): number {
368-
return this._googleMap.getZoom();
381+
return this._googleMap ? this._googleMap.getZoom() : 0;
369382
}
370383

371384
/**
372385
* See
373386
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.controls
374387
*/
375388
get controls(): Array<google.maps.MVCArray<Node>> {
376-
return this._googleMap.controls;
389+
return this._googleMap ? this._googleMap.controls : [];
377390
}
378391

379392
/**
380393
* See
381394
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.data
395+
* @deprecated Type to be changed to `google.maps.StreetViewPanorama|null`.
396+
* @breaking-change 10.0.0
382397
*/
383398
get data(): google.maps.Data {
384-
return this._googleMap.data;
399+
return this._googleMap ? this._googleMap.data : null!;
385400
}
386401

387402
/**
388403
* See
389404
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.mapTypes
405+
* @deprecated Return type to be changed to `google.maps.MapTypeRegistry|null`.
406+
* @breaking-change 10.0.0
390407
*/
391408
get mapTypes(): google.maps.MapTypeRegistry {
392-
return this._googleMap.mapTypes;
409+
return this._googleMap ? this._googleMap.mapTypes : null!;
393410
}
394411

395412
/**
396413
* See
397414
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.overlayMapTypes
415+
* @deprecated Return type to be changed to `google.maps.MVCArray<google.maps.MapType>|null`.
416+
* @breaking-change 10.0.0
398417
*/
399418
get overlayMapTypes(): google.maps.MVCArray<google.maps.MapType> {
400-
return this._googleMap.overlayMapTypes;
419+
return this._googleMap ? this._googleMap.overlayMapTypes : null!;
401420
}
402421

403422
private _setSize() {
@@ -423,9 +442,8 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
423442
private _initializeMap(optionsChanges: Observable<google.maps.MapOptions>):
424443
Observable<google.maps.Map> {
425444
return optionsChanges.pipe(
426-
take(1), map(options => {
427-
return new google.maps.Map(this._mapEl, options);
428-
}),
445+
take(1),
446+
map(options => new google.maps.Map(this._mapEl, options)),
429447
shareReplay(1));
430448
}
431449

@@ -457,7 +475,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
457475
});
458476
}
459477

460-
private _initializeEventHandlers() {
478+
private _initializeEventHandlers(googleMap: UpdatedGoogleMap) {
461479
// Ensure that we don't leak if called multiple times.
462480
this._clearListeners();
463481

@@ -484,7 +502,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
484502
]);
485503
eventHandlers.forEach((eventHandler: EventEmitter<void>, name: string) => {
486504
if (eventHandler.observers.length > 0) {
487-
this._listeners.push(this._googleMap.addListener(name, () => {
505+
this._listeners.push(googleMap.addListener(name, () => {
488506
eventHandler.emit();
489507
}));
490508
}
@@ -493,13 +511,13 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
493511
(eventHandler: EventEmitter<google.maps.MouseEvent>, name: string) => {
494512
if (eventHandler.observers.length > 0) {
495513
this._listeners.push(
496-
this._googleMap.addListener(name, (event: google.maps.MouseEvent) => {
514+
googleMap.addListener(name, (event: google.maps.MouseEvent) => {
497515
eventHandler.emit(event);
498516
}));
499517
}
500518
});
501519
if (this.mapClick.observers.length > 0) {
502-
this._listeners.push(this._googleMap.addListener(
520+
this._listeners.push(googleMap.addListener(
503521
'click', (event: google.maps.MouseEvent|google.maps.IconMouseEvent) => {
504522
this.mapClick.emit(event);
505523
}));

src/google-maps/map-marker/map-marker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ export class MapMarker implements OnInit, OnDestroy {
216216

217217
combinedOptionsChanges.pipe(take(1)).subscribe(options => {
218218
this._marker = new google.maps.Marker(options);
219-
this._marker.setMap(this._googleMap._googleMap);
219+
this._marker.setMap(this._googleMap._googleMap || null);
220220
this._initializeEventHandlers();
221221
});
222222

@@ -341,7 +341,7 @@ export class MapMarker implements OnInit, OnDestroy {
341341
position: position || options.position,
342342
label: label || options.label,
343343
clickable: clickable !== undefined ? clickable : options.clickable,
344-
map: this._googleMap._googleMap || null,
344+
map: this._googleMap._googleMap || undefined,
345345
};
346346
return combinedOptions;
347347
}));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export declare class GoogleMap implements OnChanges, OnInit, OnDestroy {
2-
_googleMap: UpdatedGoogleMap;
2+
_googleMap: UpdatedGoogleMap | undefined;
33
boundsChanged: EventEmitter<void>;
44
center: google.maps.LatLngLiteral | google.maps.LatLng;
55
centerChanged: EventEmitter<void>;

0 commit comments

Comments
 (0)