I have been struggling for 4 years to figure out how I can use google maps in my Rails app.
I've tried to use gmaps4rails but given up because it's too hard. I managed to find a solution on SO, which worked except that i couldn't specify a zoom level. However, in production, all of my other javascripts didn't work. The solution for that was to move the:
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
out of the head tag and to the bottom of the body tag.
That works to let all of my other js files operate as they do in the development environment, when in production mode, however, now the map won't render at all.
Is there a solution which:
allows me to use my javascript files (other than the google maps js file)- which I think is achieved by having the javascript include tag at the end of the body instead of the head
allows me to render a google map in production? If I move the javascript include tag back to the head I can render the map, but the rest of my js files don't work (in production only).
allows me to specify a zoom level on my map?
I have tried to ask for help here, here and here (and a million other times on this board). I haven't managed to find help yet.
In my address.js, I have:
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 5
});
var bounds = new google.maps.LatLngBounds();
// var opts = {
// zoom: 10,
// max_zoom: 16
// }
var n = addresses.length;
for (var i = 0; i < n; i++) {
var lat = addresses[i].latitude;
var lng = addresses[i].longitude;
if (lat == null || lng ==null){
console.log(addresses[i].name + " doesn't have coordinates");
}else {
var address = new google.maps.Marker({
position: {lat: parseFloat(lat), lng: parseFloat(lng)},
title: addresses[i].name,
map: map //,
// zoom: 8
});
bounds.extend(address.position);
}
}
map.fitBounds(bounds);
}
As I outlined in this post, I get an error that says:
initMap is not a function
I've tagged all the relevant posts that suggest solutions in that linked post, together with the results of my efforts to use those solutions to solve my problem.
In relation to the zoom level on the map, the answer in this post explained the problem as being attributable to the js asking for a map with each address in it. That somehow forces the zoom level to be the most zoomed in, regardless of the level I try to specify. So there are 2 issues, the first is how to remove the bit that forces the most zoomed in level so that I can set a zoom level that will be acknowledged and the second is whether there has been some change in the way to express the zoom level, because so many answers on SO (including those linked in my post) suggest using "setZoom" instead of "zoom: 5". I can't find a solution that worked.
As I outlined in this post, the js files (except for google maps) don't work in production if I have the javascript include tag in the head. I only arrived at this solution after a long, expensive session on codementor. I can't afford another session to find out how to solve the problems that this solution has created. I also didn't get an explanation as to the fundamental reason why this change is necessary. The rails guides suggest including it there so I don't understand why, for the most part, moving it to the end of the body tag is a solution that gets the js to work in the production environment. This breaks the address js though, so it isn't the right solution
In my config.rb I have
config/initializers/assets.rb
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path
# Rails.application.config.assets.paths << Emoji.images_path
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# Rails.application.config.assets.precompile += %w( search.js )
config/production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
I have tried each of the solutions posted in the linked posts (which are mostly from others who have posted on this board. I would love to learn the principle involved in figuring this out (so that I can begin to learn to think the way that rails wants me to). I'm baffled by why this is so difficult and why so little is written about configuring apps to work in production in any of the texts. There is clearly a problem that needs a solution (I have combed through 100s of other posts on this topic, but cannot find a principled answer) or a solution that works.
If it makes any difference, my production environment is on heroku. Any advice, for principles that are relevant would be very much appreciated - if a direct answer is not immediately apparent.
TAKING R_G'S SUGGESTION
I changed my address.js file to include a new first line and a new last line. That file now has:
;(function ready() {
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 5
});
var bounds = new google.maps.LatLngBounds();
// var opts = {
// zoom: 10,
// max_zoom: 16
// }
var n = addresses.length;
for (var i = 0; i < n; i++) {
var lat = addresses[i].latitude;
var lng = addresses[i].longitude;
if (lat == null || lng ==null){
console.log(addresses[i].name + " doesn't have coordinates");
}else {
var address = new google.maps.Marker({
position: {lat: parseFloat(lat), lng: parseFloat(lng)},
title: addresses[i].name,
map: map //,
// zoom: 8
});
bounds.extend(address.position);
}
}
map.fitBounds(bounds);
}
})();
When I start the server and try to render the page, the map still does not render (just an empty white space where it should be). There are no console errors showing in the chrome inspector (just as there are not when I do not use the first and last lines suggested by R_G).
RESPONSE TO ENGINEER DAVE In response to your questions:
Q1: Are your production assets being precompiled?
No - the reason for that is that my production.rb notes tell me not to. See here:
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
I understand these notes to mean that assets.rb handles the precompilation, so there is no longer a need to precompile production assets. The first line of the instructions that I copied above tells me not to fallback to the assets pipeline so I'm not sure what you're expecting to see differently.
In asset.rb, the notes say:
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.# Do not fallback to assets pipeline if a precompiled asset is missed.
I understand this to mean everything listed in application.js is already handled so there is no need for further recompilation steps. I understand (from previous attempts over the years to solve this problem that doubling up on the compilation is a cause of a separate set of problems).
My address.js file is listed in my application.js file (via the require tree), so it should be dealt with by whatever the process that assets.rb uses.
Q2. Are you requiring the tree in application.js
Yes. I am.
To your comment: "Also if you're not requiring the whole JS tree of files in application.js then the files you have flagged for pre-compilation in assets.rb, e.g. address.js, will get precompiled but will not be loaded."
My application.js has:
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require jquery-fileupload/vendor/jquery.ui.widget
//= require jquery-fileupload/jquery.iframe-transport
//= require jquery-fileupload/jquery.fileupload
//= require bootstrap-sprockets
//= require moment
//= require bootstrap-datetimepicker
//= require pickers
// require underscore
// require gmaps/google
//= require markerclusterer
//= require cocoon
//= require cable
//= require_tree .
I am requiring the tree. Address.js is in my app/assets/javascripts folder so it should be picked up by the require_tree line at the end of the list in application.js
I'm not sure I've understood any of your comment. Is there a suggestion that I could try to get this working? Is your suggestion to ignore the instructions in the assets.rb and production.rb setup?
What is the difference between 'precompiled' and 'loaded'? I'll try to do some research to understand what that means now.
ENGINEERDAVE'S COMMENT ON R_G'S SUGGESTION
EngineerDave suggests referring to this post as a potential solution. This post suggests:
moving the javascript include tag to the end of my body tag (which I have done, with the effect that the google map no longer works but the hide /show js in the other file that wasn't working now does work).
using this line:
document.addEventListener("DOMContentLoaded", function(event) { //do work });
That post goes on to suggest a simpler implementation of that line, as:
An actual working plain javascript implementation here if someone wants code they can just drop in: stackoverflow.com/questions/9899372/… – jfriend00 Dec 13 '14 at 7:58
That link, suggests:
<script>
// self executing function here
(function() {
// your page initialization code here
// the DOM will be available here
})();
</script>
My current code in address.js begins with this line:
function initMap() {
Taking the suggestion in this post, I try replacing that line with:
(function() {
function initMap() {
//then continue with the rest of my address.js file
})();
When I save this and try it, I get the same outcome as when I tried R_G's first suggestion. No map. No console errors showing in the chrome inspector.
THE DOWNVOTE
I don't understand the reason for the down vote on this question. I have been struggling for years to try to learn how to work with rails with javascript. I have spent thousands of dollars on codementor.io/ upwork and a bunch of the other sites trying to pay for professional help to learn this. I've been to every meetup in my city and make a point to try them in different cities when I have the opportunity to do so. I haven't found a person who has been able to figure this out. 4+ years and I'm still trying to solve this. What's the reason to down vote this. It doesn't give me any insight as to why this forum is not the right place to seek help. I'm out of options for alternatives. I just don't have any more budget to waste on codementor.io. The down vote only costs me points which slows me down from being able to start another bounty on this forum to try and find some information that will help to solve this problem. If you have constructive feedback on how better to ask this question or do the research necessary to find the answer - either of those things would be gratefully appreciated. Have a nice day.
To everyone else, thank you for trying to help me.
BRENZY'S SUGGESTION
Thank you so much for taking the time to make the repo. It's so helpful to see someone else lay this out. That said, this doesnt work for me. I had to adapt some of your approach to get rid of some of the errors this made for me. I've set out how I incorporated your suggestion and the error it gives below:
Application.html.erb
Changes:
I moved the javascript include tag back to the head tag
Above the js include tag, I put the google source link. I've shown the link you suggested in comment below, and the link I used beneath it. I had to change the link because I got an error saying no API keys using your link.
<%= csrf_meta_tags %> <%= favicon_link_tag %> <!-- <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script> --> <script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV["GOOGLE_MAPS_API_KEY"] %>"async defer></script> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <!-- Latest compiled and minified CSS --> <%= stylesheet_link_tag href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css" %> <!-- Optional theme --> <%= stylesheet_link_tag href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap-theme.min.css" %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
app/assets/javascripts/initialise.js
I made a new file - like yours.
function initialize() {
var losAngeles = new google.maps.LatLng(34.0500, -118.2500);
var pasadena = new google.maps.LatLng(34.14778, -118.14452);
var mapOptions = {
zoom: 10,
center: losAngeles,
disableDefaultUI: true,
mapTypeControlOptions: google.maps.MapTypeId.SATELLITE
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
The current error is pointing at the last line of this file.
One thing I don't understand from your commentary is that you think this file is outside of the asset pipeline. I don't understand that because your application.js has require_tree, which I understand to mean that everything in app/assets/javascripts gets included in the pipeline.
My view is now the same as yours:
<div id="map-canvas"></div>
When I save all of this and try it, I get an error that says:
Uncaught ReferenceError: google is not defined
at initialize.self-f919dc6….js?body=1:13
(anonymous) @ initialize.self-f919dc6….js?body=1:13
There is just a blank white space instead of a map.
THE BIGGER PROBLEM
I pushed this to heroku and tried the production version. Now, the js that hides & shows form fields based on other form selections doesnt work. This was the original problem that led to putting the javascript include tag at the end of the body instead of the head tag.
That js is in a file called app/assets/javascripts/stance/commercial.js
jQuery(document).ready(function() {
jQuery('[name="organisation[commercial_attributes][standard_financial_split]"]').on('click', function() {
if (jQuery(this).val() == 'true' ) {
jQuery('#commercial_standard_financial_split_content').removeClass('hidden');
} else {
jQuery('#commercial_standard_financial_split_content').removeClass('hidden').addClass('hidden');
}
});
});
This did work when the javascript include tag was moved to the end of the body tag. Now that it's back in the head tag - it doesnt work.