Skip to content

Commit 44f46bb

Browse files
committed
feat(google-maps): Add google-map-marker component
Add a google-map-marker component that when used as a child to a google-map component, will display a marker on the map.
1 parent d2baacc commit 44f46bb

File tree

13 files changed

+356
-1
lines changed

13 files changed

+356
-1
lines changed

packages.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ MATERIAL_SCSS_LIBS = [
8282

8383
GOOGLE_MAPS_PACKAGES = [
8484
"google-map",
85+
"google-map-marker",
8586
]
8687

8788
GOOGLE_MAPS_TARGETS = ["//src/google-maps"] + ["//src/google-maps/%s" % p for p in GOOGLE_MAPS_PACKAGES]

src/dev-app/system-config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ var MATERIAL_PACKAGES = [
5757

5858
var GOOGLE_MAPS_PACKAGES = [
5959
'google-map',
60+
'google-map-marker',
6061
];
6162

6263
var MATERIAL_EXPERIMENTAL_PACKAGES = [
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load(
4+
"//tools:defaults.bzl",
5+
"markdown_to_html",
6+
"ng_module",
7+
"ng_test_library",
8+
"ng_web_test_suite",
9+
)
10+
11+
ng_module(
12+
name = "google-map-marker",
13+
srcs = glob(
14+
["**/*.ts"],
15+
exclude = ["**/*.spec.ts"],
16+
),
17+
module_name = "@angular/google-maps/google-map-marker",
18+
deps = [
19+
"@npm//@angular/core",
20+
"@npm//@types/googlemaps",
21+
"@npm//rxjs",
22+
],
23+
)
24+
25+
ng_test_library(
26+
name = "unit_test_sources",
27+
srcs = glob(
28+
["**/*.spec.ts"],
29+
exclude = ["**/*.e2e.spec.ts"],
30+
),
31+
deps = [
32+
":google-map-marker",
33+
"@npm//@angular/platform-browser",
34+
],
35+
)
36+
37+
ng_web_test_suite(
38+
name = "unit_tests",
39+
deps = [":unit_test_sources"],
40+
)
41+
42+
markdown_to_html(
43+
name = "overview",
44+
srcs = [":google-map-marker.md"],
45+
)
46+
47+
filegroup(
48+
name = "source-files",
49+
srcs = glob(["**/*.ts"]),
50+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { NgModule } from '@angular/core';
10+
import { GoogleMapMarker } from './google-map-marker';
11+
12+
@NgModule({
13+
exports: [GoogleMapMarker],
14+
declarations: [GoogleMapMarker],
15+
})
16+
export class GoogleMapMarkerModule { }

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

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
descript('GoogleMapMarker', () => {});
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
2+
import {BehaviorSubject, combineLatest, Observable, ReplaySubject, Subject} from 'rxjs';
3+
import {map, takeUntil} from 'rxjs/operators';
4+
5+
export const DEFAULT_OPTIONS = {
6+
position: { lat: 37.421995, lng: -122.084092 },
7+
};
8+
9+
/**
10+
* Angular component that renders a Google Maps marker via the Google Maps JavaScript API.
11+
* @see https://developers.google.com/maps/documentation/javascript/reference/marker
12+
*/
13+
@Component({
14+
selector: 'google-map-marker',
15+
template: '<ng-content></ng-content>',
16+
changeDetection: ChangeDetectionStrategy.OnPush,
17+
})
18+
export class GoogleMapMarker implements OnInit, OnDestroy {
19+
@Input() set options(options: google.maps.MarkerOptions) {
20+
this._options.next(options || DEFAULT_OPTIONS);
21+
}
22+
23+
@Input() set title(title: string) {
24+
this._title.next(title);
25+
}
26+
27+
@Input() set position(position: google.maps.LatLngLiteral) {
28+
this._position.next(position);
29+
}
30+
31+
@Input() set label(label: string|google.maps.MarkerLabel) {
32+
this._label.next(label);
33+
}
34+
35+
@Input() set clickable(clickable: boolean) {
36+
this._clickable.next(clickable);
37+
}
38+
39+
/** */
40+
@Output() animationChanged = new EventEmitter<void>();
41+
42+
/** */
43+
@Output() mapClick = new EventEmitter<google.maps.MouseEvent>();
44+
45+
/** */
46+
@Output() clickableChanged = new EventEmitter<void>();
47+
48+
/** */
49+
@Output() cursorChanged = new EventEmitter<void>();
50+
51+
/** */
52+
@Output() mapDblclick = new EventEmitter<google.maps.MouseEvent>();
53+
54+
/** */
55+
@Output() mapDrag = new EventEmitter<google.maps.MouseEvent>();
56+
57+
/** */
58+
@Output() mapDragend = new EventEmitter<google.maps.MouseEvent>();
59+
60+
/** */
61+
@Output() draggableChanged = new EventEmitter<void>();
62+
63+
/** */
64+
@Output() mapDragstart = new EventEmitter<google.maps.MouseEvent>();
65+
66+
/** */
67+
@Output() flatChanged = new EventEmitter<void>();
68+
69+
/** */
70+
@Output() iconChanged = new EventEmitter<void>();
71+
72+
/** */
73+
@Output() mapMousedown = new EventEmitter<google.maps.MouseEvent>();
74+
75+
/** */
76+
@Output() mapMouseout = new EventEmitter<google.maps.MouseEvent>();
77+
78+
/** */
79+
@Output() mapMouseover = new EventEmitter<google.maps.MouseEvent>();
80+
81+
/** */
82+
@Output() mapMouseup = new EventEmitter<google.maps.MouseEvent>();
83+
84+
/** */
85+
@Output() positionChanged = new EventEmitter<void>();
86+
87+
/** */
88+
@Output() mapRightclick = new EventEmitter<google.maps.MouseEvent>();
89+
90+
/** */
91+
@Output() shapeChanged = new EventEmitter<void>();
92+
93+
/** */
94+
@Output() titleChanged = new EventEmitter<void>();
95+
96+
/** */
97+
@Output() visibleChanged = new EventEmitter<void>();
98+
99+
/** */
100+
@Output() zindexChanged = new EventEmitter<void>();
101+
102+
private readonly _options = new BehaviorSubject<google.maps.MarkerOptions>(DEFAULT_OPTIONS);
103+
private readonly _title = new BehaviorSubject<string|undefined>(undefined);
104+
private readonly _position = new BehaviorSubject<google.maps.LatLngLiteral|undefined>(undefined);
105+
private readonly _label = new BehaviorSubject<string|google.maps.MarkerLabel|undefined>(undefined);
106+
private readonly _clickable = new BehaviorSubject<boolean>(true);
107+
108+
private readonly _map = new ReplaySubject<google.maps.Map>(1);
109+
110+
private readonly _destroy = new Subject<void>();
111+
112+
private readonly _listeners: google.maps.MapsEventListener[] = [];
113+
114+
private _marker?: google.maps.Marker;
115+
private _hasMap = false;
116+
117+
ngOnInit() {
118+
const combinedOptionsChanges = this._combineOptions();
119+
120+
combineLatest(this._map, combinedOptionsChanges).pipe(takeUntil(this._destroy)).subscribe(([map, options]) => {
121+
if (this._marker) {
122+
this._marker.setOptions(options);
123+
} else {
124+
this._marker = new google.maps.Marker(options);
125+
this._marker.setMap(map);
126+
this._initializeEventHandlers();
127+
}
128+
});
129+
}
130+
131+
ngOnDestroy() {
132+
this._destroy.next();
133+
this._destroy.complete();
134+
for (let listener of this._listeners) {
135+
listener.remove();
136+
}
137+
if (this._marker) {
138+
this._marker.setMap(null);
139+
}
140+
}
141+
142+
setMap(map: google.maps.Map) {
143+
if (!this._hasMap) {
144+
this._map.next(map);
145+
this._hasMap = true;
146+
}
147+
}
148+
149+
getAnimation(): google.maps.Animation|null {
150+
return this._marker!.getAnimation() || null;
151+
}
152+
153+
getClickable(): boolean {
154+
return this._marker!.getClickable();
155+
}
156+
157+
getCursor(): string|null {
158+
return this._marker!.getCursor() || null;
159+
}
160+
161+
getDraggable(): boolean {
162+
return !!this._marker!.getDraggable();
163+
}
164+
165+
getIcon(): string|google.maps.Icon|google.maps.Symbol|null {
166+
return this._marker!.getIcon() || null;
167+
}
168+
169+
getLabel(): google.maps.MarkerLabel|null {
170+
return this._marker!.getLabel() || null;
171+
}
172+
173+
getOpacity(): number|null {
174+
return this._marker!.getOpacity() || null;
175+
}
176+
177+
getPosition(): google.maps.LatLng|null {
178+
return this._marker!.getPosition() || null;
179+
}
180+
181+
getShape(): google.maps.MarkerShape|null {
182+
return this._marker!.getShape() || null;
183+
}
184+
185+
getTitle(): string|null {
186+
return this._marker!.getTitle() || null;
187+
}
188+
189+
getVisible(): boolean {
190+
return this._marker!.getVisible();
191+
}
192+
193+
getZIndex(): number|null {
194+
return this._marker!.getZIndex() || null;
195+
}
196+
197+
private _combineOptions(): Observable<google.maps.MarkerOptions> {
198+
return combineLatest(this._options, this._title, this._position, this._label, this._clickable, this._map)
199+
.pipe(map(([options, title, position, label, clickable, map]) => {
200+
const combinedOptions: google.maps.MarkerOptions = {
201+
...options,
202+
title: title || options.title,
203+
position: position || options.position,
204+
label: label || options.label,
205+
clickable: clickable || options.clickable,
206+
map: map || null,
207+
};
208+
return combinedOptions;
209+
}));
210+
}
211+
212+
private _initializeEventHandlers() {
213+
const eventHandlers = new Map<string, EventEmitter<void>>([]);
214+
const mouseEventHandlers = new Map<string, EventEmitter<google.maps.MouseEvent>>([]);
215+
216+
eventHandlers.forEach((eventHandler: EventEmitter<void>, name: string) => {
217+
if (eventHandler.observers.length > 0) {
218+
this._listeners.push(this._marker!.addListener(name, () => {
219+
eventHandler.emit();
220+
}));
221+
}
222+
});
223+
mouseEventHandlers.forEach((eventHandler: EventEmitter<google.maps.MouseEvent>, name: string) => {
224+
if (eventHandler.observers.length > 0) {
225+
this._listeners.push(this._marker!.addListener(name, (event: google.maps.MouseEvent) => {
226+
eventHandler.emit(event);
227+
}));
228+
}
229+
});
230+
}
231+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './public-api';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './google-map-marker-module';
10+
export * from './google-map-marker';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": "../tsconfig-build",
3+
"files": [
4+
"public-api.ts",
5+
"../typings.d.ts"
6+
],
7+
"angularCompilerOptions": {
8+
"annotateForClosureCompiler": true,
9+
"strictMetadataEmit": true,
10+
"flatModuleOutFile": "index.js",
11+
"flatModuleId": "@angular/google-maps/google-map-marker",
12+
"skipTemplateCodegen": true,
13+
"fullTemplateTypeCheck": true
14+
}
15+
}

src/google-maps/google-map/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ng_module(
1919
"@npm//@angular/core",
2020
"@npm//@types/googlemaps",
2121
"@npm//rxjs",
22+
"//src/google-maps/google-map-marker",
2223
],
2324
)
2425

0 commit comments

Comments
 (0)