Skip to content

Commit 54e64bd

Browse files
mbehrlichjelbourn
authored andcommitted
refactor(google-maps): Removes reference to GoogleMap from MapMarker (#17222)
Get access to the GoogleMap parent component from a MapMarker through the constructor instead of calling a "setMap" method on the marker from the GoogleMap to prevent MapMarker being loaded in all maps.
1 parent 6458c3b commit 54e64bd

File tree

5 files changed

+49
-121
lines changed

5 files changed

+49
-121
lines changed

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

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

55
import {GoogleMapsModule} from '../google-maps-module';
6-
import {MapMarker} from '../map-marker/map-marker';
76
import {
87
createMapConstructorSpy,
98
createMapSpy,
@@ -240,18 +239,6 @@ describe('GoogleMap', () => {
240239
expect(mapSpy.addListener).not.toHaveBeenCalledWith('tilt_changed', jasmine.any(Function));
241240
expect(mapSpy.addListener).not.toHaveBeenCalledWith('zoom_changed', jasmine.any(Function));
242241
});
243-
244-
it('calls setMap on child marker components', () => {
245-
mapSpy = createMapSpy(DEFAULT_OPTIONS);
246-
createMapConstructorSpy(mapSpy).and.callThrough();
247-
248-
const fixture = TestBed.createComponent(TestApp);
249-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
250-
spyOn(markerComponent, '_setMap').and.callThrough();
251-
fixture.detectChanges();
252-
253-
expect(markerComponent._setMap).toHaveBeenCalledWith(mapSpy);
254-
});
255242
});
256243

257244
@Component({
@@ -264,7 +251,6 @@ describe('GoogleMap', () => {
264251
(mapClick)="handleClick($event)"
265252
(centerChanged)="handleCenterChanged()"
266253
(mapRightclick)="handleRightclick($event)">
267-
<map-marker></map-marker>
268254
</google-map>`,
269255
})
270256
class TestApp {

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

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,20 @@
77
*/
88

99
import {
10-
AfterContentInit,
1110
ChangeDetectionStrategy,
1211
Component,
13-
ContentChildren,
1412
ElementRef,
1513
EventEmitter,
1614
Input,
1715
OnChanges,
1816
OnDestroy,
1917
OnInit,
2018
Output,
21-
QueryList,
2219
ViewEncapsulation,
2320
} from '@angular/core';
2421
import {BehaviorSubject, combineLatest, Observable, Subject} from 'rxjs';
2522
import {map, shareReplay, take, takeUntil} from 'rxjs/operators';
2623

27-
import {MapMarker} from '../map-marker/map-marker';
28-
2924
interface GoogleMapsWindow extends Window {
3025
google?: typeof google;
3126
}
@@ -62,7 +57,7 @@ export const DEFAULT_WIDTH = '500px';
6257
template: '<div class="map-container"></div><ng-content></ng-content>',
6358
encapsulation: ViewEncapsulation.None,
6459
})
65-
export class GoogleMap implements OnChanges, OnInit, AfterContentInit, OnDestroy {
60+
export class GoogleMap implements OnChanges, OnInit, OnDestroy {
6661
@Input() height = DEFAULT_HEIGHT;
6762

6863
@Input() width = DEFAULT_WIDTH;
@@ -188,8 +183,6 @@ export class GoogleMap implements OnChanges, OnInit, AfterContentInit, OnDestroy
188183
*/
189184
@Output() zoomChanged = new EventEmitter<void>();
190185

191-
@ContentChildren(MapMarker) _markers: QueryList<MapMarker>;
192-
193186
private _mapEl: HTMLElement;
194187
_googleMap!: UpdatedGoogleMap;
195188

@@ -234,13 +227,6 @@ export class GoogleMap implements OnChanges, OnInit, AfterContentInit, OnDestroy
234227
this._watchForOptionsChanges(combinedOptionsChanges);
235228
}
236229

237-
ngAfterContentInit() {
238-
for (const marker of this._markers.toArray()) {
239-
marker._setMap(this._googleMap);
240-
}
241-
this._watchForMarkerChanges();
242-
}
243-
244230
ngOnDestroy() {
245231
this._destroy.next();
246232
this._destroy.complete();
@@ -472,14 +458,4 @@ export class GoogleMap implements OnChanges, OnInit, AfterContentInit, OnDestroy
472458
}));
473459
}
474460
}
475-
476-
private _watchForMarkerChanges() {
477-
combineLatest(this._googleMapChanges, this._markers.changes)
478-
.pipe(takeUntil(this._destroy))
479-
.subscribe(([googleMap, markers]) => {
480-
for (let marker of markers) {
481-
marker._setMap(googleMap);
482-
}
483-
});
484-
}
485461
}

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

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import {Component} 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';
56
import {
7+
createMapConstructorSpy,
8+
createMapSpy,
69
createMarkerConstructorSpy,
710
createMarkerSpy,
811
TestingWindow
@@ -12,6 +15,8 @@ import {GoogleMapsModule} from '../google-maps-module';
1215
import {DEFAULT_MARKER_OPTIONS, MapMarker} from './map-marker';
1316

1417
describe('MapMarker', () => {
18+
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
19+
1520
beforeEach(async(() => {
1621
TestBed.configureTestingModule({
1722
imports: [GoogleMapsModule],
@@ -21,6 +26,9 @@ describe('MapMarker', () => {
2126

2227
beforeEach(() => {
2328
TestBed.compileComponents();
29+
30+
mapSpy = createMapSpy(DEFAULT_OPTIONS);
31+
createMapConstructorSpy(mapSpy).and.callThrough();
2432
});
2533

2634
afterEach(() => {
@@ -33,28 +41,24 @@ describe('MapMarker', () => {
3341
const markerConstructorSpy = createMarkerConstructorSpy(markerSpy).and.callThrough();
3442

3543
const fixture = TestBed.createComponent(TestApp);
36-
const fakeMap = {} as unknown as google.maps.Map;
37-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
38-
markerComponent._setMap(fakeMap);
3944
fixture.detectChanges();
4045

4146
expect(markerConstructorSpy).toHaveBeenCalledWith({
4247
...DEFAULT_MARKER_OPTIONS,
4348
title: undefined,
4449
label: undefined,
4550
clickable: undefined,
46-
map: fakeMap
51+
map: mapSpy,
4752
});
4853
});
4954

5055
it('sets marker inputs', () => {
51-
const fakeMap = {} as unknown as google.maps.Map;
5256
const options: google.maps.MarkerOptions = {
5357
position: {lat: 3, lng: 5},
5458
title: 'marker title',
5559
label: 'marker label',
5660
clickable: false,
57-
map: fakeMap,
61+
map: mapSpy,
5862
};
5963
const markerSpy = createMarkerSpy(options);
6064
const markerConstructorSpy = createMarkerConstructorSpy(markerSpy).and.callThrough();
@@ -64,15 +68,12 @@ describe('MapMarker', () => {
6468
fixture.componentInstance.title = options.title;
6569
fixture.componentInstance.label = options.label;
6670
fixture.componentInstance.clickable = options.clickable;
67-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
68-
markerComponent._setMap(fakeMap);
6971
fixture.detectChanges();
7072

7173
expect(markerConstructorSpy).toHaveBeenCalledWith(options);
7274
});
7375

7476
it('sets marker options, ignoring map', () => {
75-
const fakeMap = {} as unknown as google.maps.Map;
7677
const options: google.maps.MarkerOptions = {
7778
position: {lat: 3, lng: 5},
7879
title: 'marker title',
@@ -85,15 +86,12 @@ describe('MapMarker', () => {
8586

8687
const fixture = TestBed.createComponent(TestApp);
8788
fixture.componentInstance.options = options;
88-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
89-
markerComponent._setMap(fakeMap);
9089
fixture.detectChanges();
9190

92-
expect(markerConstructorSpy).toHaveBeenCalledWith({...options, map: fakeMap});
91+
expect(markerConstructorSpy).toHaveBeenCalledWith({...options, map: mapSpy});
9392
});
9493

9594
it('gives precedence to specific inputs over options', () => {
96-
const fakeMap = {} as unknown as google.maps.Map;
9795
const options: google.maps.MarkerOptions = {
9896
position: {lat: 3, lng: 5},
9997
title: 'marker title',
@@ -107,7 +105,7 @@ describe('MapMarker', () => {
107105
label: 'updated label',
108106
clickable: true,
109107
icon: 'icon name',
110-
map: fakeMap,
108+
map: mapSpy,
111109
};
112110
const markerSpy = createMarkerSpy(options);
113111
const markerConstructorSpy = createMarkerConstructorSpy(markerSpy).and.callThrough();
@@ -118,43 +116,17 @@ describe('MapMarker', () => {
118116
fixture.componentInstance.label = expectedOptions.label;
119117
fixture.componentInstance.clickable = expectedOptions.clickable;
120118
fixture.componentInstance.options = options;
121-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
122-
markerComponent._setMap(fakeMap);
123119
fixture.detectChanges();
124120

125121
expect(markerConstructorSpy).toHaveBeenCalledWith(expectedOptions);
126122
});
127123

128-
it('sets the map on the marker only once', () => {
129-
const markerSpy = createMarkerSpy(DEFAULT_MARKER_OPTIONS);
130-
const markerConstructorSpy = createMarkerConstructorSpy(markerSpy).and.callThrough();
131-
132-
const fixture = TestBed.createComponent(TestApp);
133-
const fakeMap = {} as unknown as google.maps.Map;
134-
const fakeMap2 = {testValue: 'test'} as unknown as google.maps.Map;
135-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
136-
markerComponent._setMap(fakeMap);
137-
markerComponent._setMap(fakeMap2);
138-
fixture.detectChanges();
139-
140-
expect(markerConstructorSpy).toHaveBeenCalledWith({
141-
...DEFAULT_MARKER_OPTIONS,
142-
title: undefined,
143-
label: undefined,
144-
clickable: undefined,
145-
map: fakeMap
146-
});
147-
expect(markerSpy.setOptions).not.toHaveBeenCalled();
148-
});
149-
150124
it('exposes methods that provide information about the marker', () => {
151125
const markerSpy = createMarkerSpy(DEFAULT_MARKER_OPTIONS);
152126
createMarkerConstructorSpy(markerSpy).and.callThrough();
153127

154128
const fixture = TestBed.createComponent(TestApp);
155-
const fakeMap = {} as unknown as google.maps.Map;
156129
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
157-
markerComponent._setMap(fakeMap);
158130
fixture.detectChanges();
159131

160132
markerSpy.getAnimation.and.returnValue(null);
@@ -199,9 +171,6 @@ describe('MapMarker', () => {
199171
createMarkerConstructorSpy(markerSpy).and.callThrough();
200172

201173
const fixture = TestBed.createComponent(TestApp);
202-
const fakeMap = {} as unknown as google.maps.Map;
203-
const markerComponent = fixture.debugElement.query(By.directive(MapMarker)).componentInstance;
204-
markerComponent._setMap(fakeMap);
205174
fixture.detectChanges();
206175

207176
expect(markerSpy.addListener)
@@ -234,13 +203,16 @@ describe('MapMarker', () => {
234203

235204
@Component({
236205
selector: 'test-app',
237-
template: `<map-marker [title]="title"
238-
[position]="position"
239-
[label]="label"
240-
[clickable]="clickable"
241-
[options]="options"
242-
(mapClick)="handleClick()"
243-
(positionChanged)="handlePositionChanged()"></map-marker>`,
206+
template: `<google-map>
207+
<map-marker [title]="title"
208+
[position]="position"
209+
[label]="label"
210+
[clickable]="clickable"
211+
[options]="options"
212+
(mapClick)="handleClick()"
213+
(positionChanged)="handlePositionChanged()">
214+
</map-marker>
215+
</google-map>`,
244216
})
245217
class TestApp {
246218
title?: string;

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

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import {
1616
Output,
1717
ViewEncapsulation
1818
} from '@angular/core';
19-
import {BehaviorSubject, combineLatest, Observable, ReplaySubject, Subject} from 'rxjs';
19+
import {BehaviorSubject, combineLatest, Observable, Subject} from 'rxjs';
2020
import {map, takeUntil} from 'rxjs/operators';
2121

22+
import {GoogleMap} from '../google-map/google-map';
23+
2224
/**
2325
* Default options for the Google Maps marker component. Displays a marker
2426
* at the Googleplex.
@@ -198,30 +200,26 @@ export class MapMarker implements OnInit, OnDestroy {
198200
new BehaviorSubject<string|google.maps.MarkerLabel|undefined>(undefined);
199201
private readonly _clickable = new BehaviorSubject<boolean|undefined>(undefined);
200202

201-
private readonly _map = new ReplaySubject<google.maps.Map>(1);
202-
203203
private readonly _destroy = new Subject<void>();
204204

205205
private readonly _listeners: google.maps.MapsEventListener[] = [];
206206

207-
private _hasMap = false;
208-
209207
_marker?: google.maps.Marker;
210208

209+
constructor(private readonly googleMap: GoogleMap) {}
210+
211211
ngOnInit() {
212212
const combinedOptionsChanges = this._combineOptions();
213213

214-
combineLatest(this._map, combinedOptionsChanges)
215-
.pipe(takeUntil(this._destroy))
216-
.subscribe(([googleMap, options]) => {
217-
if (this._marker) {
218-
this._marker.setOptions(options);
219-
} else {
220-
this._marker = new google.maps.Marker(options);
221-
this._marker.setMap(googleMap);
222-
this._initializeEventHandlers();
223-
}
224-
});
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+
}
222+
});
225223
}
226224

227225
ngOnDestroy() {
@@ -235,13 +233,6 @@ export class MapMarker implements OnInit, OnDestroy {
235233
}
236234
}
237235

238-
_setMap(googleMap: google.maps.Map) {
239-
if (!this._hasMap) {
240-
this._map.next(googleMap);
241-
this._hasMap = true;
242-
}
243-
}
244-
245236
/**
246237
* See
247238
* developers.google.com/maps/documentation/javascript/reference/marker#Marker.getAnimation
@@ -339,16 +330,15 @@ export class MapMarker implements OnInit, OnDestroy {
339330
}
340331

341332
private _combineOptions(): Observable<google.maps.MarkerOptions> {
342-
return combineLatest(
343-
this._options, this._title, this._position, this._label, this._clickable, this._map)
344-
.pipe(map(([options, title, position, label, clickable, googleMap]) => {
333+
return combineLatest(this._options, this._title, this._position, this._label, this._clickable)
334+
.pipe(map(([options, title, position, label, clickable]) => {
345335
const combinedOptions: google.maps.MarkerOptions = {
346336
...options,
347337
title: title || options.title,
348338
position: position || options.position,
349339
label: label || options.label,
350340
clickable: clickable !== undefined ? clickable : options.clickable,
351-
map: googleMap || null,
341+
map: this.googleMap._googleMap || null,
352342
};
353343
return combinedOptions;
354344
}));

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,15 @@ export function createMarkerConstructorSpy(markerSpy: jasmine.SpyObj<google.maps
6868
return markerSpy;
6969
});
7070
const testingWindow: TestingWindow = window;
71-
testingWindow.google = {
72-
maps: {
73-
'Marker': markerConstructorSpy,
74-
},
75-
};
71+
if (testingWindow.google && testingWindow.google.maps) {
72+
testingWindow.google.maps['Marker'] = markerConstructorSpy;
73+
} else {
74+
testingWindow.google = {
75+
maps: {
76+
'Marker': markerConstructorSpy,
77+
},
78+
};
79+
}
7680
return markerConstructorSpy;
7781
}
7882

0 commit comments

Comments
 (0)