Skip to content

Commit

Permalink
Add onLocationChange action hook to g-map-address-marker
Browse files Browse the repository at this point in the history
  • Loading branch information
asennikov committed Jan 23, 2016
1 parent 2555dff commit 12db920
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 24 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ Google Map object on creation and updated on change.

Mandatory `context` attribute ties child-elements
with the main `g-map` component. You can set simple title appearing on click
using `title` attribute.
using `title` attribute and marker label using `label`.

```handlebars
{{#g-map lat=37.7833 lng=-122.4167 zoom=12 as |context|}}
{{g-map-marker context lat=37.7933 lng=-122.4167}}
{{g-map-marker context lat=37.7833 lng=-122.4267 title=titleForSecondMarker}}
{{g-map-marker context lat=37.7733 lng=-122.4067 title="Marker #3"}}
{{g-map-marker context lat=37.7733 lng=-122.4067 label="3" title="Marker #3"}}
{{/g-map}}
```

Expand Down Expand Up @@ -158,6 +158,11 @@ one Info Window is open at each moment for Markers of each group.

Proxy `g-map-address-marker` component takes address string as parameter
and translates it to lat/lng under the hood.

Optional `onLocationChange` action hook will send you back coordinates
of the latest address search result and the raw array of
[google.maps.places.PlaceResult](https://developers.google.com/maps/documentation/javascript/reference#PlaceResult) objects provided by `places` library.

Other optional parameters are the same as for `g-map-marker`.
Requires `places` library to be specified in `environment.js`.

Expand All @@ -167,6 +172,15 @@ ENV['g-map'] = {
}
```

```javascript
actions: {
onLocationChangeHandler(lat, lng, results) {
Ember.Logger.log(`lat: ${lat}, lng: ${lng}`);
Ember.Logger.debug(results);
}
}
```

```handlebars
{{#g-map lat=37.7833 lng=-122.4167 zoom=12 as |context|}}
{{g-map-address-marker context address="San Francisco, Russian Hill"}}
Expand All @@ -175,6 +189,11 @@ ENV['g-map'] = {
Works in block form too.
{{/g-map-infowindow}}
{{/g-map-address-marker}}
{{g-map-address-marker context address=searchedAddress
onLocationChange=(action "onLocationChangeHandler")}}
{{g-map-address-marker context address=anotherSearchedAddress
onLocationChange="onLocationChangeHandler"}}
{{/g-map}}
```

Expand Down
30 changes: 24 additions & 6 deletions addon/components/g-map-address-marker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Ember from 'ember';
import layout from '../templates/components/g-map-address-marker';
/* global google */

const { computed, observer, run, isPresent, isEmpty } = Ember;
const { computed, observer, run, isPresent, isEmpty, typeOf } = Ember;

const GMapAddressMarkerComponent = Ember.Component.extend({
layout: layout,
Expand All @@ -26,15 +26,15 @@ const GMapAddressMarkerComponent = Ember.Component.extend({
if (isPresent(map) && isEmpty(service)) {
service = new google.maps.places.PlacesService(map);
this.set('placesService', service);
this.updateLocation();
this.searchLocation();
}
},

onAddressChanged: observer('address', function() {
run.once(this, 'updateLocation');
run.once(this, 'searchLocation');
}),

updateLocation() {
searchLocation() {
const service = this.get('placesService');
const address = this.get('address');

Expand All @@ -43,11 +43,29 @@ const GMapAddressMarkerComponent = Ember.Component.extend({

service.textSearch(request, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
this.set('lat', results[0].geometry.location.lat());
this.set('lng', results[0].geometry.location.lng());
this.updateLocation(results);
}
});
}
},

updateLocation(results) {
const lat = results[0].geometry.location.lat();
const lng = results[0].geometry.location.lng();

this.set('lat', lat);
this.set('lng', lng);
this.sendOnLocationChange(lat, lng, results);
},

sendOnLocationChange() {
const { onLocationChange } = this.attrs;

if (typeOf(onLocationChange) === 'function') {
onLocationChange(...arguments);
} else {
this.sendAction('onLocationChange', ...arguments);
}
}
});

Expand Down
86 changes: 70 additions & 16 deletions tests/unit/components/g-map-address-marker-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,35 +60,50 @@ test('it triggers `initPlacesService` on `mapContext.map` change', function() {
sinon.assert.calledOnce(component.initPlacesService);
});

test('it triggers `updateLocation` on `initPlacesService` call', function() {
component.updateLocation = sinon.spy();
test('it triggers `searchLocation` on `initPlacesService` call', function() {
component.searchLocation = sinon.stub();

run(() => component.set('map', {}));
run(() => component.initPlacesService());

sinon.assert.calledOnce(component.updateLocation);
sinon.assert.calledOnce(component.searchLocation);
});

test('it triggers `updateLocation` on `address` change', function() {
component.updateLocation = sinon.spy();
test('it triggers `searchLocation` on `address` change', function() {
component.searchLocation = sinon.stub();
run(() => component.set('address', 'query string'));
sinon.assert.calledOnce(component.updateLocation);
sinon.assert.calledOnce(component.searchLocation);
});

test('it calls `textSearch` of placesService on `updateLocation`', function() {
test('it calls `textSearch` of placesService on `searchLocation`', function() {
run(() => component.set('address', 'query string'));
run(() => component.set('placesService', fakePlacesService));

fakePlacesService.textSearch = sinon.stub();
run(() => component.updateLocation());
run(() => component.searchLocation());

const correctRequest = { query: 'query string' };

sinon.assert.calledOnce(fakePlacesService.textSearch);
sinon.assert.calledWith(fakePlacesService.textSearch, correctRequest);
});

test('it sets `lat` & `lng` of the first textSearch result on `updateLocation`', function(assert) {
test('it calls `updateLocation` after successful textSearch on `searchLocation`', function() {
const results = [{ a: 1 }, { b: 2 }];
const status = google.maps.places.PlacesServiceStatus.OK;

run(() => component.set('address', 'query string'));
run(() => component.set('placesService', fakePlacesService));

fakePlacesService.textSearch.callsArgWith(1, results, status);
component.updateLocation = sinon.stub();
run(() => component.searchLocation());

sinon.assert.calledOnce(component.updateLocation);
sinon.assert.calledWith(component.updateLocation, results);
});

test('it sets `lat` & `lng` of the first provided result on `updateLocation`', function(assert) {
const results = [{
geometry: {
location: {
Expand All @@ -104,16 +119,55 @@ test('it sets `lat` & `lng` of the first textSearch result on `updateLocation`',
}
}
}];
const status = google.maps.places.PlacesServiceStatus.OK;
fakePlacesService.textSearch.callsArgWith(1, results, status);

run(() => component.set('address', 'query string'));
run(() => component.set('placesService', fakePlacesService));

run(() => component.updateLocation());
run(() => component.set('attrs', {}));
run(() => component.updateLocation(results));

assert.equal(component.get('lat'), 12);
assert.equal(component.get('lng'), -20);
});

fakePlacesService.textSearch = sinon.stub();
test('it calls `sendOnLocationChange` on `updateLocation`', function() {
const results = [{
geometry: {
location: {
lat: () => 12,
lng: () => -20
}
}
}, {
geometry: {
location: {
lat: () => 24,
lng: () => 100
}
}
}];

component.sendOnLocationChange = sinon.stub();
run(() => component.set('attrs', {}));
run(() => component.updateLocation(results));

sinon.assert.calledOnce(component.sendOnLocationChange);
sinon.assert.calledWith(component.sendOnLocationChange, 12, -20, results);
});

test('it sends action `onLocationChange` on `sendOnLocationChange`', function() {
const results = [{ a: 1 }, { b: 2 }];
component.sendAction = sinon.stub();

run(() => component.set('attrs', { onLocationChange: 'action' }));
run(() => component.sendOnLocationChange(12, 30, results));

sinon.assert.calledOnce(component.sendAction);
sinon.assert.calledWith(component.sendAction, 'onLocationChange', 12, 30, results);
});

test('it runs closure action `attrs.onLocationChange` directly on `updateLocation`', function() {
const results = [{ a: 1 }, { b: 2 }];
run(() => component.set('attrs', { onLocationChange: sinon.stub() }));
run(() => component.sendOnLocationChange(12, 30, results));

sinon.assert.calledOnce(component.attrs.onLocationChange);
sinon.assert.calledWith(component.attrs.onLocationChange, 12, 30, results);
});

0 comments on commit 12db920

Please sign in to comment.