diff --git a/src/components.ts b/src/components.ts index e800cdef1..0cfe0321d 100644 --- a/src/components.ts +++ b/src/components.ts @@ -1 +1,2 @@ export * from './components/google_map'; +export * from './components/google_map_marker'; diff --git a/src/components/google_map.ts b/src/components/google_map.ts index e4b20bd7d..01c8aa0bd 100644 --- a/src/components/google_map.ts +++ b/src/components/google_map.ts @@ -1,20 +1,24 @@ -import {Component, Directive, Input, Output, ContentChild, ElementRef, ViewChild, SimpleChange, NgZone, Provider, EventEmitter} from 'angular2/angular2'; +import {Component, Directive, Provider, Input, Output, Renderer, ContentChildren, ElementRef, SimpleChange, NgZone, EventEmitter, QueryList, provide} from 'angular2/angular2'; import {GoogleMapsAPIWrapper, GoogleMapsAPIWrapperFactory} from '../services/google_maps_api_wrapper'; +import {SebmGoogleMapMarker} from './google_map_marker'; +import {MarkerManager} from '../services/marker_manager'; /** * Todo: add docs */ @Component({ selector: 'sebm-google-map', - providers: [GoogleMapsAPIWrapperFactory], + providers: [GoogleMapsAPIWrapperFactory, MarkerManager], + viewProviders: [MarkerManager], styles: [` - .sebm-google-map-container { - width: 100%; + .sebm-google-map-container-inner { + width: inherit; + height: inherit; display: block; } `], template: ` -
+
` }) @@ -24,8 +28,9 @@ export class SebmGoogleMap { private _zoom: number = 8; private _mapsWrapper: GoogleMapsAPIWrapper; - constructor(private elem: ElementRef, private _zone: NgZone, mapsFactory: GoogleMapsAPIWrapperFactory) { - this._initMapInstance(elem.nativeElement.querySelector('.sebm-google-map-container'), mapsFactory); + constructor(private elem: ElementRef, private _zone: NgZone, mapsFactory: GoogleMapsAPIWrapperFactory, renderer: Renderer) { + renderer.setElementClass(elem, 'sebm-google-map-container', true); + this._initMapInstance(elem.nativeElement.querySelector('.sebm-google-map-container-inner'), mapsFactory); } private _initMapInstance(el: HTMLElement, mapsFactory: GoogleMapsAPIWrapperFactory) { @@ -36,8 +41,10 @@ export class SebmGoogleMap { @Input() set zoom(value: number|string) { - this._zoom = this._convertToDecimal(value); - this._mapsWrapper.setZoom(this._zoom); + this._zoom = this._convertToDecimal(value); + if (typeof this._zoom === 'number') { + this._mapsWrapper.setZoom(this._zoom); + } } @Input() @@ -55,11 +62,16 @@ export class SebmGoogleMap { private _convertToDecimal(value: string|number): number { if (typeof value === 'string') { return parseFloat(value); + } else if (typeof value === 'number') { + return value; } - return value; + return null; } private _updateCenter() { + if (typeof this._latitude !== 'number' || typeof this._longitude !== 'number') { + return; + } this._mapsWrapper.setCenter({ lat: this._latitude, lng: this._longitude, diff --git a/src/components/google_map_marker.ts b/src/components/google_map_marker.ts new file mode 100644 index 000000000..dc7ecf580 --- /dev/null +++ b/src/components/google_map_marker.ts @@ -0,0 +1,48 @@ +import {Directive, Input, provide, Host, Inject, SkipSelf, SimpleChange} from 'angular2/angular2'; +import {GoogleMapsAPIWrapper} from '../services/google_maps_api_wrapper'; +import {MarkerManager} from '../services/marker_manager'; +import {SebmGoogleMap} from './google_map'; + +let markerId = 0; + +@Directive({ + selector: 'sebm-google-map-marker' +}) +export class SebmGoogleMapMarker { + @Input() latitude: number; + @Input() longitude: number; + @Input() title: string; + + private _markerAddedToManger: boolean = false; + private _id: string; + + constructor(@Host() @SkipSelf() private _map: SebmGoogleMap, private _markerManager: MarkerManager) { + this._id = (markerId++).toString(); + } + + onChanges(changes: {[key: string]: SimpleChange}) { + if (!this._markerAddedToManger && this.latitude && this.longitude) { + this._markerManager.addMarker(this); + this._markerAddedToManger = true; + return; + } + if (changes['latitude'] || changes['logitude']) { + this._markerManager.updateMarkerPosition(this); + } + if (changes['title']) { + this._markerManager.updateTitle(this); + } + } + + id(): string { + return this._id; + } + + toString(): string { + return 'SebmGoogleMapMarker-' + this._id.toString(); + } + + onDestroy() { + this._markerManager.deleteMarker(this); + } +} diff --git a/src/custom_typings/google_maps.d.ts b/src/custom_typings/google_maps.d.ts index 2c52478bb..4efb8bf2b 100644 --- a/src/custom_typings/google_maps.d.ts +++ b/src/custom_typings/google_maps.d.ts @@ -15,6 +15,19 @@ declare module google.maps { lng(): number; } + export class Marker { + constructor(options?: MarkerOptions) + setMap(map:Map): void + setPosition(latLng: LatLng|LatLngLiteral): void + setTitle(title: string): void; + } + + export interface MarkerOptions { + position: LatLng|LatLngLiteral; + title?: string; + map?: Map; + } + export interface LatLngLiteral { lat: number; lng: number; diff --git a/src/services/google_maps_api_wrapper.ts b/src/services/google_maps_api_wrapper.ts index 7c3f1e03c..310ca3ed2 100644 --- a/src/services/google_maps_api_wrapper.ts +++ b/src/services/google_maps_api_wrapper.ts @@ -8,13 +8,11 @@ import {Observable} from 'rx'; export class GoogleMapsAPIWrapper { private _el: HTMLElement; private _map: google.maps.Map; - private _isInitialized: boolean; private _centerChangeObservable: Observable; private _zoomChangeObservable: Observable; constructor(_el: HTMLElement, latitude: number, longitude: number, private _zone: NgZone) { - this._isInitialized = true; this._el = _el; this._map = new google.maps.Map(this._el, { center: { @@ -28,9 +26,7 @@ export class GoogleMapsAPIWrapper { createEventObservable(eventName: string, callback: (observer: Rx.Observer) => void): Observable { return Observable.create((observer: Rx.Observer) => { this._map.addListener(eventName, () => { - this._zone.run(() => { - callback(observer); - }); + callback(observer); }); }); } @@ -48,6 +44,14 @@ export class GoogleMapsAPIWrapper { }); } + /** + * Creates a google map marker with the map context + */ + createMarker(options: google.maps.MarkerOptions = {}): google.maps.Marker { + options.map = this._map; + return new google.maps.Marker(options); + } + getZoomChangeObserable(): Observable { return this._zoomChangeObservable; } @@ -65,21 +69,27 @@ export class GoogleMapsAPIWrapper { } getCenter(): google.maps.LatLng { - if (!this._isInitialized) { - return; - } return this._map.getCenter(); } - - isInitialized(): boolean { - return this._isInitialized; - } } +// todo: change name, because it's not a real factory. +// We have to create the instance with the component element and I don't see +// any chances to modify the viewproviders for after the component instance is created. @Injectable() export class GoogleMapsAPIWrapperFactory { + private _instance: GoogleMapsAPIWrapper; + constructor(private _zone: NgZone) {} + create(el: HTMLElement, latitude: number, longitude: number): GoogleMapsAPIWrapper { - return new GoogleMapsAPIWrapper(el, latitude, latitude, this._zone); + if (this._instance) { + throw new Error('instance already created'); + } + return this._instance = new GoogleMapsAPIWrapper(el, latitude, latitude, this._zone); + } + + getInstance(): GoogleMapsAPIWrapper { + return this._instance; } } diff --git a/src/services/marker_manager.ts b/src/services/marker_manager.ts new file mode 100644 index 000000000..515c61d39 --- /dev/null +++ b/src/services/marker_manager.ts @@ -0,0 +1,39 @@ +import {Injectable} from 'angular2/angular2'; +import {SebmGoogleMapMarker} from '../components/google_map_marker'; +import {GoogleMapsAPIWrapperFactory, GoogleMapsAPIWrapper} from './google_maps_api_wrapper'; + +@Injectable() +export class MarkerManager { + private _markers: Map = new Map(); + private _mapsAPI: GoogleMapsAPIWrapper; + + constructor(f: GoogleMapsAPIWrapperFactory) { + this._mapsAPI = f.getInstance(); + } + + deleteMarker(marker: SebmGoogleMapMarker) { + console.log(this._markers.values()); + this._markers.get(marker).setMap(null); + this._markers.delete(marker); + } + + updateMarkerPosition(marker: SebmGoogleMapMarker) { + this._markers.get(marker).setPosition({ + lat: marker.latitude, + lng: marker.longitude + }); + } + + updateTitle(marker: SebmGoogleMapMarker) { + this._markers.get(marker).setTitle(marker.title); + } + + addMarker(marker: SebmGoogleMapMarker) { + this._markers.set(marker, this._mapsAPI.createMarker({ + position: { + lat: marker.latitude, + lng: marker.longitude + } + })); + } +}