Populating Google Maps for Rails markers based on geolocation - ruby-on-rails

I'm trying to use the Google Maps for Rails gem to populate a map with markers based on a custom gmaps4rails_callback that uses the current location, but I can't seem to get the code to replace the markers to trigger after the user's geolocation has been determined.
My application.js defines a custom maps callback function, along with a callback for getCurrentPosition:
function nearby_locations(position) {
$.getJSON('locations/nearby.json',
{ lat: "33.7012", long: "-117.8683" },
function(data) {
Gmaps4Rails.replace_markers(data);
});
}
function gmaps4rails_callback() {
navigator.geolocation.getCurrentPosition(nearby_locations);
}
For now, I'm using hard coded lat/long data, but eventually I'm going to key off of the real data in the position object passed to me. Running the getJSON call manually works fine, and my locations/nearby.json returns location data in the proper format that updates the markers as expected.
My view template just uses
<%= gmaps("map_options" => {"auto_adjust" => true, "detect_location" => true}) %>
Now I realize that I'm probably doing a geo lookup twice, by using both detect_location and then calling getCurrentPosition, but in a earlier attempt, code in gmaps4rails_callback was being executed before the geolocation permission from the browser (Safari) had even completed, so I thought this would delay execution until the location had been determined.
I've previously displayed a map without a custom gmaps4rails_callback and it worked fine, so I know my jQuery and JavaScript files are being included just fine.
Is it possible that Google Maps for Rails somehow already sets up its own getCurrentPosition callback and mine is being ignored? After adding some debug logging, it doesn't look like nearby_locations is called at all.

Basically, when you set "detect_location" => true, this triggers the following function:
findUserLocation: function() {
if(navigator.geolocation) {
//try to retrieve user's position
navigator.geolocation.getCurrentPosition(function(position) {
//saves the position in the userLocation variable
Gmaps4Rails.userLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
//change map's center to focus on user's geoloc if asked
if(Gmaps4Rails.map_options.center_on_user === true) {
Gmaps4Rails.map.setCenter(Gmaps4Rails.userLocation);
}
},
function() {
//if failure, triggers the function if defined
if(this.fnSet("gmaps4rails_geolocation_failure")) { gmaps4rails_geolocation_failure(true); }
});
}
else {
//if failure, triggers the function if defined
if(this.fnSet("gmaps4rails_geolocation_failure")) { gmaps4rails_geolocation_failure(false); }
}
}
So yes the geolocation is already made... or in progress. This function is triggered before the gmaps4rails_callback but it's the tough part with browser geolocation: it takes an undefined time.
I thought about proposing a callback on failure but didn't think it was necessary to include a callback on success (I'm still not convinced).
Unfortunately, I don't understand the problem with your gmpas4rails_callback: I've both detection_locationand callback and it works as expected.

Related

HTMLIFrameElement.contentWindow.print() from GoogleSheets is not opening print preview

I am using GoogleSheets to print a png/image file using HTMLService. I created a temporary Iframe element with an img tag in the modalDialog and call IFrame element's contentWindow.print() function after IFrame element and its image are loaded. (I have not set visibility:hidden attribute of IFrame element to check if image is getting loaded.)
However, I only see the printer dialog without any print preview. I am testing on Firefox. Am I missing anything?
[Updated] - I am using Googles Apps script. performPrint() is in printJsSource.html and openUrl() is in Code.gs.
Inside printJsSource.html
function performPrint(iframeElement, params) {
try {
iframeElement.focus()
// If Edge or IE, try catch with execCommand
if (Browser.isEdge() || Browser.isIE()) {
try {
iframeElement.contentWindow.document.execCommand('print', false, null)
} catch (e) {
iframeElement.contentWindow.print()
}
} else {
// Other browsers
iframeElement.contentWindow.print() // as I am using Firefox, it is coming here
}
} catch (error) {
params.onError(error)
} finally {
//cleanUp(params)
}
}
Inside Code.gs
function openUrl() {
var html = HtmlService.createHtmlOutputFromFile("printJsSource");
html.setWidth(500).setHeight(500);
var ui = SpreadsheetApp.getUi().showModalDialog(html, "Opening ..." );
}
I think there is some general confusion about the concept
First of all, function performPrint() seems to be a client-side Javascript funciton, while function openUrl() is a server-side Apps Script function.
While you did not specify either you use Google Apps Script - if you do so, function openUrl()belongs into the code.gs file and function performPrint() into printJsSource.html file
function openUrl() allows you to open a modal dialog which can show some data on the UI, e.g. your image
Do not confuse this behavior with actual printing (preview)!
It is NOT possible to trigger the opening of a Google Sheets printing preview programamticaly!
The Javascript method you are using iframeElement.contentWindow.print() might trigger the printing of the whole content of a browser window (different from the Google Sheets printing dialog, also depends on the browser), but if you try to incorporate it into the client-side coe of an Apps Script project, you will most likely run into restrictions due to the scopes of modal diloags and usage of iframes.
While from your code it is hard to say either you implemented the funcitons in the correct files of the Apps Script project, keep in mind that to work with iframes you need to specify in function openUrl()
html.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);

iOS share sheet fires twice [Cordova/Ionic/Angular]

For some reason, the share sheet in iOS is firing twice when I press the n-click share button in my app menu (i.e. first one sheet comes into view, followed by another on top of it).
menu.html...
<ion-nav-buttons side="right">
<button ng-cloak ng-if="sharable" ng-click="systemShare()" class="button button-icon button-clear ion-share"></button>
</ion-nav-buttons>
controller.js...
.controller('dataCtrl', function($scope, $rootScope, $stateParams, dataService, $cordovaSocialSharing) {
dataService.getData($stateParams.itemId).then(function(data){
$scope.data = data;
// set sharable
$rootScope.sharable=true;
document.addEventListener("deviceready", function () {
$rootScope.systemShare = function(){
$cordovaSocialSharing
.share($scope.data.title, $scope.data.title, null, $scope.data.url)
.then(function(result) {
// Success!
}, function(err) {
// An error occured. Show a message to the user
},false);
}
});
});
})
I have a couple controllers that follow this same pattern (and the code for each is basically identical in terms of what is shown above). They fetch the data, populate the view, then define the share button functionality and parameters using $rootScope.
The first time the button is presses it fires twice. Sometimes it happens a second time, but not always. The double-firing happens regardless of which version of the view/controller above is used first (that is, I have a controller for tours and a controller for stops: both are pretty much the same and whichever one I go to first will exhibit this behavior).
I'm guessing I need to change the way I define the systemShare() function, but I'm not sure how to do it other than via $rootScope since it needs to be accessible via the global menu.
Looks like this is an Ionic bug. See:
https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/issues/465
The suggested fix (which worked for me) is to wrap the call in a timeout.
setTimeout(function() {
$cordovaSocialSharing
.share('Subject', 'Message', null, 'http://www.google.com') // Share via native share sheet
.then(function(result) {
console.log(result);
}, function(err) {
alert('Sharing failed. Please try again.');
});
}, 500);

Joining Firebase tables in React

I am hoping to display a list of user's notes from a Firebase DB inside of a React app.
After reading through the Firebase recommended approach on structuring data, I've created my database in the flattened format they recommend. The data structure looks something like this:
notes
- [noteKey]
- note: [noteData]
- created_at: [date]
- updated_at: [date]
...
users
- [userKey]
- name: [userName]
- notes
- [noteKey]: true
...
...
Each user has an array called notes, which lists the noteKeys of the notes that they own.
So far I've been able to get the full list of notes (from all users, not what I want), and the user's list of noteKeys. The issue that I'm having is combining those two. I have seen the question about joining tables, but I have more of a React focused question:
In which React function does the join happen?
Right now my code looks like this:
getInitialState: function(){
return {
notesList: []
};
},
componentWillMount: function() {
base = Rebase.createClass('https://appName.firebaseio.com');
base.syncState('notes', {
context: this,
state: 'notesList',
asArray: true,
queries: {
limitToLast: 20
}
});
this.state.notesList.map(function(item, i) {
base.child("notes/" + item['.key'] + "/note").on('child_added', function(snapshot) {
console.log(item['.key'])
});
});
},
I see two issues with this.
When the this.state.notesList.map function is called in componentWillMount, the array hasn't been populated with the Firebase data yet, so it looks like an empty array and returns an error.
Once I solve #1, I'm not sure how to get the user specific notes into it's own array that's accessible by the rest of the component.
--
In which React timeline function should the join be happening?
How do the second table items (the user's notes) get added to an array that is accessible by the rest of the component?
You're working with an async library (re-base) here but you've written synchronous code.
What this means is base.syncState is going to fire off a request to your Firebase instance and in the meantime, your JavaScript is going to just keep happily executing down the line with or without results. It follows that this.state.notesList.map is going to map over an empty array since JS is going to execute faster than a round trip to the server.
Looking at the options available for the syncState method, there's one called then that executes a callback.
then: (function - optional) The callback function that will be invoked when the initial listener is established with Firbase. Typically used (with syncState) to change this.state.loading to false.
This makes me think that it fires after you get your data from Firebase.
Try running your .map in there since you'll actually have the data you want.
componentWillMount: function() {
base = Rebase.createClass('https://appName.firebaseio.com');
base.syncState('notes', {
context: this,
state: 'notesList',
asArray: true,
queries: {
limitToLast: 20
},
then: function() {
this.state.notesList.map(function(item, i) {
base.child("notes/" + item['.key'] + "/note").on('child_added', function(snapshot) {
console.log(item['.key'])
});
});
}
});
}

Knowing when KML file has loaded and features added (OpenLayers3)?

I see that ol.source.KML (untick "Stable only") fires the events addfeature,
change and removefeature. However, I just need to know when the KML was retrieved over the network and all its features added. Is there an event like "loaded" or similar in OpenLayers 3?
I need to execute some code when the KML has been added. Waiting for document.ready is not enough as the KML file is loaded (over network) afterwards.
Listen to change event, check if the source state is ready, then do what you want, not forgetting to deregister your listener.
var key = source.on('change', function() {
if (source.getState() == 'ready') {
source.unByKey(key);
// do something with the source
}
});

jQuery Gmap3: where is the success callback?

I would like to know when my gmap is loaded. I didn't see any callback method in the documentation and around the web.
I'm using this plugin: http://gmap3.net/en/catalog/
Any tip? Thx
Regards
I think you will have to revert and use the google maps native function. The answer toyour question is outlined in this question: How can I check whether Google Maps is fully loaded?
and it looks like the best answer is given with this code snippet:
google.maps.event.addListenerOnce(map, 'idle', function(){
// do something only the first time the map is loaded
});
it doesn't seem to be documented but there is a callback property on the map object
$('#map_canvas').gmap3(
{
map: {
options: {
zoom: 9,
center: [view.lat, view.lng]
},
callback: function (e) {
alert("loaded");
}
}
});

Resources