Skip to content

Commit 0e88125

Browse files
authored
refactor(google-maps): simplify marker internal setup (#20975)
Currently the various Google Maps sub-components are set up so that each input has a setter, a `BehaviorSubject` and a method to watch it for changes. This doesn't scale very well on components with lots of inputs so these changes use an alternate one where we synchronously update the marker state whenever an input changes.
1 parent de5a940 commit 0e88125

File tree

2 files changed

+93
-97
lines changed

2 files changed

+93
-97
lines changed

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

Lines changed: 90 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@
99
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
1010
/// <reference types="googlemaps" />
1111

12-
import {Input, OnDestroy, OnInit, Output, NgZone, Directive} from '@angular/core';
13-
import {BehaviorSubject, combineLatest, Observable, Subject} from 'rxjs';
14-
import {map, take, takeUntil} from 'rxjs/operators';
12+
import {
13+
Input,
14+
OnDestroy,
15+
OnInit,
16+
Output,
17+
NgZone,
18+
Directive,
19+
OnChanges,
20+
SimpleChanges,
21+
} from '@angular/core';
22+
import {Observable} from 'rxjs';
1523

1624
import {GoogleMap} from '../google-map/google-map';
1725
import {MapEventManager} from '../map-event-manager';
@@ -34,42 +42,58 @@ export const DEFAULT_MARKER_OPTIONS = {
3442
selector: 'map-marker',
3543
exportAs: 'mapMarker',
3644
})
37-
export class MapMarker implements OnInit, OnDestroy, MapAnchorPoint {
45+
export class MapMarker implements OnInit, OnChanges, OnDestroy, MapAnchorPoint {
3846
private _eventManager = new MapEventManager(this._ngZone);
39-
private readonly _options =
40-
new BehaviorSubject<google.maps.MarkerOptions>(DEFAULT_MARKER_OPTIONS);
41-
private readonly _title = new BehaviorSubject<string|undefined>(undefined);
42-
private readonly _position =
43-
new BehaviorSubject<google.maps.LatLngLiteral|google.maps.LatLng|undefined>(undefined);
44-
private readonly _label =
45-
new BehaviorSubject<string|google.maps.MarkerLabel|undefined>(undefined);
46-
private readonly _clickable = new BehaviorSubject<boolean|undefined>(undefined);
47-
private readonly _destroy = new Subject<void>();
48-
49-
@Input()
50-
set options(options: google.maps.MarkerOptions) {
51-
this._options.next(options || DEFAULT_MARKER_OPTIONS);
52-
}
5347

48+
/**
49+
* Title of the marker.
50+
* See: developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions.title
51+
*/
5452
@Input()
5553
set title(title: string) {
56-
this._title.next(title);
54+
this._title = title;
5755
}
56+
private _title: string;
5857

58+
/**
59+
* Title of the marker. See:
60+
* developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions.position
61+
*/
5962
@Input()
6063
set position(position: google.maps.LatLngLiteral|google.maps.LatLng) {
61-
this._position.next(position);
64+
this._position = position;
6265
}
66+
private _position: google.maps.LatLngLiteral|google.maps.LatLng;
6367

68+
/**
69+
* Label for the marker.
70+
* See: developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions.label
71+
*/
6472
@Input()
6573
set label(label: string|google.maps.MarkerLabel) {
66-
this._label.next(label);
74+
this._label = label;
6775
}
76+
private _label: string|google.maps.MarkerLabel;
6877

78+
/**
79+
* Whether the marker is clickable. See:
80+
* developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions.clickable
81+
*/
6982
@Input()
7083
set clickable(clickable: boolean) {
71-
this._clickable.next(clickable);
84+
this._clickable = clickable;
7285
}
86+
private _clickable: boolean;
87+
88+
/**
89+
* Options used to configure the marker.
90+
* See: developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions
91+
*/
92+
@Input()
93+
set options(options: google.maps.MarkerOptions) {
94+
this._options = options;
95+
}
96+
private _options: google.maps.MarkerOptions;
7397

7498
/**
7599
* See
@@ -239,27 +263,45 @@ export class MapMarker implements OnInit, OnDestroy, MapAnchorPoint {
239263

240264
ngOnInit() {
241265
if (this._googleMap._isBrowser) {
242-
this._combineOptions().pipe(take(1)).subscribe(options => {
243-
// Create the object outside the zone so its events don't trigger change detection.
244-
// We'll bring it back in inside the `MapEventManager` only for the events that the
245-
// user has subscribed to.
246-
this._ngZone.runOutsideAngular(() => this.marker = new google.maps.Marker(options));
247-
this._assertInitialized();
248-
this.marker.setMap(this._googleMap.googleMap!);
249-
this._eventManager.setTarget(this.marker);
266+
// Create the object outside the zone so its events don't trigger change detection.
267+
// We'll bring it back in inside the `MapEventManager` only for the events that the
268+
// user has subscribed to.
269+
this._ngZone.runOutsideAngular(() => {
270+
this.marker = new google.maps.Marker(this._combineOptions());
250271
});
272+
this._assertInitialized();
273+
this.marker.setMap(this._googleMap.googleMap!);
274+
this._eventManager.setTarget(this.marker);
275+
}
276+
}
277+
278+
ngOnChanges(changes: SimpleChanges) {
279+
const {marker, _title, _position, _label, _clickable} = this;
280+
281+
if (marker) {
282+
if (changes.options) {
283+
marker.setOptions(this._combineOptions());
284+
}
251285

252-
this._watchForOptionsChanges();
253-
this._watchForTitleChanges();
254-
this._watchForPositionChanges();
255-
this._watchForLabelChanges();
256-
this._watchForClickableChanges();
286+
if (changes.title && _title !== undefined) {
287+
marker.setTitle(_title);
288+
}
289+
290+
if (changes.position && _position) {
291+
marker.setPosition(_position);
292+
}
293+
294+
if (changes.label && _label !== undefined) {
295+
marker.setLabel(_label);
296+
}
297+
298+
if (changes.clickable && _clickable !== undefined) {
299+
marker.setClickable(_clickable);
300+
}
257301
}
258302
}
259303

260304
ngOnDestroy() {
261-
this._destroy.next();
262-
this._destroy.complete();
263305
this._eventManager.destroy();
264306
if (this.marker) {
265307
this.marker.setMap(null);
@@ -380,64 +422,17 @@ export class MapMarker implements OnInit, OnDestroy, MapAnchorPoint {
380422
return this.marker;
381423
}
382424

383-
private _combineOptions(): Observable<google.maps.MarkerOptions> {
384-
return combineLatest([this._options, this._title, this._position, this._label, this._clickable])
385-
.pipe(map(([options, title, position, label, clickable]) => {
386-
const combinedOptions: google.maps.MarkerOptions = {
387-
...options,
388-
title: title || options.title,
389-
position: position || options.position,
390-
label: label || options.label,
391-
clickable: clickable !== undefined ? clickable : options.clickable,
392-
map: this._googleMap.googleMap,
393-
};
394-
return combinedOptions;
395-
}));
396-
}
397-
398-
private _watchForOptionsChanges() {
399-
this._options.pipe(takeUntil(this._destroy)).subscribe(options => {
400-
if (this.marker) {
401-
this._assertInitialized();
402-
this.marker.setOptions(options);
403-
}
404-
});
405-
}
406-
407-
private _watchForTitleChanges() {
408-
this._title.pipe(takeUntil(this._destroy)).subscribe(title => {
409-
if (this.marker && title !== undefined) {
410-
this._assertInitialized();
411-
this.marker.setTitle(title);
412-
}
413-
});
414-
}
415-
416-
private _watchForPositionChanges() {
417-
this._position.pipe(takeUntil(this._destroy)).subscribe(position => {
418-
if (this.marker && position) {
419-
this._assertInitialized();
420-
this.marker.setPosition(position);
421-
}
422-
});
423-
}
424-
425-
private _watchForLabelChanges() {
426-
this._label.pipe(takeUntil(this._destroy)).subscribe(label => {
427-
if (this.marker && label !== undefined) {
428-
this._assertInitialized();
429-
this.marker.setLabel(label);
430-
}
431-
});
432-
}
433-
434-
private _watchForClickableChanges() {
435-
this._clickable.pipe(takeUntil(this._destroy)).subscribe(clickable => {
436-
if (this.marker && clickable !== undefined) {
437-
this._assertInitialized();
438-
this.marker.setClickable(clickable);
439-
}
440-
});
425+
/** Creates a combined options object using the passed-in options and the individual inputs. */
426+
private _combineOptions(): google.maps.MarkerOptions {
427+
const options = this._options || DEFAULT_MARKER_OPTIONS;
428+
return {
429+
...options,
430+
title: this._title || options.title,
431+
position: this._position || options.position,
432+
label: this._label || options.label,
433+
clickable: this._clickable !== undefined ? this._clickable : options.clickable,
434+
map: this._googleMap.googleMap,
435+
};
441436
}
442437

443438
private _assertInitialized(): asserts this is {marker: google.maps.Marker} {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export declare class MapKmlLayer implements OnInit, OnDestroy {
171171
static ɵfac: i0.ɵɵFactoryDef<MapKmlLayer, never>;
172172
}
173173

174-
export declare class MapMarker implements OnInit, OnDestroy, MapAnchorPoint {
174+
export declare class MapMarker implements OnInit, OnChanges, OnDestroy, MapAnchorPoint {
175175
animationChanged: Observable<void>;
176176
set clickable(clickable: boolean);
177177
clickableChanged: Observable<void>;
@@ -213,9 +213,10 @@ export declare class MapMarker implements OnInit, OnDestroy, MapAnchorPoint {
213213
getTitle(): string | null;
214214
getVisible(): boolean;
215215
getZIndex(): number | null;
216+
ngOnChanges(changes: SimpleChanges): void;
216217
ngOnDestroy(): void;
217218
ngOnInit(): void;
218-
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MapMarker, "map-marker", ["mapMarker"], { "options": "options"; "title": "title"; "position": "position"; "label": "label"; "clickable": "clickable"; }, { "animationChanged": "animationChanged"; "mapClick": "mapClick"; "clickableChanged": "clickableChanged"; "cursorChanged": "cursorChanged"; "mapDblclick": "mapDblclick"; "mapDrag": "mapDrag"; "mapDragend": "mapDragend"; "draggableChanged": "draggableChanged"; "mapDragstart": "mapDragstart"; "flatChanged": "flatChanged"; "iconChanged": "iconChanged"; "mapMousedown": "mapMousedown"; "mapMouseout": "mapMouseout"; "mapMouseover": "mapMouseover"; "mapMouseup": "mapMouseup"; "positionChanged": "positionChanged"; "mapRightclick": "mapRightclick"; "shapeChanged": "shapeChanged"; "titleChanged": "titleChanged"; "visibleChanged": "visibleChanged"; "zindexChanged": "zindexChanged"; }, never>;
219+
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MapMarker, "map-marker", ["mapMarker"], { "title": "title"; "position": "position"; "label": "label"; "clickable": "clickable"; "options": "options"; }, { "animationChanged": "animationChanged"; "mapClick": "mapClick"; "clickableChanged": "clickableChanged"; "cursorChanged": "cursorChanged"; "mapDblclick": "mapDblclick"; "mapDrag": "mapDrag"; "mapDragend": "mapDragend"; "draggableChanged": "draggableChanged"; "mapDragstart": "mapDragstart"; "flatChanged": "flatChanged"; "iconChanged": "iconChanged"; "mapMousedown": "mapMousedown"; "mapMouseout": "mapMouseout"; "mapMouseover": "mapMouseover"; "mapMouseup": "mapMouseup"; "positionChanged": "positionChanged"; "mapRightclick": "mapRightclick"; "shapeChanged": "shapeChanged"; "titleChanged": "titleChanged"; "visibleChanged": "visibleChanged"; "zindexChanged": "zindexChanged"; }, never>;
219220
static ɵfac: i0.ɵɵFactoryDef<MapMarker, never>;
220221
}
221222

0 commit comments

Comments
 (0)