Skip to content

feat(google-maps): expose the underlying Google Maps objects. #18613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@angular/elements": "^9.1.0",
"@angular/forms": "^9.1.0",
"@angular/platform-browser": "^9.1.0",
"@types/googlemaps": "^3.37.0",
"@types/googlemaps": "^3.39.3",
"@types/youtube": "^0.0.38",
"@webcomponents/custom-elements": "^1.1.0",
"core-js": "^2.6.9",
Expand Down
3 changes: 1 addition & 2 deletions src/google-maps/google-map/google-map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
DEFAULT_OPTIONS,
DEFAULT_WIDTH,
GoogleMap,
UpdatedGoogleMap
} from './google-map';

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

describe('GoogleMap', () => {
let mapConstructorSpy: jasmine.Spy;
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
let mapSpy: jasmine.SpyObj<google.maps.Map>;

beforeEach(async(() => {
TestBed.configureTestingModule({
Expand Down
61 changes: 29 additions & 32 deletions src/google-maps/google-map/google-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@ interface GoogleMapsWindow extends Window {
google?: typeof google;
}

// TODO(mbehrlich): Update this to use original map after updating DefinitelyTyped
/**
* Extends the Google Map interface due to the Definitely Typed implementation
* missing "getClickableIcons".
*/
export interface UpdatedGoogleMap extends google.maps.Map {
getClickableIcons: () => boolean;
}

/** default options set to the Googleplex */
export const DEFAULT_OPTIONS: google.maps.MapOptions = {
center: {lat: 37.421995, lng: -122.084092},
Expand Down Expand Up @@ -74,7 +65,13 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
private readonly _zoom = new BehaviorSubject<number|undefined>(undefined);
private readonly _destroy = new Subject<void>();
private _mapEl: HTMLElement;
_googleMap: UpdatedGoogleMap;

/**
* The underlying google.maps.Map object
*
* See developers.google.com/maps/documentation/javascript/reference/map#Map
*/
googleMap?: google.maps.Map;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the object is created asynchronously, maybe we should expose an Observable or a Promise instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly, I'm unsure if there's actually a case where the component is initialized but the google map object is unset outside of the server-side rendering case. I do not like the idea of an observable wrapped around a union of the map and undefined though. We could have the observable never emit if we are server-side rendering. Any thoughts, @jelbourn ?

Copy link
Member

@jelbourn jelbourn Mar 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the map ever be uninitialized by ngOnInit? If so, what would happen? If there's no situation where the map can be undefined without throwing an error by the end of ngOnInit, I don't see the need to make it a promise/stream.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The components were changed so that when they're loaded server side, the map is not initialized. However, in that case, the map is never initialized. To me, that seems like undefined would be more appropriate than an unemitted observable.


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

ngOnChanges() {
this._setSize();
if (this._googleMap && this.mapTypeId) {
this._googleMap.setMapTypeId(this.mapTypeId);
if (this.googleMap && this.mapTypeId) {
this.googleMap.setMapTypeId(this.mapTypeId);
}
}

Expand All @@ -269,8 +266,8 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
this._setSize();
this._googleMapChanges = this._initializeMap(this._combineOptions());
this._googleMapChanges.subscribe((googleMap: google.maps.Map) => {
this._googleMap = googleMap as UpdatedGoogleMap;
this._eventManager.setTarget(this._googleMap);
this.googleMap = googleMap;
this._eventManager.setTarget(this.googleMap);
});

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

/**
Expand All @@ -302,7 +299,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
panBy(x: number, y: number) {
this._assertInitialized();
this._googleMap.panBy(x, y);
this.googleMap!.panBy(x, y);
}

/**
Expand All @@ -311,7 +308,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
panTo(latLng: google.maps.LatLng|google.maps.LatLngLiteral) {
this._assertInitialized();
this._googleMap.panTo(latLng);
this.googleMap!.panTo(latLng);
}

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

/**
Expand All @@ -331,7 +328,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getBounds(): google.maps.LatLngBounds|null {
this._assertInitialized();
return this._googleMap.getBounds() || null;
return this.googleMap!.getBounds() || null;
}

/**
Expand All @@ -340,7 +337,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getCenter(): google.maps.LatLng {
this._assertInitialized();
return this._googleMap.getCenter();
return this.googleMap!.getCenter();
}

/**
Expand All @@ -349,7 +346,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getClickableIcons(): boolean {
this._assertInitialized();
return this._googleMap.getClickableIcons();
return this.googleMap!.getClickableIcons();
}

/**
Expand All @@ -358,7 +355,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getHeading(): number {
this._assertInitialized();
return this._googleMap.getHeading();
return this.googleMap!.getHeading();
}

/**
Expand All @@ -367,7 +364,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getMapTypeId(): google.maps.MapTypeId|string {
this._assertInitialized();
return this._googleMap.getMapTypeId();
return this.googleMap!.getMapTypeId();
}

/**
Expand All @@ -376,7 +373,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getProjection(): google.maps.Projection|null {
this._assertInitialized();
return this._googleMap.getProjection();
return this.googleMap!.getProjection();
}

/**
Expand All @@ -385,7 +382,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getStreetView(): google.maps.StreetViewPanorama {
this._assertInitialized();
return this._googleMap.getStreetView();
return this.googleMap!.getStreetView();
}

/**
Expand All @@ -394,7 +391,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getTilt(): number {
this._assertInitialized();
return this._googleMap.getTilt();
return this.googleMap!.getTilt();
}

/**
Expand All @@ -403,7 +400,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
getZoom(): number {
this._assertInitialized();
return this._googleMap.getZoom();
return this.googleMap!.getZoom();
}

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

/**
Expand All @@ -421,7 +418,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
get data(): google.maps.Data {
this._assertInitialized();
return this._googleMap.data;
return this.googleMap!.data;
}

/**
Expand All @@ -430,7 +427,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
*/
get mapTypes(): google.maps.MapTypeRegistry {
this._assertInitialized();
return this._googleMap.mapTypes;
return this.googleMap!.mapTypes;
}

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

private _setSize() {
Expand Down Expand Up @@ -507,7 +504,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {

/** Asserts that the map has been initialized. */
private _assertInitialized() {
if (!this._googleMap) {
if (!this.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.');
}
Expand Down
4 changes: 2 additions & 2 deletions src/google-maps/map-circle/map-circle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Component, ViewChild} from '@angular/core';
import {async, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';

import {DEFAULT_OPTIONS, UpdatedGoogleMap} from '../google-map/google-map';
import {DEFAULT_OPTIONS} from '../google-map/google-map';
import {GoogleMapsModule} from '../google-maps-module';
import {
createCircleConstructorSpy,
Expand All @@ -15,7 +15,7 @@ import {
import {MapCircle} from './map-circle';

describe('MapCircle', () => {
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
let mapSpy: jasmine.SpyObj<google.maps.Map>;
let circleCenter: google.maps.LatLngLiteral;
let circleRadius: number;
let circleOptions: google.maps.CircleOptions;
Expand Down
46 changes: 34 additions & 12 deletions src/google-maps/map-circle/map-circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class MapCircle implements OnInit, OnDestroy {
*
* @see developers.google.com/maps/documentation/javascript/reference/polygon#Circle
*/
circle: google.maps.Circle; // initialized in ngOnInit
circle?: google.maps.Circle; // initialized in ngOnInit

@Input()
set options(options: google.maps.CircleOptions) {
Expand Down Expand Up @@ -167,7 +167,8 @@ export class MapCircle implements OnInit, OnDestroy {
this._ngZone.runOutsideAngular(() => {
this.circle = new google.maps.Circle(options);
});
this.circle.setMap(this._map._googleMap);
this._assertInitialized();
this.circle!.setMap(this._map.googleMap!);
this._eventManager.setTarget(this.circle);
});

Expand All @@ -181,7 +182,6 @@ export class MapCircle implements OnInit, OnDestroy {
this._eventManager.destroy();
this._destroyed.next();
this._destroyed.complete();

if (this.circle) {
this.circle.setMap(null);
}
Expand All @@ -192,47 +192,53 @@ export class MapCircle implements OnInit, OnDestroy {
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getBounds
*/
getBounds(): google.maps.LatLngBounds {
return this.circle.getBounds();
this._assertInitialized();
return this.circle!.getBounds();
}

/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getCenter
*/
getCenter(): google.maps.LatLng {
return this.circle.getCenter();
this._assertInitialized();
return this.circle!.getCenter();
}

/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getDraggable
*/
getDraggable(): boolean {
return this.circle.getDraggable();
this._assertInitialized();
return this.circle!.getDraggable();
}

/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getEditable
*/
getEditable(): boolean {
return this.circle.getEditable();
this._assertInitialized();
return this.circle!.getEditable();
}

/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getCenter
*/
getRadius(): number {
return this.circle.getRadius();
this._assertInitialized();
return this.circle!.getRadius();
}

/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getVisible
*/
getVisible(): boolean {
return this.circle.getVisible();
this._assertInitialized();
return this.circle!.getVisible();
}

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

private _watchForOptionsChanges() {
this._options.pipe(takeUntil(this._destroyed)).subscribe(options => {
this.circle.setOptions(options);
this._assertInitialized();
this.circle!.setOptions(options);
});
}

private _watchForCenterChanges() {
this._center.pipe(takeUntil(this._destroyed)).subscribe(center => {
if (center) {
this.circle.setCenter(center);
this._assertInitialized();
this.circle!.setCenter(center);
}
});
}

private _watchForRadiusChanges() {
this._radius.pipe(takeUntil(this._destroyed)).subscribe(radius => {
if (radius !== undefined) {
this.circle.setRadius(radius);
this._assertInitialized();
this.circle!.setRadius(radius);
}
});
}

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.');
}
if (!this.circle) {
throw Error(
'Cannot interact with a Google Map Circle before it has been ' +
'initialized. Please wait for the Circle to load before trying to interact with it.');
}
}
}
Loading