Skip to content

Commit 3e00f4c

Browse files
authored
feat(google-maps): expose the underlying Google Maps objects. (#18613)
Expose the underlying Google Maps objects so they can be interacted with when it's necessary to do something unimplemented by angular/google-maps. As part of this change, update DefinitelyTyped so that we do not need a custom map object. Also standardize how underlying objects interact with the components and updates new components to work correctly when loading from the server.
1 parent 04d1588 commit 3e00f4c

File tree

19 files changed

+397
-225
lines changed

19 files changed

+397
-225
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"@angular/elements": "^9.1.0",
5454
"@angular/forms": "^9.1.0",
5555
"@angular/platform-browser": "^9.1.0",
56-
"@types/googlemaps": "^3.37.0",
56+
"@types/googlemaps": "^3.39.3",
5757
"@types/youtube": "^0.0.38",
5858
"@webcomponents/custom-elements": "^1.1.0",
5959
"core-js": "^2.6.9",

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
DEFAULT_OPTIONS,
1414
DEFAULT_WIDTH,
1515
GoogleMap,
16-
UpdatedGoogleMap
1716
} from './google-map';
1817

1918
/** Represents boundaries of a map to be used in tests. */
@@ -32,7 +31,7 @@ const testPosition: google.maps.LatLngLiteral = {
3231

3332
describe('GoogleMap', () => {
3433
let mapConstructorSpy: jasmine.Spy;
35-
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
34+
let mapSpy: jasmine.SpyObj<google.maps.Map>;
3635

3736
beforeEach(async(() => {
3837
TestBed.configureTestingModule({

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

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,6 @@ interface GoogleMapsWindow extends Window {
3333
google?: typeof google;
3434
}
3535

36-
// TODO(mbehrlich): Update this to use original map after updating DefinitelyTyped
37-
/**
38-
* Extends the Google Map interface due to the Definitely Typed implementation
39-
* missing "getClickableIcons".
40-
*/
41-
export interface UpdatedGoogleMap extends google.maps.Map {
42-
getClickableIcons: () => boolean;
43-
}
44-
4536
/** default options set to the Googleplex */
4637
export const DEFAULT_OPTIONS: google.maps.MapOptions = {
4738
center: {lat: 37.421995, lng: -122.084092},
@@ -74,7 +65,13 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
7465
private readonly _zoom = new BehaviorSubject<number|undefined>(undefined);
7566
private readonly _destroy = new Subject<void>();
7667
private _mapEl: HTMLElement;
77-
_googleMap: UpdatedGoogleMap;
68+
69+
/**
70+
* The underlying google.maps.Map object
71+
*
72+
* See developers.google.com/maps/documentation/javascript/reference/map#Map
73+
*/
74+
googleMap?: google.maps.Map;
7875

7976
/** Whether we're currently rendering inside a browser. */
8077
_isBrowser: boolean;
@@ -257,8 +254,8 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
257254

258255
ngOnChanges() {
259256
this._setSize();
260-
if (this._googleMap && this.mapTypeId) {
261-
this._googleMap.setMapTypeId(this.mapTypeId);
257+
if (this.googleMap && this.mapTypeId) {
258+
this.googleMap.setMapTypeId(this.mapTypeId);
262259
}
263260
}
264261

@@ -269,8 +266,8 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
269266
this._setSize();
270267
this._googleMapChanges = this._initializeMap(this._combineOptions());
271268
this._googleMapChanges.subscribe((googleMap: google.maps.Map) => {
272-
this._googleMap = googleMap as UpdatedGoogleMap;
273-
this._eventManager.setTarget(this._googleMap);
269+
this.googleMap = googleMap;
270+
this._eventManager.setTarget(this.googleMap);
274271
});
275272

276273
this._watchForOptionsChanges();
@@ -293,7 +290,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
293290
bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral,
294291
padding?: number|google.maps.Padding) {
295292
this._assertInitialized();
296-
this._googleMap.fitBounds(bounds, padding);
293+
this.googleMap!.fitBounds(bounds, padding);
297294
}
298295

299296
/**
@@ -302,7 +299,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
302299
*/
303300
panBy(x: number, y: number) {
304301
this._assertInitialized();
305-
this._googleMap.panBy(x, y);
302+
this.googleMap!.panBy(x, y);
306303
}
307304

308305
/**
@@ -311,7 +308,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
311308
*/
312309
panTo(latLng: google.maps.LatLng|google.maps.LatLngLiteral) {
313310
this._assertInitialized();
314-
this._googleMap.panTo(latLng);
311+
this.googleMap!.panTo(latLng);
315312
}
316313

317314
/**
@@ -322,7 +319,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
322319
latLngBounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral,
323320
padding?: number|google.maps.Padding) {
324321
this._assertInitialized();
325-
this._googleMap.panToBounds(latLngBounds, padding);
322+
this.googleMap!.panToBounds(latLngBounds, padding);
326323
}
327324

328325
/**
@@ -331,7 +328,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
331328
*/
332329
getBounds(): google.maps.LatLngBounds|null {
333330
this._assertInitialized();
334-
return this._googleMap.getBounds() || null;
331+
return this.googleMap!.getBounds() || null;
335332
}
336333

337334
/**
@@ -340,7 +337,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
340337
*/
341338
getCenter(): google.maps.LatLng {
342339
this._assertInitialized();
343-
return this._googleMap.getCenter();
340+
return this.googleMap!.getCenter();
344341
}
345342

346343
/**
@@ -349,7 +346,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
349346
*/
350347
getClickableIcons(): boolean {
351348
this._assertInitialized();
352-
return this._googleMap.getClickableIcons();
349+
return this.googleMap!.getClickableIcons();
353350
}
354351

355352
/**
@@ -358,7 +355,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
358355
*/
359356
getHeading(): number {
360357
this._assertInitialized();
361-
return this._googleMap.getHeading();
358+
return this.googleMap!.getHeading();
362359
}
363360

364361
/**
@@ -367,7 +364,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
367364
*/
368365
getMapTypeId(): google.maps.MapTypeId|string {
369366
this._assertInitialized();
370-
return this._googleMap.getMapTypeId();
367+
return this.googleMap!.getMapTypeId();
371368
}
372369

373370
/**
@@ -376,7 +373,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
376373
*/
377374
getProjection(): google.maps.Projection|null {
378375
this._assertInitialized();
379-
return this._googleMap.getProjection();
376+
return this.googleMap!.getProjection();
380377
}
381378

382379
/**
@@ -385,7 +382,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
385382
*/
386383
getStreetView(): google.maps.StreetViewPanorama {
387384
this._assertInitialized();
388-
return this._googleMap.getStreetView();
385+
return this.googleMap!.getStreetView();
389386
}
390387

391388
/**
@@ -394,7 +391,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
394391
*/
395392
getTilt(): number {
396393
this._assertInitialized();
397-
return this._googleMap.getTilt();
394+
return this.googleMap!.getTilt();
398395
}
399396

400397
/**
@@ -403,7 +400,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
403400
*/
404401
getZoom(): number {
405402
this._assertInitialized();
406-
return this._googleMap.getZoom();
403+
return this.googleMap!.getZoom();
407404
}
408405

409406
/**
@@ -412,7 +409,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
412409
*/
413410
get controls(): Array<google.maps.MVCArray<Node>> {
414411
this._assertInitialized();
415-
return this._googleMap.controls;
412+
return this.googleMap!.controls;
416413
}
417414

418415
/**
@@ -421,7 +418,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
421418
*/
422419
get data(): google.maps.Data {
423420
this._assertInitialized();
424-
return this._googleMap.data;
421+
return this.googleMap!.data;
425422
}
426423

427424
/**
@@ -430,7 +427,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
430427
*/
431428
get mapTypes(): google.maps.MapTypeRegistry {
432429
this._assertInitialized();
433-
return this._googleMap.mapTypes;
430+
return this.googleMap!.mapTypes;
434431
}
435432

436433
/**
@@ -439,7 +436,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
439436
*/
440437
get overlayMapTypes(): google.maps.MVCArray<google.maps.MapType> {
441438
this._assertInitialized();
442-
return this._googleMap.overlayMapTypes;
439+
return this.googleMap!.overlayMapTypes;
443440
}
444441

445442
private _setSize() {
@@ -507,7 +504,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
507504

508505
/** Asserts that the map has been initialized. */
509506
private _assertInitialized() {
510-
if (!this._googleMap) {
507+
if (!this.googleMap) {
511508
throw Error('Cannot access Google Map information before the API has been initialized. ' +
512509
'Please wait for the API to load before trying to interact with it.');
513510
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {Component, ViewChild} from '@angular/core';
22
import {async, TestBed} from '@angular/core/testing';
33
import {By} from '@angular/platform-browser';
44

5-
import {DEFAULT_OPTIONS, UpdatedGoogleMap} from '../google-map/google-map';
5+
import {DEFAULT_OPTIONS} from '../google-map/google-map';
66
import {GoogleMapsModule} from '../google-maps-module';
77
import {
88
createCircleConstructorSpy,
@@ -15,7 +15,7 @@ import {
1515
import {MapCircle} from './map-circle';
1616

1717
describe('MapCircle', () => {
18-
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
18+
let mapSpy: jasmine.SpyObj<google.maps.Map>;
1919
let circleCenter: google.maps.LatLngLiteral;
2020
let circleRadius: number;
2121
let circleOptions: google.maps.CircleOptions;

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class MapCircle implements OnInit, OnDestroy {
3737
*
3838
* @see developers.google.com/maps/documentation/javascript/reference/polygon#Circle
3939
*/
40-
circle: google.maps.Circle; // initialized in ngOnInit
40+
circle?: google.maps.Circle; // initialized in ngOnInit
4141

4242
@Input()
4343
set options(options: google.maps.CircleOptions) {
@@ -167,7 +167,8 @@ export class MapCircle implements OnInit, OnDestroy {
167167
this._ngZone.runOutsideAngular(() => {
168168
this.circle = new google.maps.Circle(options);
169169
});
170-
this.circle.setMap(this._map._googleMap);
170+
this._assertInitialized();
171+
this.circle!.setMap(this._map.googleMap!);
171172
this._eventManager.setTarget(this.circle);
172173
});
173174

@@ -181,7 +182,6 @@ export class MapCircle implements OnInit, OnDestroy {
181182
this._eventManager.destroy();
182183
this._destroyed.next();
183184
this._destroyed.complete();
184-
185185
if (this.circle) {
186186
this.circle.setMap(null);
187187
}
@@ -192,47 +192,53 @@ export class MapCircle implements OnInit, OnDestroy {
192192
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getBounds
193193
*/
194194
getBounds(): google.maps.LatLngBounds {
195-
return this.circle.getBounds();
195+
this._assertInitialized();
196+
return this.circle!.getBounds();
196197
}
197198

198199
/**
199200
* @see
200201
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getCenter
201202
*/
202203
getCenter(): google.maps.LatLng {
203-
return this.circle.getCenter();
204+
this._assertInitialized();
205+
return this.circle!.getCenter();
204206
}
205207

206208
/**
207209
* @see
208210
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getDraggable
209211
*/
210212
getDraggable(): boolean {
211-
return this.circle.getDraggable();
213+
this._assertInitialized();
214+
return this.circle!.getDraggable();
212215
}
213216

214217
/**
215218
* @see
216219
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getEditable
217220
*/
218221
getEditable(): boolean {
219-
return this.circle.getEditable();
222+
this._assertInitialized();
223+
return this.circle!.getEditable();
220224
}
221225

222226
/**
223227
* @see
224228
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getCenter
225229
*/
226230
getRadius(): number {
227-
return this.circle.getRadius();
231+
this._assertInitialized();
232+
return this.circle!.getRadius();
228233
}
229234

230235
/**
231236
* @see
232237
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getVisible
233238
*/
234239
getVisible(): boolean {
235-
return this.circle.getVisible();
240+
this._assertInitialized();
241+
return this.circle!.getVisible();
236242
}
237243

238244
private _combineOptions(): Observable<google.maps.CircleOptions> {
@@ -249,23 +255,39 @@ export class MapCircle implements OnInit, OnDestroy {
249255

250256
private _watchForOptionsChanges() {
251257
this._options.pipe(takeUntil(this._destroyed)).subscribe(options => {
252-
this.circle.setOptions(options);
258+
this._assertInitialized();
259+
this.circle!.setOptions(options);
253260
});
254261
}
255262

256263
private _watchForCenterChanges() {
257264
this._center.pipe(takeUntil(this._destroyed)).subscribe(center => {
258265
if (center) {
259-
this.circle.setCenter(center);
266+
this._assertInitialized();
267+
this.circle!.setCenter(center);
260268
}
261269
});
262270
}
263271

264272
private _watchForRadiusChanges() {
265273
this._radius.pipe(takeUntil(this._destroyed)).subscribe(radius => {
266274
if (radius !== undefined) {
267-
this.circle.setRadius(radius);
275+
this._assertInitialized();
276+
this.circle!.setRadius(radius);
268277
}
269278
});
270279
}
280+
281+
private _assertInitialized() {
282+
if (!this._map.googleMap) {
283+
throw Error(
284+
'Cannot access Google Map information before the API has been initialized. ' +
285+
'Please wait for the API to load before trying to interact with it.');
286+
}
287+
if (!this.circle) {
288+
throw Error(
289+
'Cannot interact with a Google Map Circle before it has been ' +
290+
'initialized. Please wait for the Circle to load before trying to interact with it.');
291+
}
292+
}
271293
}

0 commit comments

Comments
 (0)