I have been trying for years to figure out how to incorporate gmaps4rails in a rails app.
I have made an entirely new app and tried fresh.
I can't figure out what's going wrong. I'm looking for setup instructions that are complete and up to date. Many SO posts refer to old versions of dependencies that end up saying that the issue is solved in a later version.
Currently, I have this in my view:
<script src="//maps.google.com/maps/api/js?v=3.23&key=<%= ENV['GOOGLE_MAPS_API_KEY'] %>"></script>
<script src="//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js" type="text/javascript"></script>
<div style='width: 800px;'>
<div id="map" style='width: 800px; height: 400px;'></div>
</div>
<script type="text/javascript">
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(<%=raw @hash.to_json %>);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
});
</script>
In my application.js, I have:
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap-sprockets
//= require moment
//= require bootstrap-datetimepicker
//= require pickers
//= require underscore
//= require gmaps/google
//= require markerclusterer
//= require_tree .
I have saved the production version of underscore.js in my vendor/assets/javascripts folder. I also saved markerclusterer.js as a file in the same folder.
In my gem file, I have:
gem 'geocoder'
gem 'gmaps4rails', '~> 2.1', '>= 2.1.2'
gem 'countries'
gem 'country_select'
In my controller, I have:
class AddressesController < ApplicationController
def index
end
def show
@hash = Gmaps4rails.build_markers(@address) do |address, marker|
marker.lat address.latitude
marker.lng address.longitude
end
end
def new
@address = Address.new
end
def create
@address = Address.new(address_params)
# authorize @address
respond_to do |format|
if @address.save
format.html { redirect_to @address, notice: 'Address was successfully created.' }
format.json { render :show, status: :created, location: @address }
else
format.html { render :new }
format.json { render json: @address.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_address
@address = Address.find(params[:id])
# authorize @address
end
# Never trust parameters from the scary internet, only allow the white list through.
def address_params
params[:address].permit(:unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude)
end
def user_time_zone(&block)
Time.use_zone(current_user.time_zone, &block)
end
end
When I try to render the view with the map, the chrome inspector shows errors as:
v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js Failed to load resource: the server responded with a status of 404 (Not Found) chrome-extension://mkjojgglmmcghgaiknnpgjgldgaocjfd/content/contentScripts/kwift.CHROME.min.js:1271 Uncaught SyntaxError: Identifier 'findGoodContent' has already been declared util.js:221 Google Maps API warning: NoApiKeys https://developers.google.com/maps/documentation/javascript/error-messages#no-api-keys util.js:221 Google Maps API warning: RetiredVersion https://developers.google.com/maps/documentation/javascript/error-messages#retired-version util.js:221 Google Maps API warning: InvalidKey https://developers.google.com/maps/documentation/javascript/error-messages#invalid-key
I don't understand any of these errors and I can't find instructions for how to setup this gem to use it in rails. I can see that it has been downloaded a lot - people must have figured out how to set it up. I am sending myself crazy trying to figure this out.
AFTER REGENERATING API KEY
I tried regenerating my browser API key on the google console.
Now when I try to render the page, I get these console errors:
primitives.self-5b8a3a6….js?body=1:5 Uncaught ReferenceError: google is not defined(…)Gmaps.Google.Primitives @ primitives.self-5b8a3a6….js?body=1:5Gmaps.Objects.Handler.Handler.setPrimitives @ handler.self-2f220ca….js?body=1:122Handler @ handler.self-2f220ca….js?body=1:8build @ base.self-8dd1d1a….js?body=1:9(anonymous function) @ VM2063:2t.SnapshotRenderer.n.assignNewBody @ turbolinks.self-c5acd7a….js?body=1:6t.SnapshotRenderer.n.replaceBody @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6t.Renderer.t.renderView @ turbolinks.self-c5acd7a….js?body=1:6t.SnapshotRenderer.n.render @ turbolinks.self-c5acd7a….js?body=1:6t.Renderer.t.render @ turbolinks.self-c5acd7a….js?body=1:6t.View.e.renderSnapshot @ turbolinks.self-c5acd7a….js?body=1:6t.View.e.render @ turbolinks.self-c5acd7a….js?body=1:6t.Controller.r.render @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6
turbolinks.self-c5acd7a….js?body=1:6 GET http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js t.SnapshotRenderer.n.assignNewBody @ turbolinks.self-c5acd7a….js?body=1:6t.SnapshotRenderer.n.replaceBody @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6t.Renderer.t.renderView @ turbolinks.self-c5acd7a….js?body=1:6t.SnapshotRenderer.n.render @ turbolinks.self-c5acd7a….js?body=1:6t.Renderer.t.render @ turbolinks.self-c5acd7a….js?body=1:6t.View.e.renderSnapshot @ turbolinks.self-c5acd7a….js?body=1:6t.View.e.render @ turbolinks.self-c5acd7a….js?body=1:6t.Controller.r.render @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6
turbolinks.self-c5acd7a….js?body=1:6 GET http://localhost:3000/users/assets/images/grayscale.svg t.SnapshotRenderer.n.assignNewBody @ turbolinks.self-c5acd7a….js?body=1:6t.SnapshotRenderer.n.replaceBody @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6t.Renderer.t.renderView @ turbolinks.self-c5acd7a….js?body=1:6t.SnapshotRenderer.n.render @ turbolinks.self-c5acd7a….js?body=1:6t.Renderer.t.render @ turbolinks.self-c5acd7a….js?body=1:6t.View.e.renderSnapshot @ turbolinks.self-c5acd7a….js?body=1:6t.View.e.render @ turbolinks.self-c5acd7a….js?body=1:6t.Controller.r.render @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6(anonymous function) @ turbolinks.self-c5acd7a….js?body=1:6
util.js:221 Google Maps API warning: NoApiKeys https://developers.google.com/maps/documentation/javascript/error-messages#no-api-keysuD.S @ util.js:221(anonymous function) @ js?v=3.23&key=:127(anonymous function) @ js?v=3.23&key=:47(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:47(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97jc @ js?v=3.23&key=:46gc.Qc @ js?v=3.23&key=:97(anonymous function) @ common.js:1
util.js:221 Google Maps API warning: RetiredVersion https://developers.google.com/maps/documentation/javascript/error-messages#retired-versionuD.S @ util.js:221(anonymous function) @ js?v=3.23&key=:127(anonymous function) @ js?v=3.23&key=:47(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:47(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97jc @ js?v=3.23&key=:46gc.Qc @ js?v=3.23&key=:97(anonymous function) @ common.js:1
util.js:221 Google Maps API warning: InvalidKey https://developers.google.com/maps/documentation/javascript/error-messages#invalid-keyuD.S @ util.js:221(anonymous function) @ js?v=3.23&key=:127(anonymous function) @ js?v=3.23&key=:47(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:47(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97(anonymous function) @ js?v=3.23&key=:44(anonymous function) @ js?v=3.23&key=:97jc @ js?v=3.23&key=:46gc.Qc @ js?v=3.23&key=:97(anonymous function) @ common.js:1
I can also see when I look in the terminal for rails s:
ActionController::RoutingError (No route matches [GET] "/assets/underscore-min.map"):
Parameters: {"id"=>"4"}
ActionController::RoutingError (No route matches [GET] "/users/assets/images/grayscale.svg"):
In relation to the underscore routing error, the line in the terminal refers to assets/underscore. My file structure is vendor/assets/javascripts and then the file is saved as underscore.js (not min.map).
ANOTHER ATTEMPT
I tried replacing the js v3.23 with v3.24 and that got rid of the above warning messages. However, I still can't get a a map to render an address. When I inspect the chrome console inspector, I can see that the address is acknowledged in the javascript, but the latitude and longitude details are not:
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers([{"lat":null,"lng":null,"infowindow":"Unit 1\u003cbr\u003e34 Darling Street\u003cbr\u003eBuilding D\u003cbr\u003eBalmain East NSW 2041\u003cbr\u003eAustralia"}]);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
});
I'm now wondering if this problem might have something to do with the model associations.
I have an address model and an organisation model. The associations are:
Address:
belongs_to :addressable, :polymorphic => true, optional: true
Organisation:
has_many :addresses, as: :addressable
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
In my address controller show action, I have:
def show
@hash = Gmaps4rails.build_markers(@address) do |address, marker|
marker.lat user.latitude
marker.lng user.longitude
# marker.title user.title
end
end
In my organisation controller show action, I have:
def show
@addresses = @organisation.addresses
@hash = Gmaps4rails.build_markers(@addresses) do |address, marker|
marker.lat address.latitude
marker.lng address.longitude
marker.infowindow address.full_address
end
end
The partial with the map in it is in views/addresses/map.html.erb
When I try to render the page again with these changes, I get a lot of errors that all say:
js?v=3.24&key=AIzaSyAleQgfNH3HRQVUCYnyAzp46xmXW7WrWrc:37 Uncaught RangeError: Maximum call stack size exceeded
I've read other SO posts where people have had this error and the notes go on to discuss 'recursive loops'. I don't seem to have any code which is similar to the code shown in those errors, so I'm not sure how to address these messages.
I'm wondering if I need to change the organisation controller action to somehow reference the organisation address instead of addresses generally (although if that's the case, then I don't understand how the console inspector shows the correct address being identified in the js from the map partial).
Can anyone help with a detailed step by step tutorial that I could use in trying to figure this out?
Others with similar problems
I can see from other SO posts that others have had similar problems. The advice seems to be this answer: Gmaps4rails Maximum call stack size exceeded?
I think I've already done this though - my show action in each of the address controller and organisation controller both refer to lat/lng rather than the full words (which are used in the db).
ERIC'S SUGGESTION
Taking Eric's suggestion, I removed gmaps4rails gem from the gem file, and the require/gmaps line from application.js
I changed the show action of my organisations controller to:
def show
@addresses = @organisation.addresses.all
# @hash = Gmaps4rails.build_markers(@addresses) do |address, marker|
# marker.lat address.latitude
# marker.lng address.longitude
# marker.infowindow address.full_address
# end
end
This step is the same as Eric's suggestion for the index action of his markers controller. I separately have an addresses controller which has an index action similar to Eric's. In my db, addresses are polymorphic and belong to organisations. I'm trying to render the map on the organisations show page, so I think this step is consistent with Eric's suggestion.
Next I updated my addresses/_map.html.erb with:
<h3>My Google Maps Demo</h3>
<div id="map"></div>
<%= javascript_tag do %>
var addresses = <%= raw @addresses.to_json %>;
<% end %>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAPS_API_KEY'] %>&callback=initMap">
</script>
I made a new file in my app/javascripts folder for addresses.js, with:
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4
});
var bounds = new google.maps.LatLngBounds();
var n = addresses.length;
for (var i = 0; i < n; i++) {
var address = new google.maps.Address({
position: {lat: parseFloat(addresses[i].latitude), lng: parseFloat(addresses[i].longitude)},
title: addresses[i].name,
map: map
});
bounds.extend(address.position);
}
map.fitBounds(bounds);
}
When I save all this and try it, an empty grey box appears on the screen.
When I use the chrome inspector I can see:
//<![CDATA[
var addresses = [{"id":5,"unit":"1","building":"d","street_number":"34","street":"darling street","city":"Balmain East","region":"NSW","zip":"2041","country":"AU","time_zone":"International Date Line West","addressable_id":1,"addressable_type":"Organisation","description":"main_address","created_at":"2016-10-27T19:17:27.919Z","updated_at":"2016-11-08T22:48:16.978Z","latitude":"-33.85751","longitude":"151.193546"}];
//]]>
The above is the address in the db. But it isn't making a map.
Can anyone see what I need to do to get the map to render with the address?
WHEN I LOOK AT THE CHROME CONSOLE INSPECTOR
I can see several new errors in the console inspector.
They are:
addresses.self-176b72f….js?body=1:9 Uncaught TypeError: google.maps.Address is not a constructor
This post has a similar problem. The accepted solution is to add a callback. I don't know how or where to do that.
Google Maps API v3 - TypeError: Result of expression 'google.maps.LatLng' [undefined] is not a constructor