Skip to content

Commit 9ee6b26

Browse files
mbehrlichjelbourn
authored andcommitted
fix(google-maps): separate map input watchers (#17364)
Allow LatLng to be an allowable input type in addition to LatLngLiteral. Decouple individual inputs from the main "options", to prevent manual user inputs from causing unpredictable behavior in conjunction with programmatic changes.
1 parent 43af4c2 commit 9ee6b26

File tree

5 files changed

+90
-23
lines changed

5 files changed

+90
-23
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ describe('GoogleMap', () => {
116116
fixture.componentInstance.zoom = 12;
117117
fixture.detectChanges();
118118

119-
expect(mapSpy.setOptions).toHaveBeenCalledWith({center: {lat: 8, lng: 9}, zoom: 12});
119+
expect(mapSpy.setCenter).toHaveBeenCalledWith({lat: 8, lng: 9});
120+
expect(mapSpy.setZoom).toHaveBeenCalledWith(12);
120121
});
121122

122123
it('sets map options', () => {

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
6363
@Input() width = DEFAULT_WIDTH;
6464

6565
@Input()
66-
set center(center: google.maps.LatLngLiteral) {
66+
set center(center: google.maps.LatLngLiteral|google.maps.LatLng) {
6767
this._center.next(center);
6868
}
6969
@Input()
@@ -191,7 +191,8 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
191191
private readonly _listeners: google.maps.MapsEventListener[] = [];
192192

193193
private readonly _options = new BehaviorSubject<google.maps.MapOptions>(DEFAULT_OPTIONS);
194-
private readonly _center = new BehaviorSubject<google.maps.LatLngLiteral|undefined>(undefined);
194+
private readonly _center =
195+
new BehaviorSubject<google.maps.LatLngLiteral|google.maps.LatLng|undefined>(undefined);
195196
private readonly _zoom = new BehaviorSubject<number|undefined>(undefined);
196197

197198
private readonly _destroy = new Subject<void>();
@@ -224,7 +225,9 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
224225
this._initializeEventHandlers();
225226
});
226227

227-
this._watchForOptionsChanges(combinedOptionsChanges);
228+
this._watchForOptionsChanges();
229+
this._watchForCenterChanges();
230+
this._watchForZoomChanges();
228231
}
229232

230233
ngOnDestroy() {
@@ -404,15 +407,34 @@ export class GoogleMap implements OnChanges, OnInit, OnDestroy {
404407
shareReplay(1));
405408
}
406409

407-
private _watchForOptionsChanges(
408-
optionsChanges: Observable<google.maps.MapOptions>) {
409-
combineLatest([this._googleMapChanges, optionsChanges])
410+
private _watchForOptionsChanges() {
411+
combineLatest([this._googleMapChanges, this._options])
410412
.pipe(takeUntil(this._destroy))
411413
.subscribe(([googleMap, options]) => {
412414
googleMap.setOptions(options);
413415
});
414416
}
415417

418+
private _watchForCenterChanges() {
419+
combineLatest([this._googleMapChanges, this._center])
420+
.pipe(takeUntil(this._destroy))
421+
.subscribe(([googleMap, center]) => {
422+
if (center) {
423+
googleMap.setCenter(center);
424+
}
425+
});
426+
}
427+
428+
private _watchForZoomChanges() {
429+
combineLatest([this._googleMapChanges, this._zoom])
430+
.pipe(takeUntil(this._destroy))
431+
.subscribe(([googleMap, zoom]) => {
432+
if (zoom !== undefined) {
433+
googleMap.setZoom(zoom);
434+
}
435+
});
436+
}
437+
416438
private _initializeEventHandlers() {
417439
const eventHandlers = new Map<string, EventEmitter<void>>([
418440
['bounds_changed', this.boundsChanged],

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class MapInfoWindow implements OnInit, OnDestroy {
3636
}
3737

3838
@Input()
39-
set position(position: google.maps.LatLngLiteral) {
39+
set position(position: google.maps.LatLngLiteral|google.maps.LatLng) {
4040
this._position.next(position);
4141
}
4242

@@ -74,7 +74,8 @@ export class MapInfoWindow implements OnInit, OnDestroy {
7474
@Output() zindexChanged = new EventEmitter<void>();
7575

7676
private readonly _options = new BehaviorSubject<google.maps.InfoWindowOptions>({});
77-
private readonly _position = new BehaviorSubject<google.maps.LatLngLiteral|undefined>(undefined);
77+
private readonly _position =
78+
new BehaviorSubject<google.maps.LatLngLiteral|google.maps.LatLng|undefined>(undefined);
7879

7980
private readonly _listeners: google.maps.MapsEventListener[] = [];
8081

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

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
ViewEncapsulation
1818
} from '@angular/core';
1919
import {BehaviorSubject, combineLatest, Observable, Subject} from 'rxjs';
20-
import {map, takeUntil} from 'rxjs/operators';
20+
import {map, take, takeUntil} from 'rxjs/operators';
2121

2222
import {GoogleMap} from '../google-map/google-map';
2323

@@ -52,7 +52,7 @@ export class MapMarker implements OnInit, OnDestroy {
5252
}
5353

5454
@Input()
55-
set position(position: google.maps.LatLngLiteral) {
55+
set position(position: google.maps.LatLngLiteral|google.maps.LatLng) {
5656
this._position.next(position);
5757
}
5858

@@ -195,7 +195,8 @@ export class MapMarker implements OnInit, OnDestroy {
195195
private readonly _options =
196196
new BehaviorSubject<google.maps.MarkerOptions>(DEFAULT_MARKER_OPTIONS);
197197
private readonly _title = new BehaviorSubject<string|undefined>(undefined);
198-
private readonly _position = new BehaviorSubject<google.maps.LatLngLiteral|undefined>(undefined);
198+
private readonly _position =
199+
new BehaviorSubject<google.maps.LatLngLiteral|google.maps.LatLng|undefined>(undefined);
199200
private readonly _label =
200201
new BehaviorSubject<string|google.maps.MarkerLabel|undefined>(undefined);
201202
private readonly _clickable = new BehaviorSubject<boolean|undefined>(undefined);
@@ -211,15 +212,17 @@ export class MapMarker implements OnInit, OnDestroy {
211212
ngOnInit() {
212213
const combinedOptionsChanges = this._combineOptions();
213214

214-
combinedOptionsChanges.pipe(takeUntil(this._destroy)).subscribe(options => {
215-
if (this._marker) {
216-
this._marker.setOptions(options);
217-
} else {
218-
this._marker = new google.maps.Marker(options);
219-
this._marker.setMap(this.googleMap._googleMap);
220-
this._initializeEventHandlers();
221-
}
215+
combinedOptionsChanges.pipe(take(1)).subscribe(options => {
216+
this._marker = new google.maps.Marker(options);
217+
this._marker.setMap(this.googleMap._googleMap);
218+
this._initializeEventHandlers();
222219
});
220+
221+
this._watchForOptionsChanges();
222+
this._watchForTitleChanges();
223+
this._watchForPositionChanges();
224+
this._watchForLabelChanges();
225+
this._watchForClickableChanges();
223226
}
224227

225228
ngOnDestroy() {
@@ -344,6 +347,46 @@ export class MapMarker implements OnInit, OnDestroy {
344347
}));
345348
}
346349

350+
private _watchForOptionsChanges() {
351+
this._options.pipe(takeUntil(this._destroy)).subscribe(options => {
352+
if (this._marker) {
353+
this._marker.setOptions(options);
354+
}
355+
});
356+
}
357+
358+
private _watchForTitleChanges() {
359+
this._title.pipe(takeUntil(this._destroy)).subscribe(title => {
360+
if (this._marker) {
361+
this._marker.setTitle(title !== undefined ? title : null);
362+
}
363+
});
364+
}
365+
366+
private _watchForPositionChanges() {
367+
this._position.pipe(takeUntil(this._destroy)).subscribe(position => {
368+
if (this._marker) {
369+
this._marker.setPosition(position || null);
370+
}
371+
});
372+
}
373+
374+
private _watchForLabelChanges() {
375+
this._label.pipe(takeUntil(this._destroy)).subscribe(label => {
376+
if (this._marker) {
377+
this._marker.setLabel(label !== undefined ? label : null);
378+
}
379+
});
380+
}
381+
382+
private _watchForClickableChanges() {
383+
this._clickable.pipe(takeUntil(this._destroy)).subscribe(clickable => {
384+
if (this._marker) {
385+
this._marker.setClickable(!!clickable);
386+
}
387+
});
388+
}
389+
347390
private _initializeEventHandlers() {
348391
const eventHandlers = new Map<string, EventEmitter<void>>([
349392
['animation_changed', this.animationChanged],

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ export interface TestingWindow extends Window {
2222
/** Creates a jasmine.SpyObj for a google.maps.Map. */
2323
export function createMapSpy(options: google.maps.MapOptions): jasmine.SpyObj<UpdatedGoogleMap> {
2424
const mapSpy = jasmine.createSpyObj('google.maps.Map', [
25-
'setOptions', 'setMap', 'addListener', 'fitBounds', 'panBy', 'panTo', 'panToBounds',
26-
'getBounds', 'getCenter', 'getClickableIcons', 'getHeading', 'getMapTypeId', 'getProjection',
27-
'getStreetView', 'getTilt', 'getZoom'
25+
'setOptions', 'setCenter', 'setZoom', 'setMap', 'addListener', 'fitBounds', 'panBy', 'panTo',
26+
'panToBounds', 'getBounds', 'getCenter', 'getClickableIcons', 'getHeading', 'getMapTypeId',
27+
'getProjection', 'getStreetView', 'getTilt', 'getZoom'
2828
]);
2929
mapSpy.addListener.and.returnValue({remove: () => {}});
3030
return mapSpy;

0 commit comments

Comments
 (0)