Gmaps4rails - Map throwing JS error on first load when JSON == "[]" - ruby-on-rails

I am using the geocoder gem's .nearby method to query my DB and return nearby location objects.
Question 1: When loading the map for the first time, no markers are passed to the JS, and it does not load the map. How can I fix this issue without loading a marker?
<script type="text/javascript">
Gmaps.map = new Gmaps4RailsGoogle();
Gmaps.load_map = function() {
Gmaps.map.map_options.detect_location = true;
Gmaps.map.map_options.center_on_user = true;
Gmaps.map.initialize();
Gmaps.map.markers = ; // BLOWS UP HERE
Gmaps.map.markers_conf.list_container = "markers_list";
Gmaps.map.create_markers();
Gmaps.map.adjustMapToBounds();
Gmaps.map.callback();
};
Gmaps.oldOnload = window.onload;
window.onload = function() { Gmaps.triggerOldOnload(); Gmaps.loadMaps(); };
</script>`
Question 2: When user searches and no nearby results are available to their search, how to recover from this just showing a random location (right now it loads the middle of the ocean).
Question 3: When user searches and no nearby results are available, can I show nearby locations to their current location using browser geolocation (I know it's not supported in all browsers).

Answer 1:
There is no bug in the gem.
This line of yours makes javascript cry:
#json = nil
Indeed nil isn't valid json.
You should do:
#json = [].to_json
Answer 2
If your query doesn't return anything, Make another one to get a random object. Can't see any problem there.
Answer 3
Geolocation from browser is included in the gem, you even have a callback on success. See code. But you'll have to write your own javascript fitting your own logic.

Related

How to load initial data from rails to redux

I tried to load json from rails, and pass it to redux createStore.
let initialStore = window.INITIAL;
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const store = createStoreWithMiddleware(rootReducer, initialStore);
But i always get undefined, or just window.INITIAL return value after store.
At first, store loads with empty object, then fetch action is dispatched, and i get updated store with json, but i already got error, when i'm trying to call something like { product.title } on empty object. No matter what I do, i can't load json before redux stuff begins, even with global data, like this.
(function() {
$.getJSON('http://localhost:3000/products.json', function(data) {
return window.INITIAL = data;
});
});
Api and controller is simple.
def index
products = Product.all
render json: products
end
How do you handle this? I want to do it without any gems like react-rails etc, i can't pass all initial state to Root component in one place.
In the question above you have the following:
let initialStore = window.INITIAL;
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const store = createStoreWithMiddleware(rootReducer, initialStore);
The problem with the above code is that it will only populate your store with window.INITIAL as it exists when the page first loads. Any further changes to window.INITIAL will be ignored.
What you need to do is populate window.INITIAL with your initial data in your server rendered code. That is, simply place the script block before your redux code:
<script>
var INITIAL = { /* this object should match the shape of your reducer state */ };
</script>
That's it. Now get rid of the AJAX call. You don't need it. This will be a lot more performant too. Instead of forcing the user to wait on an additional request after the page has already rendered, the user instead gets all the data at the same time as the rest of the page.

Caching visited page in Jquery Mobile

I work on Jquery Mobile/Phonegap app for android.
I´d like my app to "remember" that(if) the user has visited one of my pages. For example if he once visits "page1.html", this action should be cached in the phone memory, so that when the user opens the app again there should be possibility to navigate to this "page2.html" directly from "index.thml".
Please, if you have a code suggestion, tell me also how/where do I use it, because sometimes for starters like me it is realy hard to understand what to do with a little piece code.
Thank you very much!
You can use HTML5 local storage for this purpose.
Each time when a page is shown you can save/update the current page URL to a local storage variable, say 'lastVisit', as below:
$(document).on('pageshow', function (event, data) {
var currentPage = $('.ui-page-active').data('url');
localStorage.setItem("lastVisit", currentPage);
});
If you are not getting $('.ui-page-active').data('url'), then you can use $.mobile.activePage.attr('id') which will give you the current page id.
So next time, when the user opens the app again, you can check whether this local storage variable is set and can action accordingly.
The code will look like as below:
$(document).on('mobileinit', function(){
var lastVisit = '';
if(localStorage.getItem("lastVisit") != null){
lastVisit = localStorage.getItem("lastVisit");
}
if(lastVisit){
document.location.href = lastVisit;
}
});
You can use these codes in the header section scripts.

Dynamic source for JQuery UI Autocomplete

I am currently working on a interface which requires an autocomplete of locations.
The list of locations is currently obtained via an AJAX call to a JSON object which is generated dymanically from a URL which has a search parameter of at least three characters.
Is there a way (im guessing most likely using an on key up), to populate the autocomplete by making an ajax call using the input current value and then returning these values in the autocomplete selection, rather than relying on jquery to create the autocomplete list from its source.
What I have currently appears to be inefficient and doesn't work as you might expect (for some reason the autocomplete only appears after four characters).
function buildAutoComplete(fieldId) {
var inputValue = $("#" + fieldId).val()
var resultsList = []
if(inputValue.length > 2) {
get("/location?prefix=" + inputValue,inputHit,inputMiss);
}
function inputHit(result) {
for(var i=0; i<result.length; i++) {
resultsList[i] = result[i].display_text
}
$("#" + fieldId).autocomplete({
source: resultsList
});
}
function inputMiss() {
}
}
$("#originField").keyup(function() {
buildAutoComplete("originField");
});
It turns out I was going about this completely the wrong way.
The autocomplete API which I knew allowed a remote source automatically includes a URL parameter of term, of the input value.
So the resulting code looks like
$("#originField").autocomplete({
minLength: 3,
source: "/location"
});
All it needed was the code at the back of the source to look for the parameter 'term' rather than 'prefix'.
Considerably more stable with a less complex and smaller code base.

Bookmarklet to save URL in Google Spreadsheet

I want to create a simple bookmarklet, that grabs the URL of the current webpage "location.ref" and saves it in a Google Spreadsheet. After it saves it, I want to stay on the current webpage.
The only way I know of writing to Google Spreadsheet is using Google App Script. So I wrote a simple script that does just that:
function doGet(request) {
var ss = SpreadsheetApp.openByUrl( "https://docs.google.com/spreadsheet/ccc?key=<MY-SPREADSHEET-ID>");
var sheet = ss.getSheets()[0];
var headers = ["Timestamp", "url"];
var nextRow = sheet.getLastRow();
var cell = sheet.getRange('a1');
var col = 0;
for (i in headers){
if (headers[i] == "Timestamp"){
val = new Date();
} else {
val = request.parameter[headers[i]];
}
cell.offset(nextRow, col).setValue(val);
col++;
}
return ContentService.createTextOutput(request.parameter.url)
.setMimeType(ContentService.MimeType.TEXT);
}
I published this as a webapp. I wrote the bookmarklet:
<a href="javascript:(
function(){
alert(window.open('https://script.google.com/macros/s/<MYWEBAPP>/exec?url='+encodeURIComponent(location.href), '_self'));
}
)();">
BOOKMARK
</a>
So far so good. It actually works when I click on the bookmarklet, it does grab the URL of the current webpage and save it in my spreadsheet. But then, the webapp returns a text response and the bookmarklet displays the text causing me to move away from my current website.
Is there a way to ignore the response? GAS webapp script requires me to use doGet() that has to return something. Is there a way to not return anything from GAS script? Alternatively, is there a way i could use some other call to replace window.open to invoke the webapp that would allow me to store the response in a variable and ignore it?
I know it's been over a year but I was trying to do exactly this. It took me a while to figure out, but this works. The 1 second delay was necessary to let the script finish loading.
javascript:(function(){
my_window=window.open('https://script.google.com/macros/s/<MYWEBAPP>/exec?url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title));
(window.setTimeout(function(){my_window.close();},1000));
void(0);
})();
Instead of using window.open you may consider sending a HTTP GET request using XMLHttpRequest.
Refer here on its usage.
Change _self to something else, e.g. bookmarker and it will open in a new window or tab. If you use it on many pages, they will all reuse the same tab if it keeps the same name.

Populating Google Maps for Rails markers based on geolocation

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.

Resources