Skip to content

Commit

Permalink
refactor(googleMaps): refactor API wrapper
Browse files Browse the repository at this point in the history
- Use an APIWrapperFactory to pass the element to a new instance
apiWrapper.
- make inputs private in <sebm-google-map> component
- add typings for local development
  • Loading branch information
sebholstein committed Nov 6, 2015
1 parent 120a5df commit 4b90bfb
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 73 deletions.
90 changes: 45 additions & 45 deletions src/components/google_map.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,79 @@
import {Component, Directive, Input, ContentChild, ViewEncapsulation, ElementRef, ViewChild, SimpleChange, NgZone} from 'angular2/angular2';
import {GoogleMapsAPIWrapper} from '../services/google_maps_api_wrapper';
import {Component, Directive, Input, Output, ContentChild, ElementRef, ViewChild, SimpleChange, NgZone, Provider, EventEmitter} from 'angular2/angular2';
import {GoogleMapsAPIWrapper, GoogleMapsAPIWrapperFactory} from '../services/google_maps_api_wrapper';

/**
* Container directive to create the Google Maps instance on a child element.
*/
@Directive({
selector: '[sebm-google-map-container]',
})
class SebmGoogleMapContainer {
constructor(private _el: ElementRef) {}

getNativeElement(): HTMLElement {
return this._el.nativeElement;
}
}
/**
* Todo: add docs
*/
@Component({
selector: 'sebm-google-map',
directives: [SebmGoogleMapContainer],
providers: [GoogleMapsAPIWrapper],
providers: [GoogleMapsAPIWrapperFactory],
styles: [`
sebm-google-map-container {
.sebm-google-map-container {
width: 100%;
display: block;
}
`],
template: `
<div sebm-google-map-container class="sebm-google-map-container"></div>
<div class="sebm-google-map-container"></div>
<ng-content></ng-content>
`
})
export class SebmGoogleMap {
@ViewChild(SebmGoogleMapContainer) private _container: SebmGoogleMapContainer;
@Input() longitude: number = 0;
@Input() latitude: number = 0;
@Input() zoom: number = 8;
private _longitude: number = 0;
private _latitude: number = 0;
private _zoom: number = 8;
private _mapsWrapper: GoogleMapsAPIWrapper;

constructor(private _zone: NgZone, private _mapsWrapper: GoogleMapsAPIWrapper) {}
constructor(private elem: ElementRef, private _zone: NgZone, mapsFactory: GoogleMapsAPIWrapperFactory) {
this._initMapInstance(elem.nativeElement.querySelector('.sebm-google-map-container'), mapsFactory);
}

afterViewInit() {
this._initMapInstance(this._container.getNativeElement());
private _initMapInstance(el: HTMLElement, mapsFactory: GoogleMapsAPIWrapperFactory) {
this._mapsWrapper = mapsFactory.create(el, this._latitude, this._longitude);
this._handleMapsCenterChanged();
this._handleZoomChanged();
}

onChanges(changes: {[key:string]: SimpleChange}) {
if (!this._mapsWrapper.isInitialized()) {
return;
}
this._updateLatLng(changes);
@Input()
set zoom(value: number|string) {
this._zoom = this._convertToDecimal(value);
this._mapsWrapper.setZoom(this._zoom);
}

@Input()
set longitude(value: number|string) {
this._longitude = this._convertToDecimal(value);
this._updateCenter();
}

@Input()
set latitude(value: number|string) {
this._latitude = this._convertToDecimal(value);
this._updateCenter();
}

private _updateLatLng(changes: {[key:string]: SimpleChange}) {
if (changes['latitude'] || changes['longitude']) {
this._mapsWrapper.panTo({
lat: this.latitude,
lng: this.longitude,
});
private _convertToDecimal(value: string|number): number {
if (typeof value === 'string') {
return parseFloat(value);
}
return <number> value;
}

private _initMapInstance(el: HTMLElement) {
this._mapsWrapper.initialize(el, this.latitude, this.longitude, this.zoom);
this._handleMapsCenterChanged();
this._handleZoomChanged();
private _updateCenter() {
this._mapsWrapper.setCenter({
lat: this._latitude,
lng: this._longitude,
});
}

private _handleMapsCenterChanged() {
this._mapsWrapper.getCenterChangeObservable().subscribe((latLng: google.maps.LatLngOptions) => {
this.latitude = latLng.lat;
this.longitude = latLng.lng;
this._mapsWrapper.getCenterChangeObservable().subscribe((latLng: google.maps.LatLngLiteral) => {
this._latitude = latLng.lat;
this._longitude = latLng.lng;
});
}

private _handleZoomChanged() {
this._mapsWrapper.getZoomChangeObserable().subscribe((zoom: number) => this.zoom = zoom);
this._mapsWrapper.getZoomChangeObserable().subscribe((zoom: number) => this._zoom = zoom);
}
}
9 changes: 5 additions & 4 deletions src/custom_typings/google_maps.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
declare module google.maps {
export class Map {
constructor (el: HTMLElement, opts?: MapOptions);
panTo(latLng: LatLng|LatLngOptions): void;
panTo(latLng: LatLng|LatLngLiteral): void;
setZoom(zoom: number): void;
addListener(eventName: string, fn: Function): void;
getCenter(): LatLng;
setCenter(latLng: LatLng|LatLngLiteral): void;
getZoom(): number;
}

Expand All @@ -14,13 +15,13 @@ declare module google.maps {
lng(): number;
}

export interface LatLngOptions {
export interface LatLngLiteral {
lat: number;
lng: number;
}

export interface MapOptions {
center: LatLng|LatLngOptions;
zoom: number;
center?: LatLng|LatLngLiteral;
zoom?: number;
}
}
54 changes: 31 additions & 23 deletions src/services/google_maps_api_wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Injectable, Inject, NgZone} from 'angular2/angular2';
import {Injectable, Inject, NgZone, ElementRef} from 'angular2/angular2';
import {Observable} from 'rx';

/**
Expand All @@ -10,10 +10,19 @@ export class GoogleMapsAPIWrapper {
private _map: google.maps.Map;
private _isInitialized: boolean;

private _centerChangeObservable: Observable<google.maps.LatLngOptions>;
private _centerChangeObservable: Observable<google.maps.LatLngLiteral>;
private _zoomChangeObservable: Observable<number>;

constructor(private _zone: NgZone) {
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: {
lat: latitude,
lng: longitude
}
});
this._createObservables();
}

createEventObservable<E>(eventName: string, callback: (observer: Rx.Observer<E>) => void): Observable<E> {
Expand All @@ -26,24 +35,8 @@ export class GoogleMapsAPIWrapper {
});
}

initialize(_el: HTMLElement, latitude: number, longitude: number, zoom: number) {
if (this._isInitialized) {
throw new Error('GooelMapsAPIWrapper is already initialized!');
}
this._isInitialized = true;
this._el = _el;
this._map = new google.maps.Map(this._el, {
center: {
lat: latitude,
lng: longitude
},
zoom: zoom
});
this._createObservables();
}

private _createObservables() {
this._centerChangeObservable = this.createEventObservable<google.maps.LatLngOptions>('center_changed', (observer: Rx.Observer<google.maps.LatLngOptions>) => {
this._centerChangeObservable = this.createEventObservable<google.maps.LatLngLiteral>('center_changed', (observer: Rx.Observer<google.maps.LatLngLiteral>) => {
const center = this._map.getCenter();
observer.onNext({
lat: center.lat(),
Expand All @@ -59,19 +52,34 @@ export class GoogleMapsAPIWrapper {
return this._zoomChangeObservable;
}

getCenterChangeObservable(): Observable<google.maps.LatLngOptions> {
getCenterChangeObservable(): Observable<google.maps.LatLngLiteral> {
return this._centerChangeObservable;
}

panTo(latLng: google.maps.LatLngOptions) {
this._map.panTo(latLng);
setCenter(latLng: google.maps.LatLngLiteral) {
this._map.setCenter(latLng);
}

setZoom(zoom: number) {
this._map.setZoom(zoom);
}

getCenter(): google.maps.LatLng {
if (!this._isInitialized) {
return;
}
return this._map.getCenter();
}

isInitialized(): boolean {
return this._isInitialized;
}
}

@Injectable()
export class GoogleMapsAPIWrapperFactory {
constructor(private _zone: NgZone) {}
create(el: HTMLElement, latitude: number, longitude: number): GoogleMapsAPIWrapper {
return new GoogleMapsAPIWrapper(el, latitude, latitude, this._zone);
}
}
3 changes: 2 additions & 1 deletion typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/// <reference path="node_modules/angular2/bundles/typings/angular2/angular2.d.ts" />
/// <reference path="dist/typings/angular2_google_maps.d.ts" />
/// <reference path="dist/typings/angular2_google_maps.d.ts" />
/// <reference path="node_modules/rx/ts/rx.all.d.ts" />

0 comments on commit 4b90bfb

Please sign in to comment.